目录

核心特性

服务发现

负载平衡

连接池

Status 类别

重试

Retry on errors

Retry on status codes

Request Passport

Request Attemps

Origin并发保护

HTTP/2

Mutual TLS

代理协议


核心特性

服务发现

Zuul可与Eureka无缝协作,但也可配置为与静态服务器列表或我们选择的发现服务协作。

使用Eureka服务器的标准方法如下所示:

#使用Eureka的负载平衡后端
eureka.shouldUseDns=true
eureka.eurekaServer.context=discovery/v2
eureka.eurekaServer.domainName=discovery${environment}.netflix.net
eureka.eurekaServer.gzipContent=trueeureka.serviceUrl.default=http://${region}.${eureka.eurekaServer.domainName}:7001/${eureka.eurekaServer.context}api.ribbon.NIWSServerListClassName=com.netflix.niws.loadbalancer.DiscoveryEnabledNIWSServerList
api.ribbon.DeploymentContextBasedVipAddresses=api-test.netflix.net:7001

在此配置中,我们必须指定我们的Eureka上下文和位置。鉴于此,Zuul将自动从Eureka中选择服务器列表,并为api Ribbon客户端指定VIP。

要使用静态服务器列表或其他发现提供程序配置Zuul,我们必须使listOfServers属性保持最新:

#没有Eureka的负载平衡后端eureka.shouldFetchRegistry=falseapi.ribbon.listOfServers=100.66.23.88:7001,100.65.155.22:7001
api.ribbon.client.NIWSServerListClassName=com.netflix.loadbalancer.ConfigurationBasedServerList
api.ribbon.DeploymentContextBasedVipAddresses=api-test.netflix.net:7001

请注意,在此配置中,服务器列表类名为ConfigurationBasedServerList,而不是DiscoveryEnabledNIWSServerList。

负载平衡

默认情况下,Zuul使用Ribbon中的ZoneAwareLoadBalancer进行负载平衡。该算法是discovery中可用实例的循环,可用性区域成功跟踪可恢复性。负载平衡器将保留每个区域的统计信息,如果故障率高于可配置的阈值,则会删除一个区域。

如果要使用自己的自定义负载平衡器,可以为该Ribbob客户端命名空间设置NFLoadBalancerClassName属性,或覆盖DefaultClientChannelManager中的getLoadBalancerClass()方法。注意,我们的类应该扩展DynamicServerListLoadBalancer。

Ribbon还允许我们配置负载平衡规则。例如,我们可以将RoundRobinRule替换为WeightedResponseTimeRule、AvailabilityFilteringRule或我们自己的规则。

连接池

Zuul不使用Ribbon进行传出连接,而是使用Netty客户端使用自己的连接池。Zuul为每个主机、每个事件循环创建一个连接池。它这样做是为了减少线程之间的上下文切换,并确保入站事件循环和出站事件循环的完整性。结果是整个请求都在同一线程上运行,而不管运行它的是哪个事件循环。

此策略的一个副作用是,如果有很多Zuul实例在运行,并且每个实例中都有很多事件循环,那么到每个后端服务器的最小连接量可能会非常高。配置连接池时,请务必记住这一点。

用于调整连接池的一些有用设置及其默认值包括:

Ribbon Client Config Properties

<originName>.ribbon.ConnectTimeout                   // default: 500 (ms)
<originName>.ribbon.ReadTimeout                      // default: 90000 (ms)
<originName>.ribbon.MaxConnectionsPerHost            // default: 50
<originName>.ribbon.ConnIdleEvictTimeMilliSeconds    // default: 60000 (ms)
<originName>.ribbon.ReceiveBufferSize                // default: 32 * 1024
<originName>.ribbon.SendBufferSize                   // default: 32 * 1024
<originName>.ribbon.UseIPAddrForServer               // default: true

Zuul Properties

# 强制关闭之前,任何给定连接的最大请求量
<originName>.netty.client.maxRequestsPerConnection    // default: 1000# 每个服务器、每个事件循环的最大连接量
<originName>.netty.client.perServerWaterline          // default: 4# Netty 配置连接
<originName>.netty.client.TcpKeepAlive                // default: false
<originName>.netty.client.TcpNoDelay                  // default: false
<originName>.netty.client.WriteBufferHighWaterMark    // default: 32 * 1024
<originName>.netty.client.WriteBufferLowWaterMark     // default: 8 * 1024
<originName>.netty.client.AutoRead                    // default: false

Status 类别

尽管HTTP状态是通用的,但它们并没有提供很多粒度。为了获得更具体的故障模式,zuul创建了一个可能故障的枚举。

状态类别 定义
SUCCESS 请求成功
SUCCESS_NOT_FOUND

已成功代理,但状态为404

SUCCESS_LOCAL_NOTSET

请求成功,但未设置StatusCategory

SUCCESS_LOCAL_NO_ROUTE

技术上成功,但未找到请求的路由

FAILURE_LOCAL

本地Zuul故障(例如引发异常)

FAILURE_LOCAL_THROTTLED_ORIGIN_SERVER_MAXCONN

由于达到到源服务器的最大连接限制,请求被阻止

FAILURE_LOCAL_THROTTLED_ORIGIN_CONCURRENCY

由于origin并发限制,请求被阻止

FAILURE_LOCAL_IDLE_TIMEOUT

由于空闲连接超时,请求失败

FAILURE_CLIENT_CANCELLED

请求失败,因为客户端已取消

FAILURE_CLIENT_PIPELINE_REJECT

请求失败,因为客户端试图发送pipelined HTTP请求

FAILURE_CLIENT_TIMEOUT

由于客户端读取超时,请求失败(例如,邮件正文被截断)

FAILURE_ORIGIN

origin返回故障(即500状态)

FAILURE_ORIGIN_READ_TIMEOUT

对origin的请求超时

FAILURE_ORIGIN_CONNECTIVITY

无法连接到origin

FAILURE_ORIGIN_THROTTLED

Origin阻止了请求(即503状态)

FAILURE_ORIGIN_NO_SERVERS

找不到origin服务器的任何要连接的服务器

FAILURE_ORIGIN_RESET_CONNECTION

Origin在请求完成之前重置连接

我们可以使用StatusCategoryUtils类获取或设置状态。例如:

// set
StatusCategoryUtils.setStatusCategory(httpRequestMessage.getContext(),ZuulStatusCategory.SUCCESS);//get
StatusCategoryUtils.getStatusCategory(context);

重试

Netflix用于恢复的关键功能之一是重试。在Zuul中对待重试并广泛使用它们。我们使用以下逻辑来确定何时重试请求:

Retry on errors

  • 如果错误为读取超时,请重置连接或连接错误

Retry on status codes

  • 如果状态代码为503

  • 如果状态代码是可配置的幂等状态(请参见下文),并且方法是:GET、HEAD或OPTIONS之一

如果处于瞬态,我们不会重试,更具体地说:

  • 如果我们已经开始将响应发送回客户端
  • 如果我们丢失了任何实体块(部分缓冲或截断的实体)

相关属性:

# 设置错误代码和状态代码重试的重试限制
<originName>.ribbon.MaxAutoRetriesNextServer            // default: 0# 这是以逗号分隔的状态代码列表
zuul.retry.allowed.statuses.idempotent                  // default: 500

Request Passport

用于调试的最佳工具之一是请求passport。它是一个按时间顺序排列的集合,包含请求所经过的所有状态,并带有以纳秒为单位的相关时间戳。

成功请求的例子

这是一个简单的请求,它运行一些过滤器,执行一些IO,代理请求,对响应运行过滤器,然后将其写入客户端。

CurrentPassport {start_ms=1523578203359,
[+0=IN_REQ_HEADERS_RECEIVED,
+260335=FILTERS_INBOUND_START,
+310862=IN_REQ_LAST_CONTENT_RECEIVED,
+1053435=MISC_IO_START,
+2202112=MISC_IO_STOP,
+3917598=FILTERS_INBOUND_END,
+4157288=ORIGIN_CH_CONNECTING,
+4218319=ORIGIN_CONN_ACQUIRE_START,
+4443588=ORIGIN_CH_CONNECTED,
+4510115=ORIGIN_CONN_ACQUIRE_END,
+4765495=OUT_REQ_HEADERS_SENDING,
+4799545=OUT_REQ_LAST_CONTENT_SENDING,
+4820669=OUT_REQ_HEADERS_SENT,
+4822465=OUT_REQ_LAST_CONTENT_SENT,
+4830443=ORIGIN_CH_ACTIVE,
+20811792=IN_RESP_HEADERS_RECEIVED,
+20961148=FILTERS_OUTBOUND_START,
+21080107=IN_RESP_LAST_CONTENT_RECEIVED,
+21109342=ORIGIN_CH_POOL_RETURNED,
+21539032=FILTERS_OUTBOUND_END,
+21558317=OUT_RESP_HEADERS_SENDING,
+21575084=OUT_RESP_LAST_CONTENT_SENDING,
+21594236=OUT_RESP_HEADERS_SENT,
+21595122=OUT_RESP_LAST_CONTENT_SENT,
+21659271=NOW]}

超时的例子

这是超时的一个示例。这与前面的示例类似,但与outbound请求和超时事件之间的时间间隔不同。

CurrentPassport {start_ms=1523578490446,
[+0=IN_REQ_HEADERS_RECEIVED,
+139712=FILTERS_INBOUND_START,
+1364667=MISC_IO_START,
+2235393=MISC_IO_STOP,
+3686560=FILTERS_INBOUND_END,
+3823010=ORIGIN_CH_CONNECTING,
+3891023=ORIGIN_CONN_ACQUIRE_START,
+4242502=ORIGIN_CH_CONNECTED,
+4311756=ORIGIN_CONN_ACQUIRE_END,
+4401724=OUT_REQ_HEADERS_SENDING,
+4453035=OUT_REQ_HEADERS_SENT,
+4461546=ORIGIN_CH_ACTIVE,
+45004599181=ORIGIN_CH_READ_TIMEOUT,
+45004813647=FILTERS_OUTBOUND_START,
+45004920343=ORIGIN_CH_CLOSE,
+45004945985=ORIGIN_CH_CLOSE,
+45005052026=ORIGIN_CH_INACTIVE,
+45005246081=FILTERS_OUTBOUND_END,
+45005359480=OUT_RESP_HEADERS_SENDING,
+45005379978=OUT_RESP_LAST_CONTENT_SENDING,
+45005399999=OUT_RESP_HEADERS_SENT,
+45005401335=OUT_RESP_LAST_CONTENT_SENT,
+45005486729=NOW]}

失败请求的例子

这是导致异常的请求的示例。同样,它与前面的类似,但请注意重试和异常事件。

CurrentPassport {start_ms=1523578533258,
[+0=IN_REQ_HEADERS_RECEIVED,
+161428=FILTERS_INBOUND_START,
+208805=IN_REQ_LAST_CONTENT_RECEIVED,
+934637=MISC_IO_START,
+1751747=MISC_IO_STOP,
+2606657=FILTERS_INBOUND_END,
+2734497=ORIGIN_CH_CONNECTING,
+2780877=ORIGIN_CONN_ACQUIRE_START,
+3181771=ORIGIN_CH_CONNECTED,
+3272876=ORIGIN_CONN_ACQUIRE_END,
+3376958=OUT_REQ_HEADERS_SENDING,
+3405924=OUT_REQ_LAST_CONTENT_SENDING,
+3557967=ORIGIN_RETRY_START,
+3590208=ORIGIN_CH_CONNECTING,
+3633635=ORIGIN_CONN_ACQUIRE_START,
+3663060=ORIGIN_CH_CLOSE,
+3664703=OUT_REQ_HEADERS_ERROR_SENDING,
+3674443=OUT_REQ_LAST_CONTENT_ERROR_SENDING,
+3681289=ORIGIN_CH_ACTIVE,
+3706176=ORIGIN_CH_INACTIVE,
+4022445=ORIGIN_CH_CONNECTED,
+4072050=ORIGIN_CONN_ACQUIRE_END,
+4144471=OUT_REQ_HEADERS_SENDING,
+4171228=OUT_REQ_LAST_CONTENT_SENDING,
+4186672=OUT_REQ_HEADERS_SENT,
+4187543=OUT_REQ_LAST_CONTENT_SENT,
+4192830=ORIGIN_CH_ACTIVE,
+4273401=ORIGIN_CH_EXCEPTION,
+4274124=ORIGIN_CH_EXCEPTION,
+4303020=ORIGIN_CH_IO_EX,
+4537569=FILTERS_OUTBOUND_START,
+4646348=ORIGIN_CH_CLOSE,
+4748074=ORIGIN_CH_INACTIVE,
+4957163=FILTERS_OUTBOUND_END,
+4968947=OUT_RESP_HEADERS_SENDING,
+4985532=OUT_RESP_LAST_CONTENT_SENDING,
+5003476=OUT_RESP_HEADERS_SENT,
+5004610=OUT_RESP_LAST_CONTENT_SENT,
+5062221=NOW]}

我们可以登录passport,将其添加到标头中,或将其发送到持久性存储以供以后调试。要将其从请求中删除,可以使用通道或会话上下文。例如:

// from channel
CurrentPassport passport = CurrentPassport.fromChannel(channel);// from context
CurrentPassport passport = CurrentPassport.fromSessionContext(context);

Request Attemps

另一个非常有用的调试特性是跟踪Zuul发出的请求尝试。我们通常在每个响应上都将其作为一个仅限内部的头添加,这使得跟踪和调试请求对于我们和我们的内部合作伙伴来说更加简单。

成功请求的例子

[{"status":200,"duration":192,"attempt":1,"region":"us-east-1","asg":"simulator-v154","instanceId":"i-061db2c67b2b3820c","vip":"simulator.netflix.net:7001"}]

失败请求的例子

[{"status":503,"duration":142,"attempt":1,"error":"ORIGIN_SERVICE_UNAVAILABLE","exceptionType":"OutboundException","region":"us-east-1","asg":"simulator-v154","instanceId":"i-061db2c67b2b3820c","vip":"simulator.netflix.net:7001"},
{"status":503,"duration":147,"attempt":2,"error":"ORIGIN_SERVICE_UNAVAILABLE","exceptionType":"OutboundException","region":"us-east-1","asg":"simulator-v154","instanceId":"i-061db2c67b2b3820c","vip":"simulator.netflix.net:7001"}]

我们可以从outbound筛选器上的会话上下文获取请求尝试。例如:

// from context
RequestAttempts attempts = RequestAttempts.getFromSessionContext(context);

Origin并发保护

有时,origin可能会遇到麻烦,特别是当请求量超过其容量时。考虑到我们是一个代理,一个坏的origin可能会通过饱和我们的连接和内存而影响其他origin。为了保护origins和Zuul,我们设置了并发限制,以帮助平滑服务中断。

我们有两种管理origin并发的方法:

Overall Origin Concurrency(全部origin并发)

zuul.origin.<originName>.concurrency.max.requests        // default: 200
zuul.origin.<originName>.concurrency.protect.enabled     // default: true

Per Server Concurrency(每个服务器并发)

<originName>.ribbon.MaxConnectionsPerHost                // default: 50

如果一个origin超过了总体并发或每主机并发,Zuul将向客户端返回503。

HTTP/2

Zuul可以在HTTP/2模式下运行,在这种模式下,它需要SSL证书,如果要在ELB后面运行Zuul,则必须使用TCP侦听器。

case HTTP2:sslConfig = ServerSslConfig.withDefaultCiphers(loadFromResources("server.cert"),loadFromResources("server.key"),WWW_PROTOCOLS);channelConfig.set(CommonChannelConfigKeys.allowProxyHeadersWhen, StripUntrustedProxyHeadersHandler.AllowWhen.NEVER);channelConfig.set(CommonChannelConfigKeys.preferProxyProtocolForClientIp, true);channelConfig.set(CommonChannelConfigKeys.isSSlFromIntermediary, false);channelConfig.set(CommonChannelConfigKeys.serverSslConfig, sslConfig);channelConfig.set(CommonChannelConfigKeys.sslContextFactory, new BaseSslContextFactory(registry, sslConfig));addHttp2DefaultConfig(channelConfig, mainListenAddressName);addrsToChannels.put(new NamedSocketAddress("http2", sockAddr),new Http2SslChannelInitializer(metricId, channelConfig, channelDependencies, clientChannels));logAddrConfigured(sockAddr, sslConfig);break;

相关HTTP/2属性:

server.http2.max.concurrent.streams            // default: 100
server.http2.initialwindowsize                 // default: 5242880
server.http2.maxheadertablesize                // default: 65536
server.http2.maxheaderlistsize                 // default: 32768

Mutual TLS

Zuul可以在Mutual TLS模式下运行。在这种模式下,我们必须同时拥有SSL证书和传入证书的信任存储。与HTTP/2一样,我们必须在ELB的TCP侦听器后面运行它。

case HTTP_MUTUAL_TLS:sslConfig = new ServerSslConfig(WWW_PROTOCOLS,ServerSslConfig.getDefaultCiphers(),loadFromResources("server.cert"),loadFromResources("server.key"),ClientAuth.REQUIRE,loadFromResources("truststore.jks"),loadFromResources("truststore.key"),false);channelConfig.set(CommonChannelConfigKeys.allowProxyHeadersWhen, StripUntrustedProxyHeadersHandler.AllowWhen.NEVER);channelConfig.set(CommonChannelConfigKeys.preferProxyProtocolForClientIp, true);channelConfig.set(CommonChannelConfigKeys.isSSlFromIntermediary, false);channelConfig.set(CommonChannelConfigKeys.withProxyProtocol, true);channelConfig.set(CommonChannelConfigKeys.serverSslConfig, sslConfig);channelConfig.set(CommonChannelConfigKeys.sslContextFactory, new BaseSslContextFactory(registry, sslConfig));addrsToChannels.put(new NamedSocketAddress("http_mtls", sockAddr),new Http1MutualSslChannelInitializer(metricId, channelConfig, channelDependencies, clientChannels));logAddrConfigured(sockAddr, sslConfig);break;

代理协议

使用TCP侦听器时,Proxy protocol(代理协议)是一项重要功能,可以使用以下服务器配置属性在Zuul中启用:

// 剥离XFF头,因为我们不再信任它们
channelConfig.set(CommonChannelConfigKeys.allowProxyHeadersWhen, StripUntrustedProxyHeadersHandler.AllowWhen.NEVER);// 首选代理协议(如果可用)
channelConfig.set(CommonChannelConfigKeys.preferProxyProtocolForClientIp, true);// 启用代理协议
channelConfig.set(CommonChannelConfigKeys.withProxyProtocol, true);

客户端IP将在筛选器中的HttpRequestMessage上正确设置,也可以直接从通道中检索:

String clientIp = channel.attr(SourceAddressChannelHandler.ATTR_SOURCE_ADDRESS).get();

Spring Cloud Zuul(2)相关推荐

  1. API 网关服务:Spring Cloud Zuul(二):路由详解、Cookie 与头信息

    实践出于<Spring Cloud 微服务实战> - 瞿永超 著 路由详解 传统路由配置   传统路由配置就是在不依赖于服务发现机制的情况下,通过在配置文件中具体指定每个路由表达式与服务实 ...

  2. 破甲两千六 Spring Cloud 教程(三):添加Spring Cloud 的 Netflix Eureka 插件,实现服务端、客户端的发现与注册

    写在前面: Spring Cloud 为开发人员提供了快速构建分布式系统的一些工具,包括配置管理.服务发现.断路器.路由.微代理.事件总线.全局锁.决策竞选.分布式会话等等. 5大常用组件: 服务发现 ...

  3. Spring Cloud Netflix(一)

    Spring Cloud Netflix(一) 介绍 Spring Cloud Netflix 是由 Netflix 开源的,并且由 Spring 项目集成到 Spring Cloud 中的,主要用于 ...

  4. Spring Cloud Gateway (七)处理流程解析

    Spring Cloud Gateway (七)处理流程解析 简介     初步梳理 Spring Cloud Gateway 的处理流程 过程记录 主要请求流程     在前面的分析中,我们知道在 ...

  5. Spring cloud Gateway(二) 一个Http请求的流程解析

    Spring cloud Gateway(二) 一个Http请求的流程解析 简介     通过一个简单示例,debug出Spring Cloud Gateway的一个HTTP请求的处理流程 思路整理 ...

  6. Spring cloud alibaba(二)nacos服务发现docker相同ip冲突

    引入服务发现其实比较简单 项目架构:同Spring cloud alibaba(一)多模块项目整合 spring cloud - pay - smdd - coupon - base - order- ...

  7. 微服务框架-Spring Cloud简介(一)

    Spring Cloud是一个微服务框架,相比Dubbo等RPC框架, Spring Cloud提供的全套的分布式系统解决方案. Spring Cloud对微服务基础框架Netflix的多个开源组件进 ...

  8. Spring Cloud系列(一)入门文章

    一篇文章入门 微服务是什么? Spring Cloud 子模块简介 Spring Cloud Netflix Spring Cloud Config Spring Cloud Bus Spring C ...

  9. Spring Cloud Gateway(过滤器)

    在上一篇文章中,我们了解了 Spring Cloud Gateway 作为网关所具备的基础功能:路由.本篇我们将关注它的另一个功能:过滤器. Spring Cloud Gateway 已经内置了很多实 ...

最新文章

  1. 教你如何在 AlertManager 报警通知中展示监控图表
  2. 避免HttpClient的”javax.net.ssl.SSLPeerUnverifiedException: peer not authenticated”异常
  3. stm32官方例程在哪找_STM32开发学习资料合集
  4. 通过分析 JDK 源代码研究 TreeMap 红黑树算法实现--转
  5. 常量元素记忆口诀_人体中的常量元素与微量元素
  6. 2016第51周三产品经理如何更有说服力
  7. qoq是什么意思的缩写_“yjgj他的pyq很zqsg”,90后从未觉得自己老,直到看到00后缩写的那一秒……...
  8. java 一次CPU占用过高问题的排查及解决,java基础面试笔试题
  9. 超级硬盘数据恢复软件v2.7.2.6_电脑磁盘上的视频误删如何恢复?误删视频恢复教程...
  10. bzoj 1237: [SCOI2008]配对(DP)
  11. python网络爬虫系列教程——PhantomJS包应用全解
  12. vue导出Excel组件
  13. 计算机网络局域网的组建实验报告,小型局域网组建实验报告
  14. 【Servlet:Java Web服务器】JSP 基本知识点与总结 (思维导图)
  15. ORACLE通过身份证号计算年龄
  16. 从零开始构建 RPM 包
  17. JSF集成Spring
  18. net-java-php-python-篮球新闻网站计算机毕业设计程序
  19. cocos2d-x 使用Box2d制作的台球游戏
  20. 零基础学会3DsMax超炫酷战斗机飞行动画

热门文章

  1. 给WordPress添加SSL认证(HTTPS)
  2. 数字孪生、动漫IP、虚拟偶像来袭!海心沙元宇宙音乐会这些亮点不容错过
  3. Oracle PL/SQL——日期时间函数的运用
  4. 敏捷,路在何方?最新完整敏捷状态报告(专业点评版)出炉
  5. 不止于美,华阳国际揽获深圳市优秀工程勘察设计奖32项殊荣
  6. 超详细的Git使用教程(图文)
  7. Python 计算两个序列之间差异的显著性水平检验
  8. 北京时间转化为utc时间格式
  9. Fidder下抓取https
  10. 转载的一道小米面试题和自己的写法