相关地址
网关官方参考地址:https://projects.spring.io/spring-cloud/spring-cloud.html#_router_and_filter_zuul
参考地址:
https://blog.csdn.net/w1014074794/article/details/88571880
https://www.jianshu.com/p/ebd62bac2ed4
https://www.jianshu.com/p/e9d353294e8c
https://github.com/Netflix/Hystrix/wiki/Configuration
网关架构图
几大厂商网关对比表格
.... | 京东 | 唯品会 | 有赞 | 阿里 | zuul |
实现关键 | servlet3.0 | servlet3.0 | servlet3.0 | servlet3.0 | servlet3.0 |
异步情况 | servlet异步 rpc是否异步不清楚 | 全链路异步 | 全链路异步 | 全链路异步 | zuul同步阻塞 zuul异步非阻塞 |
限流 | ... | ... | 平滑限流 最初是cods 后续换到每个单机的令牌桶限流 | 1.基本流控:基于API的QPS做限流。2.运营流控:支持APP流量包,APP+API+USER的流控3.大促流控:APP访问API的权重流控。阿里开源 Sentinel | 提供了jar包:spring-cloud-zuul-rateLimit。1.对请求的目标URL进行限流(例如:某个URL 每分钟只允许调用多少次)。2.对客户端的访问IP进行限流(例如某个IP分钟只允许请求多少次)。 3.对某些特定用户或者用户组进行限流 4.多维度混合的限流。此时,就需要实现一些限流规则的编排机制 与、或、非等关系 |
熔断降级 | ... | ... | Hystrix | ... | 只支持服务级别熔断,不支持URL级别 |
隔离 | 线程池隔离 | ... | 信号量隔离 | ... | 线程池隔离和线程隔离 |
缓存 | redis | ... | 二级缓存 本地缓存 +Cods | HDCC本地缓存,远程缓存,数据库 | 需要自己开发 |
泛化调用 | ... | https,http,http1,http2,二进制 | dubbo,http,nova | redis,dubbo,http,http1,http2 | 只支持http |
相关概念说明
1.泛化调用
泛化调用指的是一些通信协议的转换,比如将HTTP转换成Thrift。在一些开源的网关中比如Zuul是没有实现的,因为各个公司的内部服务通信协议都不同。比如在唯品会中支持HTTP1,HTTP2,以及二进制的协议,然后转化成内部的协议,淘宝的支持HTTPS,HTTP1,HTTP2这些协议都可以转换成,HTTP,HSF,Dubbo等协议。
如何去实现泛化调用呢?由于协议很难自动转换,那么其实每个协议对应的接口需要提供一种映射。简单来说就是把两个协议都能转换成共同语言,从而互相转换。
一般来说共同语言有三种方式指定:json:json数据格式比较简单,解析速度快,较轻量级。在Dubbo的生态中有一个HTTP转Dubbo的项目是用JsonRpc做的,将HTTP转化成JsonRpc再转化成Dubbo。比如可以将一个 www.baidu.com/id = 1 GET 可以映射为json:代码块{“method”: "getBaidu""param" : {"id" : 1}}xml:xml数据比较重,解析比较困难,这里不过多讨论。自定义描述语言:一般来说这个成本比较高需要自己定义语言来进行描述并进行解析,但是其扩展性,自定义个性化性都是最高。例:spring自定义了一套自己的SPEL表达式语言对于泛化调用如果要自己设计的话JSON基本可以满足,如果对于个性化的需要特别多的话倒是可以自己定义一套语言。
2.信号量隔离
信号量隔离只是限制了总的并发数,服务还是主线程进行同步调用。这个隔离如果远程调用超时依然会影响主线程,从而会影响其他业务。因此,如果只是想限制某个服务的总并发调用量或者调用的服务不涉及远程调用的话,可以使用轻量级的信号量来实现。有赞的网关由于没有自定义filter所以选取的是信号量隔离。
3.线程池隔离
最简单的就是不同业务之间通过不同的线程池进行隔离,就算业务接口出现了问题由于线程池已经进行了隔离那么也不会影响其他业务。在京东的网关实现之中就是采用的线程池隔离,比较重要的业务比如商品或者订单 都是单独的通过线程池去处理。但是由于是统一网关平台,如果业务线众多,大家都觉得自己的业务比较重要需要单独的线程池隔离,如果使用的是Java语言开发的话那么,在Java中线程是比较重的资源比较受限,如果需要隔离的线程池过多不是很适用。如果使用一些其他语言比如Golang进行开发网关的话,线程是比较轻的资源,所以比较适合使用线程池隔离。
网关调优
1.hystrix熔断器并发调优
THREAD: 在单独的线程上执行,并发请求受线程池中线程数限制
SEMAPHORE:在调用线程上执行,并发请求受信号量计数限制(只有在高并发(单个实例每秒达到几百个调用)的调用,semaphore隔离策略通常只用于非网络调用)
2.zuul网关的并发参数控制
3.Feign客户端和连接数参数调优
4.Tomcat并发连接数调优
5.timeout超时参数调优
6.jvm参数调优
7.ribbon和hystrix的请求超时,重试以及幂等性配置
无法获取信号量(semaphore异常)
异常信息-1:
截图一
截图二
spring cloud zuul : could not acquire a semaphore for execution and no fallback
available.
无法获取信号量,系统默认每个路由的信号量为100,当后端一个实例且并发大于100就会经常出现
这个异常信息
调优配置-1:
zuul:
semaphore:
max-semaphores: 5000
可根据系统需要支持的并发数适当增加信号量的大小
超时
异常信息-2:
connect time out...
当并发访问时,有些服务所在主机响应可能会比较慢,或者某些业务本身比较耗时
(比如上传一个大文件的接口)。如果在Zuul层设置的超时时间小于足业务的耗时,
会导致正常的业务请求失败。
调优配置-2:
ribbon:
ReadTimeout: 6000 #请求处理超时时间
ConnectTimeout: 6000 #请求连接时间
根据业务可适当调大超时时间
熔断
异常信息-3:
截图1
截图2
short-circuited and no fallback available
并发访问时,后端某些服务发生熔断
调优配置-3:
hystrix:
command:
default:
execution:
isolation:
thread:
timeoutInMilliseconds: 60000 #发生熔断的超时时间
调整熔断超时时间,熔断时间太短,些耗时的业务部不能work
熔断时太长,Zuul服务器可能会被拖垮。所以根据具体业务找到一个合适值。
ribbon:
OkToRetryOnAllOperations:true #全部请求开启重试机制
ReadTimeout: 6000 #请求处理超时时间
ConnectTimeout: 6000 #请求连接超时时间
MaxAutoRetries: 10 #最大重试次数
调整重试次数,实际项目中由于网络或者资源不够,偶尔会出现后端服务不能访问,一次访问失败不能
代表后端服务就挂了。
因此开启重试机制,调整重试次数。在一定时间内,重试几次都失败,我们才认为后端服务挂了。
调式网关限流报错问题分析
com.netflix.zuul.exception.ZuulException: 429 请求资源数达到上限
zuul网关多维度限流,
相关的配置文件如下:
api-a在60s连续访问10次,如果说超过这个刷新窗口,会报如下错误
查看了一下源码:
和limit和quota有关