3.2 网关策略
简介
Wing
的服务网关支持全局和个性化的整个服务或API设置网关策略,目前支持的策略有:熔断
、降级
、超时
、重试
、限流
、舱壁
、鉴权
。
策略配置
Gateway:Policy:Global
全局策略配置,如果没有配置个性化的策略,将按照全局策略执行。Gateway:Policy:Policies[]
个性化服务策略集合,优先于全局策略配置。Gateway:Policy:Policies[0]:MethodPolicies[]
个性化服务方法策略集合,优先于服务策略。
网关策略优先级:服务方法策略>服务策略>全局策略。
降级
什么是服务降级?
服务降级一般是指在服务器压力剧增的时候,根据实际业务使用情况以及流量,对一些服务和页面有策略的不处理或者用一种简单的方式进行处理,从而释放服务器资源以保证核心业务的正常高效运行。通常原因为服务器的资源是有限的,而请求是无限的。在用户使用即并发高峰期,会影响整体服务的性能,严重的话会导致宕机,以至于某些重要服务不可用。故高峰期为了保证核心功能服务的可用性,就需要对某些服务降级处理。可以理解为舍小保大,通常处理为不让客户端等待而是立即返回一个友好的提示。服务降级是从整个系统的负荷情况出发和考虑的,对某些负荷会比较高的情况,为了预防某些功能(业务场景)出现负荷过载或者响应慢的情况,在其内部暂时舍弃对一些非核心的接口和数据的请求,而直接返回一个提前准备好的fallback(兜底处理)错误处理信息。这样,虽然提供的是一个有损的服务,但却保证了整个系统的稳定性和可用性。
降级一般是和其他策略搭配使用,比如熔断降级、超时降级、限流降级、舱壁降级。
Wing服务降级会根据异常情况返回对应的“http状态码”,您可以根据返回的“状态码”做一些友好提示,“状态码”说明如下:
404 服务或请求URL不存在
429 触发限流或舱壁策略,请求太多
502 网关发生未知异常
504 触发超时策略,请求超时
熔断
什么是熔断?
当电路中的负载过高的时候,“保险丝”就会熔断。微服务的熔断就如同保险丝一样,当服务间的调用出现频繁的超时,核心服务却一直在等待这个超时服务的响应结果,后果就是整个系统服务的卡顿、无反应,这对于用户端是不可接受的。所以熔断就是某个服务发生不断的调用响应超时的时候,就屏蔽掉这个服务,短路这个服务,不调用这个服务的具体内容直接返回一个默认值,对服务进行降级处理。
- 策略配置
示例3.2-1
(点击查看完整示例代码)
Breaker:IsEnabled
是否启用熔断策略
Breaker:ExceptionsAllowedBeforeBreaking
熔断前出现允许出现错误次数
Breaker:MillisecondsOfBreak
熔断时长,单位:毫秒
全局策略
{
"Gateway": {
"Policy": {
// 全局策略
"Global": {
//熔断
"Breaker": {
//是否启用
"IsEnabled": true,
//熔断前出现允许错误几次
"ExceptionsAllowedBeforeBreaking": 1,
//熔断多长时间(毫秒)
"MillisecondsOfBreak": 60000
}
}
}
}
}
服务(方法)策略,Restful风格的动态路由参数可以使用两个中括号{}
{
"Gateway": {
"Policy": {
// 个性化策略
"Policies": [
{
// 服务名称
"Key": "Wing.Demo_3.2",
//熔断
"Breaker": {
//是否启用
"IsEnabled": true,
//熔断前出现允许错误几次
"ExceptionsAllowedBeforeBreaking": 1,
//熔断多长时间(毫秒)
"MillisecondsOfBreak": 60000
},
"MethodPolicies": [
{
// 下游地址,{name}是动态路由参数
"Key": "weatherforecast/Breaker2/{name}",
//熔断
"Breaker": {
//是否启用
"IsEnabled": true,
//熔断前出现允许错误几次
"ExceptionsAllowedBeforeBreaking": 2,
//熔断多长时间(毫秒)
"MillisecondsOfBreak": 60000
}
}
]
}
]
}
}
}
- Breaker方法代码,阻塞120秒,触发网关异常
示例3.2
(点击查看完整示例代码)
[HttpGet("Breaker1")]
public string Breaker1()
{
Thread.Sleep(120 * 1000);
return "我是Breaker1";
}
[HttpGet("Breaker2/{name}")]
public string Breaker2(string name)
{
Thread.Sleep(120 * 1000);
return $"我是Breaker2,name:{name}";
}
- 运行结果
分别运行示例3.2
、3.2-1
,浏览器访问
http://localhost:3211/Wing.Demo_3.2/weatherforecast/breaker1 ,
可以看到breaker1被调用1次发生异常之后,被熔断1分钟。浏览器访问
http://localhost:3211/Wing.Demo_3.2/weatherforecast/breaker2/wing ,
可以看到breaker2被调用2次发生异常之后,被熔断1分钟。
超时
什么是超时?
超时很好理解,当一个请求的响应时间超过我们设定的时长,那么,我们就认为它“超时”了。“超时”策略可以跟“熔断”策略组合使用。
- 策略配置
示例3.2-2
(点击查看完整示例代码)
TimeOut:IsEnabled
是否启用超时策略
TimeOut:TimeOutMilliseconds
设定超时时长,单位:毫秒
HttpClientTimeOut
HttpClient请求超时,单位:毫秒
超时全局策略,请求超过5000毫秒,则触发超时降级处理,个性化服务策略设定参考“熔断”配置
{
"Gateway": {
"Policy": {
// 全局策略
"Global": {
// HttpClient请求超时,单位:毫秒
"HttpClientTimeOut": 100*1000,
// 超时策略
"TimeOut": {
//是否启用
"IsEnabled": true,
//执行超过多少毫秒则认为超时
"TimeOutMilliseconds": 5000
}
}
}
}
}
- Timeout方法代码,阻塞120秒,触发网关超时
示例3.2
(点击查看完整示例代码)
[HttpGet("Timeout")]
public string Timeout()
{
Thread.Sleep(120 * 1000);
return "我是Timeout";
}
- 运行结果
分别运行示例3.2
、3.2-2
,浏览器访问
http://localhost:3221/Wing.Demo_3.2/weatherforecast/timeout ,
我们可以看到触发了超时异常降级策略,如下图:
重试
什么是重试?
“重试”就是重复执行当前的动作,在微服务架构中,重试模式是从瞬态错误中恢复的常见模式。 在分布式系统中,这些错误的一些示例是: 网络故障-应用程序在短时间内失去连接。 组件故障-某个组件在短时间内不可用。这通常发生在维护或从崩溃中自动恢复期间。 组件过载-短时间内无法接受新请求。这也可能是由于节流或限速实现。 如您所见,上述错误是自我修复的。在这种情况下,客户端重试请求(立即或延迟后)而不是记录错误并中止请求是有意义的。
- 策略配置
示例3.2-3
(点击查看完整示例代码)
Retry:IsEnabled
是否启用重试策略
Retry:MaxTimes
重试次数
Retry:IntervalMilliseconds
重试间隔,单位:毫秒
超时重试组合全局策略,执行的请求时间超过2000毫秒,则重试3次,每次请求间隔100毫秒,个性化服务策略设定参考“熔断”配置
{
"Gateway": {
"Policy": {
// 全局策略
"Global": {
// 超时策略
"TimeOut": {
//是否启用
"IsEnabled": true,
//执行超过多少毫秒则认为超时
"TimeOutMilliseconds": 2000
},
//重试
"Retry": {
//是否启用
"IsEnabled": true,
//最多重试几次
"MaxTimes": 3,
//重试间隔的毫秒数
"IntervalMilliseconds": 100
}
}
}
}
}
- 运行结果
分别运行示例3.2
、3.2-3
,浏览器访问
http://localhost:3231/Wing.Demo_3.2/weatherforecast/retry ,
我们可以看到触发了超时重试策略,如下图:
限流
什么是限流?
顾名思义,限流就是限制流量,限流的对象可以是请求的频率,传输的速率,或者并发量等,其中最常见的两个限流对象是请求频率和并发量,他们对应的限流被称为 请求频率限流(Request rate limiting)和 并发量限流(Concurrent requests limiting)。
- 策略配置
示例3.2-4
(点击查看完整示例代码)
RateLimit:IsEnabled
是否启用限流策略
RateLimit:NumberOfExecutions
最多执行次数
RateLimit:PerSeconds
每秒数
RateLimit:MaxBurst
最大执行并发数
限流全局策略,每10秒只能执行1个请求,每次最大执行并发数是1,个性化服务策略设定参考“熔断”配置
{
"Gateway": {
"Policy": {
// 全局策略
"Global": {
//限流
"RateLimit": {
//是否启用
"IsEnabled": true,
//最多执行次数
"NumberOfExecutions": 1,
//秒数
"PerSeconds": 10,
//每次最大执行并发数
"MaxBurst": 1
}
}
}
}
}
- 运行结果
分别运行示例3.2
、3.2-4
,浏览器访问
http://localhost:3241/Wing.Demo_3.2/weatherforecast/ratelimit ,
我们可以看到触发了限流策略,如下图:
舱壁
什么是舱壁?
在造船行业,往往使用此类模式对船舱进行隔离,利用舱壁将不同的船舱隔离起来,这样如果一个船舱破了进水,只损失一个船舱,其它船舱可以不受影响,而借鉴造船行业的经验,这种模式也在软件行业得到使用。线程隔离(Thread Isolation)就是这种模式的常见的一个场景,调用者在调用服务提供者时,给每个调用的请求分配独立线程池,出现故障时,最多消耗这个线程池内资源,避免把调用者的所有资源耗尽。
- 策略配置
示例3.2-5
(点击查看完整示例代码)
BulkHead:IsEnabled
是否启用舱壁策略
BulkHead:MaxParallelization
最大并发执行数量
BulkHead:MaxQueuingActions
等待处理的队列长度
舱壁全局策略,个性化服务策略设定参考“熔断”配置
{
"Gateway": {
"Policy": {
// 全局策略
"Global": {
//舱壁
"BulkHead": {
//是否启用
"IsEnabled": true,
//最大并发执行数量
"MaxParallelization": 1,
//等待处理的队列长度
"MaxQueuingActions": 0
}
}
}
}
- 运行结果
分别运行示例3.2
、3.2-5
,浏览器访问
http://localhost:3210/weatherforecast/BulkHeadTest ,
我们可以看到触发了舱壁策略,如下图:
鉴权
支持JWT和设定的AuthKey认证,示例3.2-6
(点击查看完整示例代码)
- JWT配置
UseJWTAuth
是否启用JWT认证
{
"Gateway": {
"Policy": {
// 全局策略
"Global": {
"UseJWTAuth": true
}
}
}
- JWT认证运行结果
分别运行示例3.2
、3.2-6
,浏览器访问
http://localhost:3210/weatherforecast/JwtAuthTest ,
如果不传token,调用接口时网关会返回401状态码,传token之后,可以访问成功。如下图:
- AuthKey配置
AuthKey
固定Key认证,通过匹配请求头“AuthKey”的值,相同则认证通过
{
"Gateway": {
"Policy": {
// 全局策略
"Global": {
"AuthKey": "abcd"
}
}
}
- AuthKey认证运行结果
分别运行示例3.2
、3.2-6
,浏览器访问
http://localhost:3210/weatherforecast/AuthKeyTest ,
如果请求头不传AuthKey,调用接口时网关会返回401状态码,传AuthKey之后,可以访问成功。如下图:
日志
网关日志持久化方式支持直接写入数据库和通过事件总线的方式写入数据库库。
- 配置
Log:IsEnabled
是否启用网关日志记录
Log:UseEventBus
是否启用事件总线(目前支持RabbitMQ)存储日志
Log:Interval
日志写入频率,单位:秒
Log:Filter
日志过滤器,可以指定哪些服务、请求URL不记录日志
{
"Gateway": {
// 请求日志
"Log": {
// 是否启用网关日志记录
"IsEnabled": true,
// 是否启用事件总线(RabbitMQ)存储日志,生产环境推荐启用,可以提升程序的性能
"UseEventBus": true,
//单位:秒
"Interval": 10,
//过滤日志
"Filter": {
//服务名称,完全匹配
"ServiceName": [],
// 请求URL,模糊匹配
"RequestUrl": [],
// 下游URL,模糊匹配
"DownstreamUrl": []
}
}
},
"RabbitMQ": {
"HostName": "192.168.56.99",
"UserName": "admin",
"Password": "admin",
"VirtualHost": "/",
"Port": 5672,
//消息过期时间,单位:毫秒,过期会自动路由到死信队列,小于或等于0则永久有效
"MessageTTL": 0,
"ExchangeName": "Sample.GateWay",
//每次投递消息数量
"PrefetchCount": 1
}
}