Spring Cloud Gateway真的有那么差吗?

Spring Cloud从一开始最受大家质疑的就是网关性能,那是由于Spring Cloud最初选择了使用Netflix几年前开源的Zuul作为基础,而高性能版的Zuul 2在经过了多次跳票之后,对于Spring这样的整合专家可能也不愿意再继续等待,Spring Cloud Gateway应运而生,那么这个由Spring社区自己开发推出的网关又如何呢?最近一篇《实测 | 转型微服务,这4大工具谁是API网关性能最优?》的翻译中对比了NGINX、ZUUL、Spring Cloud Gateway、Linkerd,得出的结论让不少读者感到诧异,Spring Cloud Gateway是否真的有这么差?不妨通过下文来进一步了解(以下内容转载自来《Spring Cloud与Docker微服务实战》作者周立的博文,原文地址:http://www.itmuch.com/spring-cloud-sum/performance-zuul-and-gateway-linkerd/)。

动机

已经不止一次看到“Spring Cloud Gateway性能比Zuul更差”的言论了,不少人人云亦云,来问我,既然如此,那Spring官方还开发Spring Cloud Gateway干嘛?难道仅仅是为了支持Zuul 1.x不支持的长连接、Web Socket吗?

故而写篇博客,纠正一下大家的错误观点。

开端

网上搜索了一下,说Spring Cloud Gateway性能比Zuul差的言论来自:http://www.servicemesh.cn/?/article/45

作者使用 ab 进行benchmark,操作非常标准。从结果来看,确实Spring Cloud Gateway比Zuul差了一大截。

但,让我们打开官方的Issue:Throughput problems when compared with Netflix Zuul and Nginx ,里面官方人员回答道:

reactor-netty has issues with http 1.0 and hence ab. reactor/reactor-netty#21

不妨跟踪到reactor/reactor-netty#21 ,看看说了啥:

As discussed recently about the issue raised on https://jira.spring.io/browse/SPR-14964, a very simple ab-n1-c1http://localhost:8082/items/10 on Spring + Reactor Netty based server block forever likely because Reactor Netty does not support HTTP 1.0.

里面说了,Reactor Netty不支持HTTP 1.0,而Spring Cloud Gateway依赖了 reactor-netty 。

也就是说——由于reactor-netty的bug,使用 ab 压测结果并不准确!

正确姿势

官方建议使用 wrk 进行benchmark测试。不仅如此,官方人员还十(丧)分(心)贴(病)心(狂)地创建了一个benchmark的项目:spring-cloud-gateway-bench ,其中对比了:

  • Spring Cloud Gateway

  • Zuul

  • Linkerd

三者的性能。

思路非常简单:static 项目是一个使用Go语言编写的简单服务器;然后分别使用Gateway/Zuul/Linkerd来代理该服务的端点,并对比。

最终结果:

组件RPS(request per second)
Spring Cloud GatewayRequests/sec: 32213.38
ZuulRequests/sec: 20800.13
LinkerdRequests/sec: 28050.76

从结果可知,Spring Cloud Gateway的RPS是Zuul1的1.6倍!比Linkerd性能还好!

展望

  • 本文的Zuul,指的是Zuul 1.x,是一个基于阻塞io的API Gateway。

  • Spring Cloud Gateway是一个很有前途的项目,上手简单,功能也比较强大。

  • Linkerd也是一个非常有前途的项目,是基于Scala实现的、目前市面上仅有的生产级别的Service Mesh(其他诸如Istio、Conduit暂时还不能用于生产)。

  • Zuul已经发布了Zuul 2.x,基于Netty,也是非阻塞的,支持长连接,但Spring Cloud暂时还没有整合计划。


推荐阅读

Spring Cloud微服务架构汇总

Netflix 的上线工具 Spinnaker

Dubbo将积极适配Spring Cloud生态

浅谈微服务基建的逻辑

Service Mesh:下一代微服务

微服务(Microservices)【翻译】

那些没说出口的研发之痛,做与不做微服务的几大理由


长按指纹

一键关注


点击 “阅读原文” 看看本号其他精彩内容

已标记关键词 清除标记
之前没有接触微服务,最近需要用到微服务了,所以先搭一个spring cloud测试一下,进行到服务网关的时候遇到了这个问题。 先上错误日志: ``` 2020-09-02 10:08:05.979 INFO 53824 --- [ctor-http-nio-3] c.n.l.DynamicServerListLoadBalancer : Using serverListUpdater PollingServerListUpdater 2020-09-02 10:08:06.007 INFO 53824 --- [ctor-http-nio-3] c.netflix.config.ChainedDynamicProperty : Flipping property: my-service.ribbon.ActiveConnectionsLimit to use NEXT property: niws.loadbalancer.availabilityFilteringRule.activeConnectionsLimit = 2147483647 2020-09-02 10:08:06.009 INFO 53824 --- [ctor-http-nio-3] c.n.l.DynamicServerListLoadBalancer : DynamicServerListLoadBalancer for client my-service initialized: DynamicServerListLoadBalancer:{NFLoadBalancer:name=my-service,current list of Servers=[192.168.1.52:8088],Load balancer stats=Zone stats: {defaultzone=[Zone:defaultzone; Instance count:1; Active connections count: 0; Circuit breaker tripped count: 0; Active connections per server: 0.0;] },Server stats: [[Server:192.168.1.52:8088; Zone:defaultZone; Total Requests:0; Successive connection failure:0; Total blackout seconds:0; Last connection made:Thu Jan 01 08:00:00 CST 1970; First connection made: Thu Jan 01 08:00:00 CST 1970; Active Connections:0; total failure count in last (1000) msecs:0; average resp time:0.0; 90 percentile resp time:0.0; 95 percentile resp time:0.0; min resp time:0.0; max resp time:0.0; stddev resp time:0.0] ]}ServerList:org.springframework.cloud.netflix.ribbon.eureka.DomainExtractingServerList@6e4b35d3 2020-09-02 10:08:06.024 DEBUG 53824 --- [ctor-http-nio-3] o.s.c.g.r.RouteDefinitionRouteLocator : RouteDefinition ReactiveCompositeDiscoveryClient_CONFIG applying {pattern=/CONFIG/**} to Path 2020-09-02 10:08:06.025 DEBUG 53824 --- [ctor-http-nio-3] o.s.c.g.r.RouteDefinitionRouteLocator : RouteDefinition ReactiveCompositeDiscoveryClient_CONFIG applying filter {regexp=/CONFIG/(?<remaining>.*), replacement=/${remaining}} to RewritePath 2020-09-02 10:08:06.026 DEBUG 53824 --- [ctor-http-nio-3] o.s.c.g.r.RouteDefinitionRouteLocator : RouteDefinition matched: ReactiveCompositeDiscoveryClient_CONFIG 2020-09-02 10:08:06.027 DEBUG 53824 --- [ctor-http-nio-3] o.s.c.g.r.RouteDefinitionRouteLocator : RouteDefinition ReactiveCompositeDiscoveryClient_MY-SERVICE applying {pattern=/MY-SERVICE/**} to Path 2020-09-02 10:08:06.027 DEBUG 53824 --- [ctor-http-nio-3] o.s.c.g.r.RouteDefinitionRouteLocator : RouteDefinition ReactiveCompositeDiscoveryClient_MY-SERVICE applying filter {regexp=/MY-SERVICE/(?<remaining>.*), replacement=/${remaining}} to RewritePath 2020-09-02 10:08:06.028 DEBUG 53824 --- [ctor-http-nio-3] o.s.c.g.r.RouteDefinitionRouteLocator : RouteDefinition matched: ReactiveCompositeDiscoveryClient_MY-SERVICE 2020-09-02 10:08:06.028 DEBUG 53824 --- [ctor-http-nio-3] o.s.c.g.r.RouteDefinitionRouteLocator : RouteDefinition ReactiveCompositeDiscoveryClient_GATEWAY applying {pattern=/GATEWAY/**} to Path 2020-09-02 10:08:06.029 DEBUG 53824 --- [ctor-http-nio-3] o.s.c.g.r.RouteDefinitionRouteLocator : RouteDefinition ReactiveCompositeDiscoveryClient_GATEWAY applying filter {regexp=/GATEWAY/(?<remaining>.*), replacement=/${remaining}} to RewritePath 2020-09-02 10:08:06.029 DEBUG 53824 --- [ctor-http-nio-3] o.s.c.g.r.RouteDefinitionRouteLocator : RouteDefinition matched: ReactiveCompositeDiscoveryClient_GATEWAY 2020-09-02 10:08:06.030 DEBUG 53824 --- [ctor-http-nio-3] o.s.c.g.r.RouteDefinitionRouteLocator : RouteDefinition test applying {_genkey_0=/myservice/**} to Path 2020-09-02 10:08:06.030 DEBUG 53824 --- [ctor-http-nio-3] o.s.c.g.r.RouteDefinitionRouteLocator : RouteDefinition matched: test 2020-09-02 10:08:06.074 ERROR 53824 --- [ctor-http-nio-3] a.w.r.e.AbstractErrorWebExceptionHandler : [7ecb6c86-3] 500 Server Error for HTTP GET "/myservice/demo/get" java.lang.IllegalStateException: block()/blockFirst()/blockLast() are blocking, which is not supported in thread reactor-http-nio-3 at reactor.core.publisher.BlockingSingleSubscriber.blockingGet(BlockingSingleSubscriber.java:83) ~[reactor-core-3.3.5.RELEASE.jar:3.3.5.RELEASE] Suppressed: reactor.core.publisher.FluxOnAssembly$OnAssemblyException: Error has been observed at the following site(s): |_ checkpoint ⇢ org.springframework.cloud.gateway.filter.WeightCalculatorWebFilter [DefaultWebFilterChain] |_ checkpoint ⇢ org.springframework.boot.actuate.metrics.web.reactive.server.MetricsWebFilter [DefaultWebFilterChain] |_ checkpoint ⇢ HTTP GET "/myservice/demo/get" [ExceptionHandlingWebHandler] Stack trace: at reactor.core.publisher.BlockingSingleSubscriber.blockingGet(BlockingSingleSubscriber.java:83) ~[reactor-core-3.3.5.RELEASE.jar:3.3.5.RELEASE] at reactor.core.publisher.Mono.block(Mono.java:1678) ~[reactor-core-3.3.5.RELEASE.jar:3.3.5.RELEASE] at org.springframework.cloud.netflix.eureka.http.WebClientEurekaHttpClient.getApplicationsInternal(WebClientEurekaHttpClient.java:139) ~[spring-cloud-netflix-eureka-client-2.2.3.RELEASE.jar:2.2.3.RELEASE] at org.springframework.cloud.netflix.eureka.http.WebClientEurekaHttpClient.getApplications(WebClientEurekaHttpClient.java:124) ~[spring-cloud-netflix-eureka-client-2.2.3.RELEASE.jar:2.2.3.RELEASE] at org.springframework.cloud.netflix.eureka.config.EurekaConfigServerBootstrapConfiguration.lambda$eurekaConfigServerInstanceProvider$0(EurekaConfigServerBootstrapConfiguration.java:112) ~[spring-cloud-netflix-eureka-client-2.2.3.RELEASE.jar:2.2.3.RELEASE] at org.springframework.cloud.config.client.ConfigServerInstanceProvider.getConfigServerInstances(ConfigServerInstanceProvider.java:50) ~[spring-cloud-config-client-2.2.3.RELEASE.jar:2.2.3.RELEASE] at org.springframework.cloud.config.client.DiscoveryClientConfigServiceBootstrapConfiguration$HeartbeatListener.refresh(DiscoveryClientConfigServiceBootstrapConfiguration.java:120) ~[spring-cloud-config-client-2.2.3.RELEASE.jar:2.2.3.RELEASE] at org.springframework.cloud.config.client.DiscoveryClientConfigServiceBootstrapConfiguration$HeartbeatListener.startup(DiscoveryClientConfigServiceBootstrapConfiguration.java:106) ~[spring-cloud-config-client-2.2.3.RELEASE.jar:2.2.3.RELEASE] at org.springframework.cloud.config.client.DiscoveryClientConfigServiceBootstrapConfiguration$HeartbeatListener.onApplicationEvent(DiscoveryClientConfigServiceBootstrapConfiguration.java:98) ~[spring-cloud-config-client-2.2.3.RELEASE.jar:2.2.3.RELEASE] at org.springframework.context.event.SimpleApplicationEventMulticaster.doInvokeListener(SimpleApplicationEventMulticaster.java:172) ~[spring-context-5.2.6.RELEASE.jar:5.2.6.RELEASE] at org.springframework.context.event.SimpleApplicationEventMulticaster.invokeListener(SimpleApplicationEventMulticaster.java:165) ~[spring-context-5.2.6.RELEASE.jar:5.2.6.RELEASE] at org.springframework.context.event.SimpleApplicationEventMulticaster.multicastEvent(SimpleApplicationEventMulticaster.java:139) ~[spring-context-5.2.6.RELEASE.jar:5.2.6.RELEASE] at org.springframework.context.support.AbstractApplicationContext.publishEvent(AbstractApplicationContext.java:403) ~[spring-context-5.2.6.RELEASE.jar:5.2.6.RELEASE] at org.springframework.context.support.AbstractApplicationContext.publishEvent(AbstractApplicationContext.java:409) ~[spring-context-5.2.6.RELEASE.jar:5.2.6.RELEASE] at org.springframework.context.support.AbstractApplicationContext.publishEvent(AbstractApplicationContext.java:409) ~[spring-context-5.2.6.RELEASE.jar:5.2.6.RELEASE] at org.springframework.context.support.AbstractApplicationContext.publishEvent(AbstractApplicationContext.java:360) ~[spring-context-5.2.6.RELEASE.jar:5.2.6.RELEASE] at org.springframework.context.support.AbstractApplicationContext.finishRefresh(AbstractApplicationContext.java:897) ~[spring-context-5.2.6.RELEASE.jar:5.2.6.RELEASE] at org.springframework.context.support.AbstractApplicationContext.refresh(AbstractApplicationContext.java:553) ~[spring-context-5.2.6.RELEASE.jar:5.2.6.RELEASE] at org.springframework.cloud.context.named.NamedContextFactory.createContext(NamedContextFactory.java:136) ~[spring-cloud-context-2.2.3.RELEASE.jar:2.2.3.RELEASE] at org.springframework.cloud.context.named.NamedContextFactory.getContext(NamedContextFactory.java:101) ~[spring-cloud-context-2.2.3.RELEASE.jar:2.2.3.RELEASE] at org.springframework.cloud.netflix.ribbon.SpringClientFactory.getContext(SpringClientFactory.java:131) ~[spring-cloud-netflix-ribbon-2.2.3.RELEASE.jar:2.2.3.RELEASE] at org.springframework.cloud.context.named.NamedContextFactory.getInstance(NamedContextFactory.java:145) ~[spring-cloud-context-2.2.3.RELEASE.jar:2.2.3.RELEASE] at org.springframework.cloud.netflix.ribbon.SpringClientFactory.getInstance(SpringClientFactory.java:121) ~[spring-cloud-netflix-ribbon-2.2.3.RELEASE.jar:2.2.3.RELEASE] at org.springframework.cloud.netflix.ribbon.SpringClientFactory.getLoadBalancer(SpringClientFactory.java:65) ~[spring-cloud-netflix-ribbon-2.2.3.RELEASE.jar:2.2.3.RELEASE] at org.springframework.cloud.netflix.ribbon.RibbonLoadBalancerClient.getLoadBalancer(RibbonLoadBalancerClient.java:193) ~[spring-cloud-netflix-ribbon-2.2.3.RELEASE.jar:2.2.3.RELEASE] at org.springframework.cloud.netflix.ribbon.RibbonLoadBalancerClient.choose(RibbonLoadBalancerClient.java:88) ~[spring-cloud-netflix-ribbon-2.2.3.RELEASE.jar:2.2.3.RELEASE] at org.springframework.cloud.netflix.ribbon.RibbonLoadBalancerClient.choose(RibbonLoadBalancerClient.java:78) ~[spring-cloud-netflix-ribbon-2.2.3.RELEASE.jar:2.2.3.RELEASE] at org.springframework.cloud.gateway.filter.LoadBalancerClientFilter.choose(LoadBalancerClientFilter.java:111) ~[spring-cloud-gateway-core-2.2.3.RELEASE.jar:2.2.3.RELEASE] at org.springframework.cloud.gateway.filter.LoadBalancerClientFilter.filter(LoadBalancerClientFilter.java:83) ~[spring-cloud-gateway-core-2.2.3.RELEASE.jar:2.2.3.RELEASE] at org.springframework.cloud.gateway.handler.FilteringWebHandler$GatewayFilterAdapter.filter(FilteringWebHandler.java:138) ~[spring-cloud-gateway-core-2.2.3.RELEASE.jar:2.2.3.RELEASE] at org.springframework.cloud.gateway.filter.OrderedGatewayFilter.filter(OrderedGatewayFilter.java:44) ~[spring-cloud-gateway-core-2.2.3.RELEASE.jar:2.2.3.RELEASE] at org.springframework.cloud.gateway.handler.FilteringWebHandler$DefaultGatewayFilterChain.lambda$filter$0(FilteringWebHandler.java:118) ~[spring-cloud-gateway-core-2.2.3.RELEASE.jar:2.2.3.RELEASE] at reactor.core.publisher.MonoDefer.subscribe(MonoDefer.java:44) [reactor-core-3.3.5.RELEASE.jar:3.3.5.RELEASE] ``` 从错误看的话,gateway底层使用了netty和reactor响应式编程模式, ``` block()/blockFirst()/blockLast() are blocking, which is not supported in thread reactor-http-nio-3 ``` 最后程序报错在这: ``` at reactor.core.publisher.BlockingSingleSubscriber.blockingGet ``` 线程阻塞,它是不被reactor支持的,但这只是一个直接原因,具体的造成这个原因的还不知道。 接下来贴下yml gateway的bootstrap.yml: ``` spring: application: name: gateway profiles: active: native cloud: config: fail-fast: true name: ${spring.application.name} #profile: ${spring.profiles.active} discovery: enabled: true service-id: config eureka: instance: prefer-ip-address: true client: service-url: defaultZone: http://eureka1:8080/eureka/ ``` config中心的gateway.yml: ``` server: port: 8082 Spring: cloud: gateway: discovery: locator: enabled: true routes: - id: test uri: lb://my-service predicates: - Path=/myservice/** #暴露/actuator/bus-refresh接口 management: endpoints: web: exposure: include: "*" hystrix: command: default: execution: isolation: strategy: THREAD thread: timeoutInMilliseconds: 3000 interruptOnTimeout: true ribbon: ReadTimeout: 3000 ConnectTimeout: 3000 logging: level: org.springframework.cloud.gateway: debug ``` 再贴一个my-service的bootstrap: ``` server: port: 8088 spring: application: name: my-service profiles: active: # 配置中心 cloud: config: fail-fast: true name: ${spring.application.name} #profile: ${spring.profiles.active} discovery: enabled: true service-id: config # 注册中心配置 eureka: instance: prefer-ip-address: false client: service-url: defaultZone: http://eureka1:8080/eureka/ #registry-fetch-interval-seconds: #instance-info-replication-interval-seconds: #logging: #level: #io.swagger.models.parameters.AbstractSerializableParameter: error ``` 浏览器访问 ``` http://192.168.1.52:8082/myservice/demo/get ``` 页面错误: ``` Whitelabel Error Page This application has no configured error view, so you are seeing this as a fallback. Wed Sep 02 10:08:06 CST 2020 [7ecb6c86-3] There was an unexpected error (type=Internal Server Error, status=500). ``` 直接访问my-service: ``` http://192.168.1.52:8088/myservice/demo/get ``` 没问题,可以正常访问
©️2020 CSDN 皮肤主题: 像素格子 设计师:CSDN官方博客 返回首页