Spring Cloud Gateway入坑记

前提

最近在做老系统的重构,重构完成后新系统中需要引入一个网关服务,作为新系统和老系统接口的适配和代理。之前,很多网关应用使用的是Spring-Cloud-Netfilx基于Zuul1.x版本实现的那套方案,但是鉴于Zuul1.x已经停止迭代,它使用的是比较传统的阻塞(B)IO + 多线程的实现方案,其实性能不太好。后来Spring团队干脆自己重新研发了一套网关组件,这个就是本次要调研的Spring-Cloud-Gateway

简介

Spring Cloud Gateway依赖于Spring Boot 2.0, Spring WebFlux,和Project Reactor。许多熟悉的同步类库(例如Spring-DataSpring-Security)和同步编程模式在Spring Cloud Gateway中并不适用,所以最好先阅读一下上面提到的三个框架的文档。

Spring Cloud Gateway依赖于Spring BootSpring WebFlux提供的基于Netty的运行时环境,它并非构建为一个WAR包或者运行在传统的Servlet容器中。

专有名词

  • 路由(Route):路由是网关的基本组件。它由ID,目标URI,谓词(Predicate)集合和过滤器集合定义。如果谓词聚合判断为真,则匹配路由。
  • 谓词(Predicate):使用的是Java8中基于函数式编程引入的java.util.Predicate。使用谓词(聚合)判断的时候,输入的参数是ServerWebExchange类型,它允许开发者匹配来自HTTP请求的任意参数,例如HTTP请求头、HTTP请求参数等等。
  • 过滤器(Filter):使用的是指定的GatewayFilter工厂所创建出来的GatewayFilter实例,可以在发送请求到下游之前或者之后修改请求(参数)或者响应(参数)。

其实Filter还包括了GlobalFilter,不过在官方文档中没有提到。

工作原理

客户端向Spring Cloud Gateway发出请求,如果Gateway Handler Mapping模块处理当前请求如果匹配到一个目标路由配置,该请求就会转发到Gateway Web Handler模块。Gateway Web Handler模块在发送请求的时候,会把该请求通过一个匹配于该请求的过滤器链。上图中过滤器被虚线分隔的原因是:过滤器的处理逻辑可以在代理请求发送之前或者之后执行。所有pre类型的过滤器执行之后,代理请求才会创建(和发送),当代理请求创建(和发送)完成之后,所有的post类型的过滤器才会执行。

见上图,外部请求进来后如果落入过滤器链,那么虚线左边的就是pre类型的过滤器,请求先经过pre类型的过滤器,再发送到目标被代理的服务。目标被代理的服务响应请求,响应会再次经过滤器链,也就是走虚线右侧的过滤器链,这些过滤器就是post类型的过滤器。

注意,如果在路由配置中没有明确指定对应的路由端口,那么会使用如下的默认端口:

  • HTTP协议,使用80端口。
  • HTTPS协议,使用443端口。

引入依赖

建议直接通过Train版本(其实笔者考究过,Train版本的代号其实是伦敦地铁站的命名,像当前的Spring Cloud最新版本是Greenwich.SR1Greenwich可以在伦敦地铁站的地图查到这个站点,对应的SpringBoot版本是2.1.x)引入Spring-Cloud-Gateway,因为这样可以跟上最新稳定版本的Spring-Cloud版本,另外由于Spring-Cloud-Gateway基于Netty的运行时环境启动,不需要引入带Servlet容器的spring-boot-starter-web

父POM引入下面的配置:

<dependencyManagement>
<dependencies>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-dependencies</artifactId>
<version>Greenwich.SR1</version>
<type>pom</type>
<scope>import</scope>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-dependencies</artifactId>
<version>2.1.4.RELEASE</version>
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencies>
</dependencyManagement>

子模块或者需要引入Spring-Cloud-Gateway的模块POM引入下面的配置:

    <dependencies>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-gateway</artifactId>
</dependency>
</dependencies>

创建一个启动类即可:

@SpringBootApplication
public class RouteServerApplication { public static void main(String[] args) {
SpringApplication.run(RouteServerApplication.class, args);
}
}

网关配置

网关配置最终需要转化为一个RouteDefinition的集合,配置的定义接口如下:

public interface RouteDefinitionLocator {
Flux<RouteDefinition> getRouteDefinitions();
}

通过YAML文件配置或者流式编程式配置(其实文档中还有配合Eureka的DiscoveryClient进行配置,这里暂时不研究),最终都是为了创建一个RouteDefinition的集合。

Yaml配置

配置实现是PropertiesRouteDefinitionLocator,关联着配置类GatewayProperties

spring:
cloud:
gateway:
routes:
- id: datetime_after_route # <------ 这里是路由配置的ID
uri: http://www.throwable.club # <------ 这里是路由最终目标Server的URI(Host)
predicates: # <------ 谓词集合配置,多个是用and逻辑连接
- Path=/blog # <------- Key(name)=Expression,键是谓词规则工厂的ID,值一般是匹配规则的正则表示

编程式流式配置

编程式和流式编程配置需要依赖RouteLocatorBuilder,目标是构造一个RouteLocator实例:

@Bean
public RouteLocator customRouteLocator(RouteLocatorBuilder builder) {
return builder.routes()
.route(r -> r.path("/blog")
.uri("http://www.throwable.club")
)
.build();
}

路由谓词工厂

Spring Cloud Gateway将路由(Route)作为Spring-WebFluxHandlerMapping组件基础设施的一部分,也就是HandlerMapping进行匹配的时候,会把配置好的路由规则也纳入匹配机制之中。Spring Cloud Gateway自身包含了很多内建的路由谓词工厂。这些谓词分别匹配一个HTTP请求的不同属性。多个路由谓词工厂可以用and的逻辑组合在一起。

目前Spring Cloud Gateway提供的内置的路由谓词工厂如下:

指定日期时间规则路由谓词

按照配置的日期时间指定的路由谓词有三种可选规则:

  • 匹配请求在指定日期时间之前。
  • 匹配请求在指定日期时间之后。
  • 匹配请求在指定日期时间之间。

值得注意的是,配置的日期时间必须满足ZonedDateTime的格式:

//年月日和时分秒用'T'分隔,接着-07:00是和UTC相差的时间,最后的[America/Denver]是所在的时间地区
2017-01-20T17:42:47.789-07:00[America/Denver]

例如网关的应用是2019-05-01T00:00:00+08:00[Asia/Shanghai]上线的,上线之后的请求都路由奥www.throwable.club,那么配置如下:

server
port: 9090
spring:
cloud:
gateway:
routes:
- id: datetime_after_route
uri: http://www.throwable.club
predicates:
- After=2019-05-01T00:00:00+08:00[Asia/Shanghai]

此时,只要请求网关http://localhost:9090,请求就会转发到http://www.throwable.club

如果想要只允许2019-05-01T00:00:00+08:00[Asia/Shanghai]之前的请求,那么只需要改为:

server
port: 9091
spring:
cloud:
gateway:
routes:
- id: datetime_before_route
uri: http://www.throwable.club
predicates:
- Before=2019-05-01T00:00:00+08:00[Asia/Shanghai]

如果只允许两个日期时间段之间的时间进行请求,那么只需要改为:

server
port: 9090
spring:
cloud:
gateway:
routes:
- id: datetime_between_route
uri: http://www.throwable.club
predicates:
- Between=2019-05-01T00:00:00+08:00[Asia/Shanghai],2019-05-02T00:00:00+08:00[Asia/Shanghai]

那么只有2019年5月1日0时到5月2日0时的请求才能正常路由。

Cookie路由谓词

CookieRoutePredicateFactory需要提供两个参数,分别是Cookie的name和一个正则表达式(value)。只有在请求中的Cookie对应的name和value和Cookie路由谓词中配置的值匹配的时候,才能匹配命中进行路由。

server
port: 9090
spring:
cloud:
gateway:
routes:
- id: cookie_route
uri: http://www.throwable.club
predicates:
- Cookie=doge,throwable

请求需要携带一个Cookie,name为doge,value需要匹配正则表达式"throwable"才能路由到http://www.throwable.club

这里尝试本地搭建一个订单Order服务,基于SpringBoot2.1.4搭建,启动在9091端口:

// 入口类
@RestController
@RequestMapping(path = "/order")
@SpringBootApplication
public class OrderServiceApplication { public static void main(String[] args) {
SpringApplication.run(OrderServiceApplication.class, args);
} @GetMapping(value = "/cookie")
public ResponseEntity<String> cookie(@CookieValue(name = "doge") String doge) {
return ResponseEntity.ok(doge);
}
}

订单服务application.yaml配置:

spring:
application:
name: order-service
server:
port: 9091

网关路由配置:

spring:
application:
name: route-server
cloud:
gateway:
routes:
- id: cookie_route
uri: http://localhost:9091
predicates:
- Cookie=doge,throwable
curl http://localhost:9090/order/cookie --cookie "doge=throwable"

//响应结果
throwable

Header路由谓词

HeaderRoutePredicateFactory需要提供两个参数,分别是Header的name和一个正则表达式(value)。只有在请求中的Header对应的name和value和Header路由谓词中配置的值匹配的时候,才能匹配命中进行路由。

订单服务中新增一个/header端点:

@RestController
@RequestMapping(path = "/order")
@SpringBootApplication
public class OrderServiceApplication { public static void main(String[] args) {
SpringApplication.run(OrderServiceApplication.class, args);
} @GetMapping(value = "/header")
public ResponseEntity<String> header(@RequestHeader(name = "accessToken") String accessToken) {
return ResponseEntity.ok(accessToken);
}
}

网关的路由配置如下:

spring:
cloud:
gateway:
routes:
- id: header_route
uri: http://localhost:9091
predicates:
- Header=accessToken,Doge
curl -H "accessToken:Doge" http://localhost:9090/order/header

//响应结果
Doge

Host路由谓词

HostRoutePredicateFactory只需要指定一个主机名列表,列表中的每个元素支持Ant命名样式,使用.作为分隔符,多个元素之间使用,区分。Host路由谓词实际上针对的是HTTP请求头中的Host属性。

订单服务中新增一个/header端点:

@RestController
@RequestMapping(path = "/order")
@SpringBootApplication
public class OrderServiceApplication { public static void main(String[] args) {
SpringApplication.run(OrderServiceApplication.class, args);
} @GetMapping(value = "/host")
public ResponseEntity<String> host(@RequestHeader(name = "Host") String host) {
return ResponseEntity.ok(host);
}
}

网关的路由配置如下:

spring:
cloud:
gateway:
routes:
- id: host_route
uri: http://localhost:9091
predicates:
- Host=localhost:9090
curl http://localhost:9090/order/host

//响应结果
localhost:9091 # <--------- 这里要注意一下,路由到订单服务的时候,Host会被修改为localhost:9091

其实可以定制更多样化的Host匹配模式,甚至可以支持URI模板变量。

- Host=www.throwable.**,**.throwable.**

- Host={sub}.throwable.club

请求方法路由谓词

MethodRoutePredicateFactory只需要一个参数:要匹配的HTTP请求方法。

网关的路由配置如下:

spring:
cloud:
gateway:
routes:
- id: method_route
uri: http://localhost:9091
predicates:
- Method=GET

这样配置,所有的进入到网关的GET方法的请求都会路由到http://localhost:9091

订单服务中新增一个/get端点:

@GetMapping(value = "/get")
public ResponseEntity<String> get() {
return ResponseEntity.ok("get");
}
curl http://localhost:9090/order/get

//响应结果
get

请求路径路由谓词

PathRoutePredicateFactory需要PathMatcher模式路径列表和一个可选的标志位参数matchOptionalTrailingSeparator。这个是最常用的一个路由谓词。

spring:
cloud:
gateway:
routes:
- id: path_route
uri: http://localhost:9091
predicates:
- Path=/order/path
@GetMapping(value = "/path")
public ResponseEntity<String> path() {
return ResponseEntity.ok("path");
}
curl http://localhost:9090/order/path

//响应结果
path

此外,可以通过{segment}占位符配置路径如/foo/1/foo/bar/bar/baz,如果通过这种形式配置,在匹配命中进行路由的时候,会提取路径中对应的内容并且将键值对放在ServerWebExchange.getAttributes()集合中,KEY为ServerWebExchangeUtils.URI_TEMPLATE_VARIABLES_ATTRIBUTE,这些提取出来的属性可以供GatewayFilter Factories使用。

请求查询参数路由谓词

QueryRoutePredicateFactory需要一个必须的请求查询参数(param的name)以及一个可选的正则表达式(regexp)。

spring:
cloud:
gateway:
routes:
- id: query_route
uri: http://localhost:9091
predicates:
- Query=doge,throwabl.

这里配置的param就是doge,正则表达式是throwabl.

@GetMapping(value = "/query")
public ResponseEntity<String> query(@RequestParam("name") String doge) {
return ResponseEntity.ok(doge);
}
curl http://localhost:9090/order/query?doge=throwable

//响应结果
throwable

远程IP地址路由谓词

RemoteAddrRoutePredicateFactory匹配规则采用CIDR符号(IPv4或IPv6)字符串的列表(最小值为1),例如192.168.0.1/16(其中192.168.0.1是远程IP地址并且16是子网掩码)。

spring:
cloud:
gateway:
routes:
- id: remoteaddr_route
uri: http://localhost:9091
predicates:
- RemoteAddr=127.0.0.1
@GetMapping(value = "/remote")
public ResponseEntity<String> remote() {
return ResponseEntity.ok("remote");
}
curl http://localhost:9090/order/remote

//响应结果
remote

关于远程IP路由这一个路由谓词其实还有很多扩展手段,这里暂时不展开。

多个路由谓词组合

因为路由配置中的predicates属性其实是一个列表,可以直接添加多个路由规则:

spring:
cloud:
gateway:
routes:
- id: remoteaddr_route
uri: http://localhost:9091
predicates:
- RemoteAddr=xxxx
- Path=/yyyy
- Query=zzzz,aaaa

这些规则是用and逻辑组合的,例如上面的例子相当于:

request = ...
if(request.getRemoteAddr == 'xxxx' && request.getPath match '/yyyy' && request.getQuery('zzzz') match 'aaaa') {
return true;
}
return false;

GatewayFilter工厂

路由过滤器GatewayFilter允许修改进来的HTTP请求内容或者返回的HTTP响应内容。路由过滤器的作用域是一个具体的路由配置。Spring Cloud Gateway提供了丰富的内建的GatewayFilter工厂,可以按需选用。

因为GatewayFilter工厂类实在太多,笔者这里举个简单的例子。

如果我们想对某些请求附加特殊的HTTP请求头,可以选用AddRequestHeaderX-Request-Foo:Barapplication.yml如下:

spring:
cloud:
gateway:
routes:
- id: add_request_header_route
uri: https://example.org
filters:
- AddRequestHeader=X-Request-Foo,Bar

那么所有的从网关入口的HTTP请求都会添加一个特殊的HTTP请求头:X-Request-Foo:Bar

目前GatewayFilter工厂的内建实现如下:

ID 类名 类型 功能
StripPrefix StripPrefixGatewayFilterFactory pre 移除请求URL路径的第一部分,例如原始请求路径是/order/query,处理后是/query
SetStatus SetStatusGatewayFilterFactory post 设置请求响应的状态码,会从org.springframework.http.HttpStatus中解析
SetResponseHeader SetResponseHeaderGatewayFilterFactory post 设置(添加)请求响应的响应头
SetRequestHeader SetRequestHeaderGatewayFilterFactory pre 设置(添加)请求头
SetPath SetPathGatewayFilterFactory pre 设置(覆盖)请求路径
SecureHeader SecureHeadersGatewayFilterFactory pre 设置安全相关的请求头,见SecureHeadersProperties
SaveSession SaveSessionGatewayFilterFactory pre 保存WebSession
RewriteResponseHeader RewriteResponseHeaderGatewayFilterFactory post 重新响应头
RewritePath RewritePathGatewayFilterFactory pre 重写请求路径
Retry RetryGatewayFilterFactory pre 基于条件对请求进行重试
RequestSize RequestSizeGatewayFilterFactory pre 限制请求的大小,单位是byte,超过设定值返回413 Payload Too Large
RequestRateLimiter RequestRateLimiterGatewayFilterFactory pre 限流
RequestHeaderToRequestUri RequestHeaderToRequestUriGatewayFilterFactory pre 通过请求头的值改变请求URL
RemoveResponseHeader RemoveResponseHeaderGatewayFilterFactory post 移除配置的响应头
RemoveRequestHeader RemoveRequestHeaderGatewayFilterFactory pre 移除配置的请求头
RedirectTo RedirectToGatewayFilterFactory pre 重定向,需要指定HTTP状态码和重定向URL
PreserveHostHeader PreserveHostHeaderGatewayFilterFactory pre 设置请求携带的属性preserveHostHeader为true
PrefixPath PrefixPathGatewayFilterFactory pre 请求路径添加前置路径
Hystrix HystrixGatewayFilterFactory pre 整合Hystrix
FallbackHeaders FallbackHeadersGatewayFilterFactory pre Hystrix执行如果命中降级逻辑允许通过请求头携带异常明细信息
AddResponseHeader AddResponseHeaderGatewayFilterFactory post 添加响应头
AddRequestParameter AddRequestParameterGatewayFilterFactory pre 添加请求参数,仅仅限于URL的Query参数
AddRequestHeader AddRequestHeaderGatewayFilterFactory pre 添加请求头

GatewayFilter工厂使用的时候需要知道其ID以及配置方式,配置方式可以看对应工厂类的公有静态内部类XXXXConfig

GlobalFilter工厂

GlobalFilter的功能其实和GatewayFilter是相同的,只是GlobalFilter的作用域是所有的路由配置,而不是绑定在指定的路由配置上。多个GlobalFilter可以通过@Order或者getOrder()方法指定每个GlobalFilter的执行顺序,order值越小,GlobalFilter执行的优先级越高。

注意,由于过滤器有pre和post两种类型,pre类型过滤器如果order值越小,那么它就应该在pre过滤器链的顶层,post类型过滤器如果order值越小,那么它就应该在pre过滤器链的底层。示意图如下:

例如要实现负载均衡的功能,application.yml配置如下:

spring:
cloud:
gateway:
routes:
- id: myRoute
uri: lb://myservice # <-------- lb特殊标记会使用LoadBalancerClient搜索目标服务进行负载均衡
predicates:
- Path=/service/**

目前Spring Cloud Gateway提供的内建的GlobalFilter如下:

类名 功能
ForwardRoutingFilter 重定向
LoadBalancerClientFilter 负载均衡
NettyRoutingFilter Netty的HTTP客户端的路由
NettyWriteResponseFilter Netty响应进行写操作
RouteToRequestUrlFilter 基于路由配置更新URL
WebsocketRoutingFilter Websocket请求转发到下游

内建的GlobalFilter大多数和ServerWebExchangeUtils的属性相关,这里就不深入展开。

跨域配置

网关可以通过配置来控制全局的CORS行为。全局的CORS配置对应的类是CorsConfiguration,这个配置是一个URL模式的映射。例如application.yaml文件如下:

spring:
cloud:
gateway:
globalcors:
corsConfigurations:
'[/**]':
allowedOrigins: "https://docs.spring.io"
allowedMethods:
- GET

在上面的示例中,对于所有请求的路径,将允许来自docs.spring.io并且是GET方法的CORS请求。

Actuator端点相关

引入spring-boot-starter-actuator,需要做以下配置开启gateway监控端点:

management.endpoint.gateway.enabled=true
management.endpoints.web.exposure.include=gateway

目前支持的端点列表:

ID 请求路径 HTTP方法 描述
globalfilters /actuator/gateway/globalfilters GET 展示路由配置中的GlobalFilter列表
routefilters /actuator/gateway/routefilters GET 展示绑定到对应路由配置的GatewayFilter列表
refresh /actuator/gateway/refresh POST 清空路由配置缓存
routes /actuator/gateway/routes GET 展示已经定义的路由配置列表
routes/{id} /actuator/gateway/routes/{id} GET 展示对应ID已经定义的路由配置
routes/{id} /actuator/gateway/routes/{id} POST 添加一个新的路由配置
routes/{id} /actuator/gateway/routes/{id} DELETE 删除指定ID的路由配置

其中/actuator/gateway/routes/{id}添加一个新的路由配置请求参数的格式如下:

{
"id": "first_route",
"predicates": [{
"name": "Path",
"args": {"doge":"/throwable"}
}],
"filters": [],
"uri": "https://www.throwable.club",
"order": 0
}

小结

笔者虽然是一个底层的码畜,但是很久之前就向身边的朋友说:

反应式编程结合同步非阻塞IO或者异步非阻塞IO是目前网络编程框架的主流方向,最好要跟上主流的步伐掌握这些框架的使用,有能力最好成为它们的贡献者。

目前常见的反应式编程框架有:

  • ReactorRxJava2,其中Reactor在后端的JVM应用比较常见,RxJava2在安卓编写的APP客户端比较常见。
  • Reactor-Netty,这个是基于ReactorNetty封装的。
  • Spring-WebFluxSpring-Cloud-Gateway,其中Spring-Cloud-Gateway依赖Spring-WebFlux,而Spring-WebFlux底层依赖于Reactor-Netty

根据这个链式关系,最好系统学习一下ReactorNetty

参考资料:

附录

选用Spring-Cloud-Gateway不仅仅是为了使用新的技术,更重要的是它的性能有了不俗的提升,基准测试项目spring-cloud-gateway-bench的结果如下:

代理组件(Proxy) 平均交互延迟(Avg Latency) 平均每秒处理的请求数(Avg Requests/Sec)
Spring Cloud Gateway 6.61ms 32213.38
Linkered 7.62ms 28050.76
Zuul(1.x) 12.56ms 20800.13
None(直接调用) 2.09ms 116841.15

原文链接

(本文完 c-3-d e-a-20190504)

Spring Cloud Gateway入坑记的更多相关文章

  1. Spring Cloud Config采坑记

    1. Spring Cloud Config采坑记 1.1. 问题 在本地运行没问题,本地客户端服务能连上本地服务端服务,可一旦上线,发现本地连不上线上的服务 服务端添加security登录加密,客户 ...

  2. Spring Cloud Gateway、并发编程等等

    2019年 JUC线程池服务ExecutorService接口实现源码分析 Github Page:http://www.throwable.club/2019/07/27/java-concurre ...

  3. Spring Cloud Gateway 之请求坑位[微服务IP不同请求会失败]

    问题产生背景 在使用Spring Cloud Gateway过程中,希望配置多Routes映射不同的微服务,因为Gateway 和Zuul的访问路径不同(zuul 会带有服务service Id),造 ...

  4. api网关揭秘--spring cloud gateway源码解析

    要想了解spring cloud gateway的源码,要熟悉spring webflux,我的上篇文章介绍了spring webflux. 1.gateway 和zuul对比 I am the au ...

  5. Spring Cloud Gateway VS Zuul 比较,怎么选择?

    Spring Cloud Gateway 是 Spring Cloud Finchley 版推出来的新组件,用来代替服务网关:Zuul. 那 Spring Cloud Gateway 和 Zuul 都 ...

  6. 微服务架构spring cloud - gateway网关限流

    1.算法 在高并发的应用中,限流是一个绕不开的话题.限流可以保障我们的 API 服务对所有用户的可用性,也可以防止网络攻击. 一般开发高并发系统常见的限流有:限制总并发数(比如数据库连接池.线程池). ...

  7. Spring Cloud Gateway 实现Token校验

    在我看来,在某些场景下,网关就像是一个公共方法,把项目中的都要用到的一些功能提出来,抽象成一个服务.比如,我们可以在业务网关上做日志收集.Token校验等等,当然这么理解很狭隘,因为网关的能力远不止如 ...

  8. 深入学习spring cloud gateway 限流熔断

    前言 Spring Cloud Gateway 目前,Spring Cloud Gateway是仅次于Spring Cloud Netflix的第二个最受欢迎的Spring Cloud项目(就GitH ...

  9. 【Spring Cloud & Alibaba 实战 | 总结篇】Spring Cloud Gateway + Spring Security OAuth2 + JWT 实现微服务统一认证授权和鉴权

    一. 前言 hi,大家好~ 好久没更文了,期间主要致力于项目的功能升级和问题修复中,经过一年时间的打磨,[有来]终于迎来v2.0版本,相较于v1.x版本主要完善了OAuth2认证授权.鉴权的逻辑,结合 ...

随机推荐

  1. 菜鸟刷面试题(二、RabbitMQ篇)

    目录: rabbitmq 的使用场景有哪些? rabbitmq 有哪些重要的角色? rabbitmq 有哪些重要的组件? rabbitmq 中 vhost 的作用是什么? rabbitmq 的消息是怎 ...

  2. IO 多路复用详解

    转自:https://blog.csdn.net/sehanlingfeng/article/details/78920423

  3. 第04组 Beta冲刺(3/4)

    队名:斗地组 组长博客:地址 作业博客:Beta冲刺(3/4) 各组员情况 林涛(组长) 过去两天完成了哪些任务: 1.分配展示任务 2.收集各个组员的进度 3.写博客 展示GitHub当日代码/文档 ...

  4. C#中获取指定目录下所有目录的名称、全路径和创建日期

    场景 指定一个路径,根据这个父级路径获取此目录下所有目录的名称.全路径.创建日期等信息. 注: 博客主页: https://blog.csdn.net/badao_liumang_qizhi 关注公众 ...

  5. GO-&获取地址与*解引用

    &变量 获取变量在内存空间的地址 *变量地址 获取变量的值 一.普通数据 package main import "fmt" func main(){ b :=1111 c ...

  6. 如何在Macbook上安装MySQL ?

    MySQL是常用的一款开源数据库,对各个平台都提供了支持,而Macbook又作为程序员的一款主力开发工具经常被使用.因此怎么在Macbook上安装MySQL进行程序开发也成了一项基本技能.下面来跟随本 ...

  7. jeesite3环境部署时初始化数据库注意问题

    ---恢复内容开始--- 首先要修改jeesite.properties下数据库连接方式,注意选择自己的数据库 其次在pom.xml文件中修改对应的数据库连接方式 最后运行db文件夹下的init-db ...

  8. C# 中使用 Redis 简单存储

    Redis 是一个开源的使用 ANSI C语言编写的支持网络.可基于内存也可持久化的日志型.Key-Value 数据库. 常用它来存储缓存数据,能非常轻松的实现缓存过期刷新机制. 多种语言都可以连接到 ...

  9. Master Note for Transportable Tablespaces (TTS) -- Common Questions and Issues (Doc ID 1166564.1)

    APPLIES TO: Oracle Database Cloud Exadata Service - Version N/A and laterOracle Database Cloud Servi ...

  10. [PHP] cli环境下php设置进程名字

    if (function_exists('cli_set_process_title')) { cli_set_process_title("superman php master proc ...