APP下载
报价宝  ›  科技  › 

微服务闸道器Spring Cloud Gateway代替Spring Cloud Netflix Zuul

报价宝 来源:baojiabao.com 发布时间:2019-07-13 11:48:00 10月23日更新
报价宝综合消息微服务闸道器Spring Cloud Gateway代替Spring Cloud Netflix Zuul

一、为什么是Spring Cloud Gateway

一句话, Spring Cloud已经放弃Netflix Zuul了 。现在Spring Cloud中引用的还是Zuul 1.x版本,而这个版本是基于过滤器的,是阻塞IO,不支援长连线。Zuul 2.x版本跟1.x的架构大一样,效能也有所提升。既然Spring Cloud已经不再整合Zuul 2.x了,那么是时候了解一下Spring Cloud Gateway了。

可以看到,最新的Spring Cloud中的Zuul还是1.3.1版本

而且,官网中也明确说了不再维护Zuul了

(PS:顺便补充几个名词: 服务发现(Eureka),断路器(Hystrix),智慧路由(Zuul),客户端负载均衡(Ribbon))

二、 API闸道器

API闸道器是一个服务器,是系统的唯一入口。从面向物件设计的角度看,它与外观模式类似。API闸道器封装了系统内部架构,为每个客户端提供一个定制的API。它可能还具有其它职责,如身份验证、监控、负载均衡、快取、请求分片与管理、静态响应处理。API闸道器方式的核心要点是,所有的客户端和消费端都通过统一的闸道器接入微服务,在闸道器层处理所有的非业务功能。通常,闸道器也是提供REST/HTTP的访问API。

闸道器应当具备以下功能:

效能:API高可用,负载均衡,容错机制。安全:许可权身份认证、脱敏,流量清洗,后端签名(保证全链路可信呼叫),黑名单(非法呼叫的限制)。日志:日志记录(spainid,traceid)一旦涉及分散式,全链路跟踪必不可少。快取:资料快取。监控:记录请求响应资料,api耗时分析,效能监控。限流:流量控制,错峰流控,可以定义多种限流规则。灰度:线上灰度部署,可以减小风险。路由:动态路由规则。目前,比较流行的闸道器有:Nginx 、 Kong 、Orange等等,还有微服务闸道器Zuul 、Spring Cloud Gateway等等

对于 API Gateway,常见的选型有基于 Openresty 的 Kong、基于 Go 的 Tyk 和基于 Java 的 Zuul。这三个选型本身没有什么明显的区别,主要还是看技术栈是否能满足快速应用和二次开发。

以上说的这些功能,这些开源的闸道器元件都有,或者借助Lua也能实现,比如:Nginx + Lua

那要Spring Cloud Gateway还有什么用呢?

其实,我个人理解是这样的:

像Nginx这类闸道器,效能肯定是没得说,它适合做那种门户闸道器,是作为整个全域性的闸道器,是对外的,处于最外层的;而Gateway这种,更像是业务闸道器,主要用来对应不同的客户端提供服务的,用于聚合业务的。各个微服务独立部署,职责单一,对外提供服务的时候需要有一个东西把业务聚合起来。像Nginx这类闸道器,都是用不同的语言编写的,不易于扩充套件;而Gateway就不同,它是用Java写的,易于扩充套件和维护Gateway这类闸道器可以实现熔断、重试等功能,这是Nginx不具备的所以,你看到的闸道器可能是这样的:

2.1. Netflix Zuul 1.x VS Netflix Zuul 2.x

三、Spring Cloud Gateway

3.1. 特性

基于Spring Framework 5、Project Reactor和Spring Boot 2.0构建能够在任意请求属性上匹配路由predicates(谓词) 和 filters(过滤器)是特定于路由的集成了Hystrix断路器集成了Spring Cloud DiscoveryClient易于编写谓词和过滤器请求速率限制路径重写3.2. 术语

Route : 路由是闸道器的基本元件。它由ID、目标URI、谓词集合和过滤器集合定义。如果聚合谓词为true,则匹配路由

Predicate : This is a Java 8 Function Predicate

Filter : 是GatewayFilter的一个例项,在这里,可以在传送下游请求之前或之后修改请求和响应

3.3. 原理

(PS:看到这张图是不是很熟悉,没错,很像SpringMVC的请求处理过程)

客户端向Spring Cloud Gateway发出请求。如果Gateway Handler Mapping确定请求与路由匹配,则将其传送给Gateway Web Handler。这个Handler执行通过特定于请求的过滤器链传送请求。过滤器可以在传送代理请求之前或之后执行逻辑。执行所有的“pre”过滤逻辑,然后发出代理请求,最后执行“post”过滤逻辑。

3.4. Route Predicate Factories

Spring Cloud Gateway 包含许多内建的 Route Predicate Factories所有这些predicates用于匹配HTTP请求的不同属性多个 Route Predicate Factories 可以通过逻辑与(and)结合起来一起使用3.4.1. After Route Predicate Factory

spring:

cloud:

gateway:

routes:

- id: after_route

uri: https://example.org

predicates:

- After=2017-01-20T17:42:47.789-07:00[America/Denver]

这个路由匹配“美国丹佛时间2017-01-20 17:42”之后的任意请求

3.4.2. Header Route Predicate Factory

spring:

cloud:

gateway:

routes:

- id: header_route

uri: https://example.org

predicates:

- Header=X-Request-Id, d+

这个路由匹配“请求头包含X-Request-Id并且其值匹配正则表示式d+”的任意请求

3.4.3. Method Route Predicate Factory

spring:

cloud:

gateway:

routes:

- id: method_route

uri: https://example.org

predicates:

- Method=GET

这个路由匹配任意GET请求

3.4.4. Path Route Predicate Factory

spring:

cloud:

gateway:

routes:

- id: host_route

uri: https://example.org

predicates:

- Path=/foo/{segment},/bar/{segment}

这个路由匹配这样路径的请求,比如:/foo/1 或 /foo/bar 或 /bar/baz

3.4.5. Query Route Predicate Factory

这个Predicate有两个引数:一个必须的引数名和一个可选的正则表示式

spring:

cloud:

gateway:

routes:

- id: query_route

uri: https://example.org

predicates:

- Query=baz

这个路由匹配“查询引数中包含baz”的请求

spring:

cloud:

gateway:

routes:

- id: query_route

uri: https://example.org

predicates:

- Query=foo, ba.

这个路由匹配“查询引数中包含foo,并且其引数值满足正则表示式ba.”的请求,比如:bar,baz

3.4.6. RemoteAddr Route Predicate Factory

这个路由接受一个IP(IPv4或IPv6)地址字串。例如:192.168.0.1/16,其中192.168.0.1,16是子网掩码

spring:

cloud:

gateway:

routes:

- id: remoteaddr_route

uri: https://example.org

predicates:

- RemoteAddr=192.168.1.1/24

这里路由匹配远端地址是这样的请求,例如:192.168.1.10

3.5. GatewayFilter Factories(闸道器过滤器)

路由过滤器允许以某种方式修改传入的HTTP请求或传出HTTP响应。路由过滤器的作用域是特定的路由。Spring Cloud Gateway包含许多内建的闸道器过滤器工厂。

3.5.1. AddRequestHeader GatewayFilter Factory

spring:

cloud:

gateway:

routes:

- id: add_request_header_route

uri: https://example.org

filters:

- AddRequestHeader=X-Request-Foo, Bar

对于所有匹配的请求,将会给传给下游的请求新增一个请求头 X-Request-Foo:Bar

3.5.2. AddRequestParameter GatewayFilter Factory

spring:

cloud:

gateway:

routes:

- id: add_request_parameter_route

uri: https://example.org

filters:

- AddRequestParameter=foo, bar

对于所有匹配的请求,将给传给下游的请求新增一个查询引数 foo=bar

3.5.3. AddResponseHeader GatewayFilter Factory

spring:

cloud:

gateway:

routes:

- id: add_response_header_route

uri: https://example.org

filters:

- AddResponseHeader=X-Response-Foo, Bar

对于所有匹配的请求,新增一个响应头 X-Response-Foo:Bar

3.5.4. Hystrix GatewayFilter Factory

Hystrix 闸道器过滤器允许你将断路器引入闸道器路由,保护你的服务免受级联失败的影响,并在下游发生故障时提供预备响应。

为了启用Hystrix闸道器过滤器,你需要引入 spring-cloud-starter-netflix-hystrix

Hystrix闸道器过滤器需要一个name引数,这个 name 是 HystrixCommand 的名字

spring:

cloud:

gateway:

routes:

- id: hystrix_route

uri: https://example.org

filters:

- Hystrix=myCommandName

给这个过滤器包装一个名字叫 myCommandName 的 HystrixCommand

Hystrix闸道器过滤器也接受一个可选的引数 fallbackUri ,但是目前只支援 forward: 字首的URL。也就是说,如果这个fallback被呼叫,请求将被重定向到匹配的这个URL。

spring:

cloud:

gateway:

routes:

- id: hystrix_route

uri: lb://backing-service:8088

predicates:

- Path=/consumingserviceendpoint

filters:

- name: Hystrix

args:

name: fallbackcmd

fallbackUri: forward:/incaseoffailureusethis

- RewritePath=/consumingserviceendpoint, /backingserviceendpoint

当fallback被呼叫的时候,请求将被重定向到 /incaseoffailureusethis

spring:

cloud:

gateway:

routes:

- id: ingredients

uri: lb://ingredients

predicates:

- Path=//ingredients/**

filters:

- name: Hystrix

args:

name: fetchIngredients

fallbackUri: forward:/fallback

- id: ingredients-fallback

uri: http://localhost:9994

predicates:

- Path=/fallback

在这个例子中,专门定义了一个端点来处理/fallback请求,它在localhost:9994上。也就是说,当fallback被呼叫的时候将重定向到http://localhost:9994/fallback

3.5.5. PrefixPath GatewayFilter Factory

spring:

cloud:

gateway:

routes:

- id: prefixpath_route

uri: https://example.org

filters:

- PrefixPath=/mypath

所有匹配的请求都将加上字首/mypath。例如,如果请求是/hello,那么经过这个过滤器后,发出去的请求变成/mypath/hello

3.5.6. RequestRateLimiter GatewayFilter Factory

RequestRateLimiter闸道器过滤器使用一个 RateLimiter 实现来决定是否当前请求可以继续往下走。如果不能,预设将返回 HTTP 429 - Too Many Requests

这个过滤器接受一个可选的引数 keyResolver ,这个引数是一个特定的rate limiter

keyResolver是实现了 KeyResolver 界面的一个Bean。

在配置的时候,使用SpEL按名称引用Bean。 #{@myKeyResolver} 是一个SpEL表示式,表示引用名字叫 myKeyResolver 的Bean。

KeyResolver.java

1 public interface KeyResolver {

2 Mono resolve(ServerWebExchange exchange);

3 }

KeyResolver预设的实现是PrincipalNameKeyResolver,它从ServerWebExchange中检索Principal,并呼叫Principal.getName()方法。

预设情况下,如果 KeyResolver 没有找到一个key,那么请求将会被denied(译:否认,拒绝)。这种行为可以通过 spring.cloud.gateway.filter.request-rate-limiter.deny-empty-key (true or false) 和 spring.cloud.gateway.filter.request-rate-limiter.empty-key-status-code 属性来进行调整.

Redis RateLimiter

需要引用 spring-boot-starter-data-redis-reactive

这个逻辑使用令牌桶算法

redis-rate-limiter.replenishRate : 允许使用者每秒处理多少个请求。这是令牌桶被填充的速率。redis-rate-limiter.burstCapacity : 使用者在一秒钟内允许执行的最大请求数。这是令牌桶可以容纳的令牌数量。将此值设定为0将阻塞所有请求。一个稳定的速率是通过将replenishRate 和 burstCapacity设为相同的值来实现的。也可以将burstCapacity设得比replenishRate大,以应对临时爆发的流量。在这种情况下,需要允许速率限制器在突发事件之间间隔一段时间,因为连续两次突发事件将导致丢弃请求(HTTP 429 - Too Many Requests)

application.yml

spring:

cloud:

gateway:

routes:

- id: requestratelimiter_route

uri: https://example.org

filters:

- name: RequestRateLimiter

args:

redis-rate-limiter.replenishRate: 10

redis-rate-limiter.burstCapacity: 20

Config.java

1 @Bean

2 KeyResolver userKeyResolver() {

3 return exchange -> Mono.just(exchange.getRequest().getQueryParams().getFirst("user"));

4 }

这里定义了每个使用者的请求速率限制为10。允许使用20个请求,但是在接下来的一秒中,只有10个请求可用。

这个例子中只是简单地从请求引数中获取"user",在实际生产环境中不建议这么做。

我们也可以通过实现RateLimiter界面来自定义,这个时候,在配置中我们就需要引用这个Bean,例如:#{@myRateLimiter}

spring:

cloud:

gateway:

routes:

- id: requestratelimiter_route

uri: https://example.org

filters:

- name: RequestRateLimiter

args:

rate-limiter: "#{@myRateLimiter}"

key-resolver: "#{@userKeyResolver}"

3.5.7. Default Filters

如果你想要新增一个过滤器并且把它应用于所有路由的话,你可以用 spring.cloud.gateway.default-filters 。这个属性接受一个过滤器列表。

spring:

cloud:

gateway:

default-filters:

- AddResponseHeader=X-Response-Default-Foo, Default-Bar

- PrefixPath=/httpbin

3.6. Global Filters(全域性过滤器)

GlobalFilter 界面的方法签名和 GatewayFilter 相同。这些是有条件地应用于所有路由的特殊过滤器。

3.6.1. GlobalFilter和GatewayFilter的顺序

当一个请求过来的时候,将会新增所有的 GatewayFilter 例项和所有特定的 GatewayFilter 例项到过滤器链上。过滤器链按照 org.springframework.core.Ordered 界面对该链路上的过滤器进行排序。你可以通过实现界面中的 getOrder() 方法或者使用 @Order 注解。

Spring Cloud Gateway将过滤器执行逻辑分为“pre”和“post”阶段。优先级最高的过滤器将会是“pre”阶段中的第一个过滤器,同时它也将是“post”阶段中的最后一个过滤器。

ExampleConfiguration.java

1 @Bean

2 @Order(-1)

3 public GlobalFilter a() {

4 return (exchange, chain) -> {

5 log.info("first pre filter");

6 return chain.filter(exchange).then(Mono.fromRunnable(() -> {

7 log.info("third post filter");

8 }));

9 };

10 }

11

12 @Bean

13 @Order(0)

14 public GlobalFilter b() {

15 return (exchange, chain) -> {

16 log.info("second pre filter");

17 return chain.filter(exchange).then(Mono.fromRunnable(() -> {

18 log.info("second post filter");

19 }));

20 };

21 }

22

23 @Bean

24 @Order(1)

25 public GlobalFilter c() {

26 return (exchange, chain) -> {

27 log.info("third pre filter");

28 return chain.filter(exchange).then(Mono.fromRunnable(() -> {

29 log.info("first post filter");

30 }));

31 };

32 }

3.6.2. LoadBalancerClient Filter

LoadBalancerClientFilter查询exchange属性中查询 ServerWebExchangeUtils.GATEWAY_REQUEST_URL_ATTR 一个URI。如果url符合 lb schema(例如: lb://myservice ),那么它将使用Spring Cloud LoadBalancerClient 来解析这个名字到一个实际的主机和埠,并替换URI中相同的属性。原始url中未被修改的部分被附加到ServerWebExchangeUtils.GATEWAY_ORIGINAL_REQUEST_URL_ATTR属性列表中。

application.yml

spring:

cloud:

gateway:

routes:

- id: myRoute

uri: lb://service

predicates:

- Path=/service/**

预设情况下,当一个服务例项在LoadBalancer中没有找到时,将返回503。你可以通过配置 spring.cloud.gateway.loadbalancer.use404=true 来让它返回404。

3.7. 配置

RouteDefinitionLocator.java

1 public interface RouteDefinitionLocator {

2 Flux getRouteDefinitions();

3 }

预设情况下,PropertiesRouteDefinitionLocator通过@ConfigurationProperties机制载入属性

下面两段配置是等价的

spring:

cloud:

gateway:

routes:

- id: setstatus_route

uri: https://example.org

filters:

- name: SetStatus

args:

status: 401

- id: setstatusshortcut_route

uri: https://example.org

filters:

- SetStatus=401

下面用Java配置

GatewaySampleApplication.java

1 // static imports from GatewayFilters and RoutePredicates

2 @Bean

3 public RouteLocator customRouteLocator(RouteLocatorBuilder builder, ThrottleGatewayFilterFactory throttle) {

4 return builder.routes()

5 .route(r -> r.host("**.abc.org").and().path("/image/png")

6 .filters(f ->

7 f.addResponseHeader("X-TestHeader", "foobar"))

8 .uri("http://httpbin.org:80")

9 )

10 .route(r -> r.path("/image/webp")

11 .filters(f ->

12 f.addResponseHeader("X-AnotherHeader", "baz"))

13 .uri("http://httpbin.org:80")

14 )

15 .route(r -> r.order(-1)

16 .host("**.throttle.org").and().path("/get")

17 .filters(f -> f.filter(throttle.apply(1,

18 1,

19 10,

20 TimeUnit.SECONDS)))

21 .uri("http://httpbin.org:80")

22 )

23 .build();

24 }

这种风格允许自定义更多的谓词断言,预设是逻辑与(and)。你也可以用and() , or() , negate()

再来一个例子

1 @SpringBootApplication

2 public class DemogatewayApplication {

3 @Bean

4 public RouteLocator customRouteLocator(RouteLocatorBuilder builder) {

5 return builder.routes()

6 .route("path_route", r -> r.path("/get")

7 .uri("http://httpbin.org"))

8 .route("host_route", r -> r.host("*.myhost.org")

9 .uri("http://httpbin.org"))

10 .route("hystrix_route", r -> r.host("*.hystrix.org")

11 .filters(f -> f.hystrix(c -> c.setName("slowcmd")))

12 .uri("http://httpbin.org"))

13 .route("hystrix_fallback_route", r -> r.host("*.hystrixfallback.org")

14 .filters(f -> f.hystrix(c -> c.setName("slowcmd").setFallbackUri("forward:/hystrixfallback")))

15 .uri("http://httpbin.org"))

16 .route("limit_route", r -> r

17 .host("*.limited.org").and().path("/anything/**")

18 .filters(f -> f.requestRateLimiter(c -> c.setRateLimiter(redisRateLimiter())))

19 .uri("http://httpbin.org"))

20 .build();

21 }

22 }

3.8. CORS配置

spring:

cloud:

gateway:

globalcors:

corsConfigurations:

'[/**]':

allowedOrigins: "https://docs.spring.io"

allowedMethods:

- GET

上面的例子中,所有原始为docs.spring.io的GET请求均被允许跨域请求。

四、示例

本例中又4个专案,如下图:

4.1. cjs-eureka-server

pom.xml

1

2 3 xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">

4 4.0.0

5

6 org.springframework.boot

7 spring-boot-starter-parent

8 2.1.6.RELEASE

9

10

11 com.cjs.example

12 cjs-eureka-server

13 0.0.1-SNAPSHOT

14 cjs-eureka-server

15

16

17 1.8

18 Greenwich.SR1

19

20

21

22

23 org.springframework.cloud

24 spring-cloud-starter-netflix-eureka-server

25

26

27 ch.qos.logback

28 logback-classic

29 1.2.3

30

31

32

33

34

35

36 org.springframework.cloud

37 spring-cloud-dependencies

38 ${spring-cloud.version}

39 pom

40 import

41

42

43

44

45

46

47

48 org.springframework.boot

49 spring-boot-maven-plugin

50

51

52

53

54

application.yml

1 server:

2 port: 8761

3

4 spring:

5 application:

6 name: cjs-eureka-server

7

8 eureka:

9 client:

10 service-url:

11 defaultZone: http://10.0.29.92:8761/eureka/,http://10.0.29.232:8761/eureka/

12

13 logging:

14 file: ${spring.application.name}.log

Application.java

1 package com.cjs.example;

2

3 import org.springframework.boot.SpringApplication;

4 import org.springframework.boot.autoconfigure.SpringBootApplication;

5 import org.springframework.cloud.netflix.eureka.server.EnableEurekaServer;

6

7 /**

8 * @author chengjiansheng

9 * @date 2019-06-26

10 */

11 @EnableEurekaServer

12 @SpringBootApplication

13 public class CjsEurekaServerApplication {

14

15 public static void main(String[] args) {

16 SpringApplication.run(CjsEurekaServerApplication.class, args);

17 }

18

19 }

4.2. cjs-gateway-server

pom.xml

1

2 3 xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">

4 4.0.0

5

6 org.springframework.boot

7 spring-boot-starter-parent

8 2.1.6.RELEASE

9

10

11 com.cjs.example

12 cjs-gateway-server

13 0.0.1-SNAPSHOT

14 cjs-gateway-server

15

16

17 1.8

18 Greenwich.SR1

19

20

21

22

23 org.springframework.cloud

24 spring-cloud-starter-gateway

25

26

27

28 org.springframework.boot

29 spring-boot-starter-data-redis-reactive

30

31

32 ch.qos.logback

33 logback-classic

34 1.2.3

35

36

37

38

39

40

41 org.springframework.cloud

42 spring-cloud-dependencies

43 ${spring-cloud.version}

44 pom

45 import

46

47

48

49

50

51

52

53 org.springframework.boot

54 spring-boot-maven-plugin

55

56

57

58

59

application.yml

1 server:

2 port: 8080

3 servlet:

4 context-path: /

5 spring:

6 application:

7 name: cjs-gateway-server

8 redis:

9 host: 10.0.29.187

10 password: 123456

11 port: 6379

12 cloud:

13 gateway:

14 routes:

15 - id: header_route

16 uri: http://10.0.29.187:8080/

17 predicates:

18 - Header=X-Request-Id, d+

19 # - id: path_route

20 # uri: http://10.0.29.187:8080/

21 # predicates:

22 # - Path=/foo/{segment},/bar/{segment}

23 - id: query_route

24 uri: http://10.0.29.187:8080/

25 predicates:

26 - Query=baz

27 # default-filters:

28 # - AddResponseHeader=X-Response-Foo, Bar

29 # - AddRequestParameter=hello, world

30

31 logging:

32 file: ${spring.application.name}.log

Application.java

1 package com.cjs.example.gateway;

2

3 import org.springframework.boot.SpringApplication;

4 import org.springframework.boot.autoconfigure.SpringBootApplication;

5 import org.springframework.cloud.gateway.filter.ratelimit.KeyResolver;

6 import org.springframework.cloud.gateway.filter.ratelimit.RedisRateLimiter;

7 import org.springframework.cloud.gateway.route.RouteLocator;

8 import org.springframework.cloud.gateway.route.builder.RouteLocatorBuilder;

9 import org.springframework.context.annotation.Bean;

10 import org.springframework.web.bind.annotation.RestController;

11 import reactor.core.publisher.Mono;

12

13 /**

14 * @author chengjiansheng

15 */

16 @RestController

17 @SpringBootApplication

18 public class CjsGatewayServerApplication {

19

20 public static void main(String[] args) {

21 SpringApplication.run(CjsGatewayServerApplication.class, args);

22 }

23

24 @Bean

25 public RouteLocator customRouteLocator(RouteLocatorBuilder builder) {

26 return builder.routes()

27 .route("path_route", r -> r.path("/price/**")

28 .filters(f -> f.addRequestHeader("hello", "world")

29 .addRequestParameter("name", "zhangsan")

30 .requestRateLimiter(c -> c.setRateLimiter(redisRateLimiter())))

31 .uri("http://10.0.29.232:8082/price"))

32 .route("path_route", r -> r.path("/commodity/**").uri("http://10.0.29.92:8081/commodity"))

33 .build();

34 }

35

36 @Bean

37 public RedisRateLimiter redisRateLimiter() {

38 return new RedisRateLimiter(2, 4);

39 }

40

41 @Bean

42 KeyResolver userKeyResolver() {

43 // return exchange -> Mono.just(exchange.getRequest().getHeaders().getFirst("userId"));

44 return exchange -> Mono.just(exchange.getRequest().getQueryParams().getFirst("userId"));

45 }

46

47 }

其余程式码就不一一贴出来了,都在git上

https://github.com/chengjiansheng/cjs-springcloud-example

截两张图吧

下面看效果

效果一:正常路由

效果二:限流

1 ab -n 20 -c 10 http://10.0.29.187:8080/price/index/test01?userId=123

观察控制台会看到

1 2019-07-03 18:21:23.946 DEBUG 34433 --- [ioEventLoop-4-1] o.s.c.g.f.ratelimit.RedisRateLimiter : response: Response{allowed=false, headers={X-RateLimit-Remaining=0, X-RateLimit-Burst-Capacity=4, X-RateLimit-Replenish-Rate=2}, tokensRemaining=-1}

2 2019-07-03 18:21:23.946 DEBUG 34433 --- [ioEventLoop-4-1] o.s.w.s.adapter.HttpWebHandler

粉丝福利

今天给大家免费分享一套阿里架构师传授的一套教学资源。帮助大家在成为架构师的道路上披荆斩棘。

这套视讯课程详细讲解了(Spring,MyBatis,Netty源代码分析,高并发、高效能、分散式、微服务架构的原理,JVM效能优化、分散式架构)等这些成为架构师必备的内容!

而且还把框架需要用到的各种程式进行了打包,根据基础视讯可以让你轻松搭建分散式框架环境,像在企业生产环境一样进行学习和实践。

关注作者+转发 转发 转发,

然后私信关键词(Java)就能免费领取这一套价值18888的架构资料

文章标签: 报价宝 降噪耳机价格 耳机价格 红米手机价格 华为手机价格 小米手机价格 电视机价格 笔记本电脑价格 笔记本价格 汽车价格 报价宝 汽车价格 笔记本价格 笔记本电脑价格 红米手机价格