应用从单体架构向微服务架构演进的过程中,由于细粒度的微服务应用数量大幅增长,微服务之间的服务发现、负载均衡、熔断限流等服务治理需求显著提高。
在微服务架构下服务之间的调用链路相比单体应用时代更长,微服务化拆分带来系统整体能力提升的同时,也增加了服务间级联故障出现的概率。多个服务之间存在依赖调用,如果某个服务无法及时响应请求,故障向调用源头方向传播,可能引发集群的大规模级联故障,造成整个系统不可用。
为应对这种情况,可以引入熔断策略。为了防止故障范围的扩大,熔断的基本逻辑就是隔离故障。通过不断探测和周期性统计服务失败调用次数,如果服务的健康状况低于设定阈值则启动熔断,剔除错误服务实例。熔断机制虽然解决不了故障,但却能在故障发生时尽量保全非故障链路上的服务接口能被正常访问,将故障范围控制在局部。被熔断的服务也不会一直处于熔断状态,在熔断机制中还会执行故障恢复,探测到服务正常后关闭熔断。Istio支持较全面的故障检测,熔断开启和熔断恢复机制。
限流功能也是保护服务的重要手段,熔断的主要目的是隔离故障,而引起故障的原因除了系统服务内部的问题外,还有可能是请求量超过了系统处理能力的极限,后续新进入的请求会持续加重服务负载,导致资源耗尽发生服务出错。限流的目的就是拒绝过多的请求流量,保证服务整体负载处于合理水平。系统的吞吐量一般是可以被测算的,为了保证系统的稳定运行,一旦达到需要限制的阈值,就需要采取措施限制流量,比如延迟处理、拒绝处理,或者部分拒绝处理等。Istio支持基于服务连接数,请求排队数等设置限流,还支持经典的令牌限流模式,主动保护上游服务。
微服务架构带来开发运维模式巨大变革的同时,也引入了服务治理的诸多问题:一个应用由多个服务组成,每个服务有多个实例,每个实例的运行状态不断变化,这对服务间流量管理和服务治理功能提出了巨大需求。以Istio为代表的服务网格,通过接管服务发送和接收的所有流量,可以轻松承载微服务应用间的通信功能,通过控制平面下发服务治理配置,然后根据配置在各个服务之间的路由流量,提供熔断限流等故障处理策略。下一章中我们重点探讨Istio熔断限流的实现原理。
01熔断限流原理
如何将服务联通起来,是服务治理首先要解决的问题。通常的做法是将通讯层基础功能以SDK的形式嵌入业务代码中,但是这种强耦合的方案会增加开发的难度,增加维护成本,增加质量风险。另外对于SDK调用的代码多处冗余出现,从服务治理的角度来看,这样的方式侵入了业务,并且分散于应用,不利于微服务体系的整体管控。
通过将原来在SDK中的逻辑转移到Sidecar中,提供了另一种可行方案。Sidecar就是在应用节点上部署的代理进程,应用将请求发给代理,由代理完成路由转发。
从整体来看,代理间流量调用关系形成完整的网络,代表服务间复杂的调用关系,承载着系统内的应用通信。各个具体的微服务之间不再直接发生连接,而是转由各自的Sidecar代理通信实现,在应用形态上形成了一组由代理所组成的网状交互结构,这也是服务网格名称的由来。
服务网格的本质是将通用流量治理的功能沉淀至Sidecar中,由Sidecar接管服务流量并对其进行治理。可以通过流量劫持的手段,做到无代码侵入实现流量治理,让开发者更聚焦业务功能,降低微服务的代码复杂性,提高开发效率。通过将服务治理功能从应用本身剥离出来,做到了控制与逻辑的分离。Sidecar模式允许我们向应用无侵入添加多种功能,避免了为满足功能扩展需求而向应用添加额外的代码。
如下图右侧所示,当外部请求调用服务实例接口时,其发送的网络请求会经过它们各自的网络代理,那么代理就可以为其提供服务熔断相关的机制,当调用服务实例持续出错时,就不再将外部请求发送到服务实例,而是直接返回配置的错误码。同样,Proxy代理可以为其提供限流功能,当外部请求流量过大时,代理会对其中一部分请求进行限流,拒绝部分请求,只将部分请求转发下游服务。
将微服务治理逻辑从原先具体的微服务进程中抽离出来,实现由统一控制面管理和代理数据面执行的体系结构,是Istio Service Mesh体系与Spring Cloude等传统微服务体系在架构上最大的区别。各种服务治理逻辑,也是在这样的架构模式下实现的。Service Mesh架构总体上由控制面(Control Plane)和数据面(Data Plane)两部分组成,其中控制面主要承担整个微服务体系治理信息的集中管控分发,而数据面的代理程序则负责具体执行由控制面下发的各类服务治理信息及规则。对于代理程序的部署问题,Istio中通过开启自动注入,在部署应用时可以把代理程序自动部署到用户应用相同的Pod下,用户无需担心代理程序的部署问题。
在Istio中,广义的熔断配置包括两部分:首先是基于请求容量限制的熔断,超过设定阈值的请求直接丢弃返回错误,直观更像限流配置。通过设置connectionPool连接线程池参数实现,该参数可以对上游服务的并发连接数和请求数进行限制(适用于TCP和HTTP),从而实现限流功能。其次是基于服务实例质量检测的服务熔断,这是通过隔离故障服务实例来减少整体服务异常率、降低服务延迟而采用的一种措施。具体来说,Istio引入了异常检测来完成熔断功能,如果在一定时间内服务实例累计发生错误的次数超过了预定义阀值,Istio就会将该错误的服务实例从负载均衡池移除。
异常检测原理:检测到了某个主机异常时,如果到目前为止负载均衡池中还没有主机被隔离出去,将会立即隔离该异常主机;如果已经有主机被隔离出去,就会检查当前隔离的主机数是否低于设定的阈值(通过Envoy中的 outlier_detection.max_ejection_percent 指定),如果当前被隔离的主机数量不超过该阈值,就将该主机隔离出去,否则不隔离。通常异常检测会与主动健康检查一起用于全面的健康检查解决方案,异常检测的类型主要包括连续的5XX响应,包括内部业务错误或连续网关故障。
隔离不是永久的,会有一个时间限制。当主机被隔离后,该主机就会被标记为不健康,除非负载均衡处于恐慌模式。隔离时间等于Envoy中的outlier_detection.base_ejection_time_ms的值乘以主机被隔离的次数。某个主机连续出现故障,会导致它被隔离的时间越来越长。经过了规定的隔离时间之后,被隔离的主机将会自动恢复过来,重新接受调用方的远程调用。被移除的实例在一段时间之后,还会被加回去进行再一次的尝试,成功的话实例被认为成功,否则实例会被重新逐出,这里的驱逐时间是一个基础时间乘以驱逐的次数。Istio中还可以控制驱逐比例,也就是说有多少比例的服务实例在不满足要求时被驱逐。当有太多实例被驱逐的时候,会进入恐慌模式,这时Istio会忽略负载均衡池上实例的健康标记,仍然向所有实例发送请求,从而保证一个服务的整体可用性。
Istio中熔断在DestinationRule的CRD资源的TrafficPolicy中设置,通过设置连接池connectionPool实现限流熔断,设置异常检测outlierDetection实现服务实例隔离熔断。
ConnectionPool下有TCP和HTTP两个类别的配置,二者相互协作,为服务提供有关限流的配置。TCP相关的基础配置有maxConnections和connectTimeout。maxConnections表示到目标服务最大的HTTP1/TCP连接数量,它只会限制基于HTTP1.1协议的连接,不会影响基于HTTP2的连接,因为HTTP2协议只建立一次连接。connectTimeout表示建立TCP连接时的超时时间,默认单位是秒,超出该时间,则连接会被自动断开。HTTP下的配置包括http1MaxPendingRequests、http2MaxRequests和maxRequestsPerConnection三种。http1MaxPendingRequests 表示HTTP请求处于pending状态下的最大请求数,也就是目标服务最多可以同时处理多少个HTTP请求,默认是1024个。http2MaxRequests表示目标服务最大的HTTP2请求数量,默认是1024。maxRequestsPerConnection表示每个TCP连接可以被多少个请求复用,如果将这一参数设置为 1,则会禁止keepalive特性。
OutlierDetection下相关的配置项涉及服务的熔断机制,具体有如下几个基础配置。consecutiveErrors表示如果目标服务连续返回多少次错误码后,会将目标服务从可用服务实例列表中剔除,也就是说进行熔断,不再请求目标服务。当通过HTTP请求访问服务,返回码为502、503或504时,Istio会将本次网络请求判断为发生错误。该属性配置的默认值是5,也就是说如果目标实例连续5个http请求都返回了5xx的错误码,则该服务实例会被剔除,不再接受客户端的网络请求。
Interval表示服务剔除的时间间隔,即在interval时间周期内发生1个consecutiveErrors错误,则触发服务熔断。其单位包括小时、分钟、秒和毫秒,默认值是10秒。baseEjectionTime表示目标服务被剔除后,至少要维持剔除状态多长时间。这段时间内,目标服务将保持拒绝访问状态。该时间会随着被剔除次数的增加而自动增加,时间为baseEjectionTime和驱逐次数的乘积。其单位包括小时、分钟、秒和毫秒,默认是30秒。maxEjectionPercent表示可用服务实例列表中实例被移除的最大百分比,默认是10%。当超出这个比率时,即使再次发生熔断,也不会将服务剔除,这样就避免了因为瞬时错误导致大多数服务实例都被剔除的问题。
minHealthPercent表示健康模式的最小百分比,也就是所有服务实例中健康(未被剔除)的比率。当低于这一比率时,整个集群被认为处于非健康状态,outlierDetection配置相关的服务剔除熔断机制将被关闭,不再进行服务健康检查,所有服务实例都可以被请求访问,包含健康和不健康的主机。该属性的默认值是50%,并且minHealthPercent和maxEjectionPercent的和一般都不超过100%。
除了熔断外,速率限制也是缓解级联故障和防止耗尽共享资源的一种简单有效的方法。Envoy是一个功能丰富的代理,通过配置Envoy过滤器可以为服务轻松添加速率限制的功能。上面介绍的熔断功能,有如下缺点:精确到实例级别,无法针对某个api限流熔断。在路由规则后起作用,服务端接收到超出配置的服务容量限制的请求后被动限流,没有做到流量分发之前主动限流。在Istio体系下,可通过EnvoyFilter这个配置来实现高级的限流需求。EnvoyFilter提供了三种方式进行扩展:编写C++扩展代码,Lua脚本扩展,WebAssembly扩展,实践中,Lua脚本方式使用较多。通过自定义HTTP Filter来匹配限流熔断条件,并将该熔断插件放在router前面,每次处理请求时,会先经过该插件来判断是否需要被限流。限流器还可引用外部的独立限流服务,EnvoyFilter支持两种配置中心集中式限流和本地限流两种限流方式,实现了基于经典令牌原理的限流功能。
在大多数情况下,本地限流控制系统中的上下游之间服务调用吞吐量非常有效,但有时它的效果并不能保证,这时候便需要全局限流服务。最常见的情况是当大量服务发送请求到少量服务(例如各应用服务发送给数据库服务的请求)。在这种情况下,很难对每个下游服务配置足够有效的熔断器,使得上游服务可以平稳运行,这种情况下为服务配置全局限速是一个很好的解决方案。
Envoy全局限流方案基于一个全局限速服务RLS(rate limit service)实现,RLS 被设计为一种提供限速场景的gRPC服务。全局限速生效于被限速业务的网络入口,这意味着无论请求来自集群内部署的其他微服务,还是来自网关以外的外部访问,请求都会被限速,因此全局限速是一种在突发流量激增场景中保护关键业务的有效手段。
02熔断限流实践
2.1 线程池熔断
下面以官方样例中的httpbin服务为例,实践上一节中的熔断技术。创建一个目标规则来配置相关的限流和熔断机制:
apiVersion: networking.istio.io/v1alpha3
kind: DestinationRule
metadata:
name: httpbin
spec:
host: httpbin
trafficPolicy:
connectionPool:
tcp:
maxConnections: 1
http:
http1MaxPendingRequests: 1
maxRequestsPerConnection: 1
outlierDetection:
consecutiveErrors: 1
interval: 1s
baseEjectionTime: 3m
maxEjectionPercent: 100
该配置表示服务连接池最多接收一个TCP连接,HTTP请求pending状态的最大请求为1,后端请求的最大数量也为1,超过上述这些数量,则Proxy会自动为httpbin限流,返回503异常。对于熔断而言,该配置表示每秒钟对httpbin的服务实例进行一次熔断剔除检查,如果处理网络请求,连续返回1次5xx错误码则会被认定为不健康,要从可用服务列表中剔除3分钟。
然后部署fortio测试客户端向httpbin发起请求检查上述配置的有效性。fortio可以控制连接数、并发数和发出去的HTTP调用延时等。下面将使用该客户端触发设置在DestinationRule中的断路器策略。
kubectl apply -f samples/httpbin/sample-client/fortio-deploy.yaml
登入客户端Pod并使用fortio工具调用httpbin服务。使用fortio请求http://httpbin:8000/get接口。-curl参数表明发送一次调用。
FORTIO_POD=$(kubectl get pod | grep fortio | awk '{ print $1 }')
kubectl exec -it $FORTIO_POD -c fortio -- /usr/bin/fortio load -curl http://httpbin:8000/get
可以看到200响应,调用后端服务的请求已经成功。
HTTP/1.1 200 OK
server: envoy
date: Tue, 25 Feb 2022 20:25:52 GMT
content-type: application/json
content-length: 586
access-control-allow-origin: *
access-control-allow-credentials: true
x-envoy-upstream-service-time: 36
{
"args": {},
"headers": {
"Content-Length": "0",
"Host": "httpbin:8000",
"User-Agent": "fortio.org/fortio-1.3.1",
"X-B3-Parentspanid": "8fc453fb1dec2c22",
"X-B3-Sampled": "1",
"X-B3-Spanid": "071d7f06bc94943c",
"X-B3-Traceid": "86a929a0e76cda378fc453fb1dec2c22",
"X-Forwarded-Client-Cert": "By=spiffe://cluster.local/ns/default/sa/httpbin;Hash=68bbaedefe01ef4cb99e17358ff63e92d04a4ce831a35ab9a31d3c8e06adb038;Subject=\"\";URI=spiffe://cluster.local/ns/default/sa/default"
},
"origin": "127.0.0.1",
"url": "http://httpbin:8000/get"
}
接下来测试熔断限流。然后,为了触发httpbin的线程池限流机制,让fortio 以两个线程模式运行,向httpbin发送20个请求,具体命令和返回值如下所示:
kubectl exec "$FORTIO_POD" -c fortio -- /usr/bin/fortio load -c 2 -qps 0 -n 20 -loglevel Warning http://httpbin:8000/get
20:33:46 I logger.go:97> Log level is now 3 Warning (was 2 Info)
Fortio 1.3.1 running at 0 queries per second, 6->6 procs, for 20 calls: http://httpbin:8000/get
Starting at max qps with 2 thread(s) [gomax 6] for exactly 20 calls (10 per thread + 0)
20:33:46 W http_client.go:679> Parsed non ok code 503 (HTTP/1.1 503)
20:33:47 W http_client.go:679> Parsed non ok code 503 (HTTP/1.1 503)
20:33:47 W http_client.go:679> Parsed non ok code 503 (HTTP/1.1 503)
Ended after 59.8524ms : 20 calls. qps=334.16
Aggregated Function Time : count 20 avg 0.0056869 +/- 0.003869 min 0.000499 max 0.0144329 sum 0.113738
# range, mid point, percentile, count
>= 0.000499 <= 0.001 , 0.0007495 , 10.00, 2
> 0.001 <= 0.002 , 0.0015 , 15.00, 1
> 0.003 <= 0.004 , 0.0035 , 45.00, 6
> 0.004 <= 0.005 , 0.0045 , 55.00, 2
> 0.005 <= 0.006 , 0.0055 , 60.00, 1
> 0.006 <= 0.007 , 0.0065 , 70.00, 2
> 0.007 <= 0.008 , 0.0075 , 80.00, 2
> 0.008 <= 0.009 , 0.0085 , 85.00, 1
> 0.011 <= 0.012 , 0.0115 , 90.00, 1
> 0.012 <= 0.014 , 0.013 , 95.00, 1
> 0.014 <= 0.0144329 , 0.0142165 , 100.00, 1
# target 50% 0.0045
# target 75% 0.0075
# target 90% 0.012
# target 99% 0.0143463
# target 99.9% 0.0144242
Sockets used: 4 (for perfect keepalive, would be 2)
Code 200 : 17 (85.0 %)
Code 503 : 3 (15.0 %)
Response Header Sizes : count 20 avg 195.65 +/- 82.19 min 0 max 231 sum 3913
Response Body/Total Sizes : count 20 avg 729.9 +/- 205.4 min 241 max 817 sum 14598
All done 20 calls (plus 0 warmup) 5.687 ms avg, 334.2 qps
从上面的返回值可以看到一共20个请求,状态码为200的请求响应有17个,状态码为503的为3个。
将并发连接数提高到3个,发送30次请求。
kubectl exec "$FORTIO_POD" -c fortio -- /usr/bin/fortio load -c 3 -qps 0 -n 30 -loglevel Warning http://httpbin:8000/get
可以看到只有36.7%的请求成功,其余的均被线程池熔断器拦截。
Code 200 : 11 (36.7 %)
Code 503 : 19 (63.3 %)
2.2 服务故障熔断
接下来使用fortio来验证服务故障熔断机制。使用fortio调用httpbin的 http://httpbin:8000/status/502接口,该接口会直接返回状态码为502的响应,这样就触发outlierDetection配置,配置中只要返回一次5xx的网络响应服务实例就会被熔断剔除。然后使用以下命令,连续调用10次相同的接口。
kubectl exec "$FORTIO_POD" -c fortio -- /usr/bin/fortio load -c 1 -qps 0 -n 10 -loglevel Warning http://httpbin:8000/get
可以发现,所有的请求都是返回的503,也就是说httpbin服务被熔断了,所以全部返回503的响应。
Code 503 : 10 (100.0 %)
2.3 本地限流
下面配置和测试Istio的限流功能,限制流量防止因系统过载而崩溃,通过配置EnvoyFilter资源来实现。istio限流有两大类,一个是本地限速,另一个是全局限流。本地限流是在envoy内部提供一种令牌桶限速的功能,全局限流需要访问外部限速服务。
首先配置本地限流:
cat < envoyfilter-local-rate-limit.yaml
apiVersion: networking.istio.io/v1alpha3
kind: EnvoyFilter
metadata:
name: filter-local-ratelimit-svc
spec:
workloadSelector:
labels:
app: productpage
configPatches:
- applyTo: HTTP_FILTER
match:
listener:
filterChain:
filter:
name: "envoy.filters.network.http_connection_manager"
patch:
operation: INSERT_BEFORE
value:
name: envoy.filters.http.local_ratelimit
typed_config:
"@type": type.googleapis.com/udpa.type.v1.TypedStruct
type_url: type.googleapis.com/envoy.extensions.filters.http.local_ratelimit.v3.LocalRateLimit
value:
stat_prefix: http_local_rate_limiter
token_bucket:
max_tokens: 10
tokens_per_fill: 10
fill_interval: 60s
filter_enabled:
runtime_key: local_rate_limit_enabled
default_value:
numerator: 100
denominator: HUNDRED
filter_enforced:
runtime_key: local_rate_limit_enforced
default_value:
numerator: 100
denominator: HUNDRED
response_headers_to_add:
- append: false
header:
key: x-local-rate-limit
value: 'true'
EOF
kubectl apply -f envoyfilter-local-rate-limit.yaml -n istio
以上本地限流配置需要通过EnvoyFilter来实现,不请求外部服务,在envoy内部通过令牌桶算法实现。http filter的名称必须是envoy.filters.http.local_ratelimit,type和type url也是固定的写法,stat_prefix可以修改,表示生成stat的指标前缀。token_bucket配置令牌桶,max_tokens表示最大令牌数量,tokens_per_fill表示每次填充的令牌数量,fill_interval表示填充令牌的时间间隔。filter_enabled表示启用但不是强制,filter_enforced表示强制,可以配置百分比。response_headers_to_add修改响应头信息,append为false表示修改,true表示添加。
go-stress-testing -c 10 -n 1000000 -u http://192.168.229.134:30945/productpage
前4秒响应统计如下,可以看到前4秒的请求拿到了5个令牌,也就有5次成功。我们上面配置了一分钟下发10个令牌,也就是说前一分钟内最多有10次请求能成功。429 响应码为Too Many Requests,代表太多的请求,响应已被限流。
─────┬───────┬───────┬───────┬────────┬───────┬────────┬────────┬────────┬────────┬────────
耗时 | 并发数│ 成功数│ 失败数│ qps │最长耗时│最短耗时 │平均耗时 │下载字节 │字节每秒 │ 响应码
─────┼───────┼───────┼───────┼───────┼────────┼────────┼────────┼────────┼────────┼────────
1s│ 7│ 2│ 761│ 2.94│ 124.68│ 1.98│ 3406.97│ 21,476│ 21,470│200:2;429:761
2s│ 10│ 5│ 1636│ 2.55│ 1788.46│ 1.98│ 3928.11│ 52,771│ 26,383│200:5;429:1636
3s│ 10│ 5│ 2962│ 1.70│ 1788.46│ 1.04│ 5871.68│ 76,639│ 25,545│200:5;429:2962
4s│ 10│ 5│ 4459│ 1.28│ 1788.46│ 1.04│ 7810.78│ 103,585│ 25,896│200:5;429:4459
2.4 全局限流
接下来我们继续验证全局限流配置。全局限流配置较复杂,需要以下四步。
1.创建ConfigMap。这个ConfigMap是限速服务用到的配置文件,envoy v3版本的限速格式。domain是域名,在envoyfilter中被引用,descriptors的PATH表示请求的路径,可以有多个值,rate_limit配置限速配额,这里配置productpage 1分钟1个请求,其他url限制1分钟100个请求。
cat << EOF > ratelimit-config.yaml
apiVersion: v1
kind: ConfigMap
metadata:
name: ratelimit-config
data:
config.yaml: |
domain: productpage-ratelimit
descriptors:
- key: PATH
value: "/productpage"
rate_limit:
unit: minute
requests_per_unit: 1
- key: PATH
rate_limit:
unit: minute
requests_per_unit: 100
EOF
kubectl apply -f ratelimit-config.yaml -n istio
2.配置独立限速服务。创建了redis和一个ratelimit服务。
cat << EOF > ratelimit-deploy.yaml
apiVersion: v1
kind: Service
metadata:
name: redis
labels:
app: redis
spec:
ports:
- name: redis
port: 6379
selector:
app: redis
---
apiVersion: apps/v1
kind: Deployment
metadata:
name: redis
spec:
replicas: 1
selector:
matchLabels:
app: redis
template:
metadata:
labels:
app: redis
spec:
containers:
- image: redis:alpine
imagePullPolicy: Always
name: redis
ports:
- name: redis
containerPort: 6379
restartPolicy: Always
serviceAccountName: ""
---
apiVersion: v1
kind: Service
metadata:
name: ratelimit
labels:
app: ratelimit
spec:
ports:
- name: http-port
port: 8080
targetPort: 8080
protocol: TCP
- name: grpc-port
port: 8081
targetPort: 8081
protocol: TCP
- name: http-debug
port: 6070
targetPort: 6070
protocol: TCP
selector:
app: ratelimit
---
apiVersion: apps/v1
kind: Deployment
metadata:
name: ratelimit
spec:
replicas: 1
selector:
matchLabels:
app: ratelimit
strategy:
type: Recreate
template:
metadata:
labels:
app: ratelimit
spec:
containers:
- image: envoyproxy/ratelimit:6f5de117 # 2021/01/08
imagePullPolicy: Always
name: ratelimit
command: ["/bin/ratelimit"]
env:
- name: LOG_LEVEL
value: debug
- name: REDIS_SOCKET_TYPE
value: tcp
- name: REDIS_URL
value: redis:6379
- name: USE_STATSD
value: "false"
- name: RUNTIME_ROOT
value: /data
- name: RUNTIME_SUBDIRECTORY
value: ratelimit
ports:
- containerPort: 8080
- containerPort: 8081
- containerPort: 6070
volumeMounts:
- name: config-volume
mountPath: /data/ratelimit/config/config.yaml
subPath: config.yaml
volumes:
- name: config-volume
configMap:
name: ratelimit-config
EOF
kubectl apply -f ratelimit-deploy.yaml -n istio
3.创建envoy-filter,这个envoyfilter作用在网关上,配置了http过滤器envoy.filters.http.ratelimit,和一个cluster。http过滤器的cluster地址指向cluster配置的地址,就是ratelimit service所在的地址。domain和步骤1中configmap的值一致,failure_mode_deny表示超过请求限值就拒绝,rate_limit_service配置ratelimit服务的地址(cluster),可以配置grpc类型或http类型。
cat << EOF > envoyfilter-filter.yaml
apiVersion: networking.istio.io/v1alpha3
kind: EnvoyFilter
metadata:
name: filter-ratelimit
namespace: istio-system
spec:
workloadSelector:
# select by label in the same namespace
labels:
istio: ingressgateway
configPatches:
# The Envoy config you want to modify
- applyTo: HTTP_FILTER
match:
context: GATEWAY
listener:
filterChain:
filter:
name: "envoy.filters.network.http_connection_manager"
subFilter:
name: "envoy.filters.http.router"
patch:
operation: INSERT_BEFORE
# Adds the Envoy Rate Limit Filter in HTTP filter chain.
value:
name: envoy.filters.http.ratelimit
typed_config:
"@type": type.googleapis.com/envoy.extensions.filters.http.ratelimit.v3.RateLimit
# domain can be anything! Match it to the ratelimter service config
domain: productpage-ratelimit
failure_mode_deny: true
rate_limit_service:
grpc_service:
envoy_grpc:
cluster_name: rate_limit_cluster
timeout: 10s
transport_api_version: V3
- applyTo: CLUSTER
match:
cluster:
service: ratelimit.istio.svc.cluster.local
patch:
operation: ADD
# Adds the rate limit service cluster for rate limit service defined in step 1.
value:
name: rate_limit_cluster
type: STRICT_DNS
connect_timeout: 10s
lb_policy: ROUND_ROBIN
http2_protocol_options: {}
load_assignment:
cluster_name: rate_limit_cluster
endpoints:
- lb_endpoints:
- endpoint:
address:
socket_address:
address: ratelimit.istio.svc.cluster.local
port_value: 8081
EOF
kubectl apply -f envoyfilter-filter.yaml -n istio-system
4.创建action envoyfilter。这个envoyfilter作用在入口网关处,给80端口的虚拟主机配置了一个rate_limits 动作,descriptor_key用于选择在configmap里配置的key。
cat << EOF > envoyfilter-action.yaml
apiVersion: networking.istio.io/v1alpha3
kind: EnvoyFilter
metadata:
name: filter-ratelimit-svc
namespace: istio-system
spec:
workloadSelector:
labels:
istio: ingressgateway
configPatches:
- applyTo: VIRTUAL_HOST
match:
context: GATEWAY
routeConfiguration:
vhost:
name: "*:80"
route:
action: ANY
patch:
operation: MERGE
# Applies the rate limit rules.
value:
rate_limits:
- actions: # any actions in here
- request_headers:
header_name: ":path"
descriptor_key: "PATH"
EOF
kubectl apply -f envoyfilter-action.yaml -n istio-system
下面进行10并发压测:
go-stress-testing -c 10 -n 100000 -u http://192.168.229.134:30945/productpage
前5秒响应统计如下,可以看到只有一个请求成功,和我们配置的一致。
─────┬───────┬───────┬───────┬────────┬────────┬────────┬────────┬────────┬────────┬────────
耗时│ 并发数│ 成功数│ 失败数 │ qps │最长耗时│最短耗时 │平均耗时 │下载字节 │字节每秒 │ 响应码
─────┼───────┼───────┼───────┼────────┼────────┼────────┼────────┼────────┼────────┼────────
1s│ 10│ 1│ 1051│ 1.01│ 55.51│ 3.70│ 9914.38│ 4,183│ 4,176│200:1;429:1051
2s│ 10│ 1│ 1629│ 0.50│ 55.51│ 3.70│19807.86│ 4,183│ 2,090│200:1;429:1629
3s│ 10│ 1│ 2154│ 0.34│ 55.51│ 3.70│29829.63│ 4,183│ 1,393│200:1;429:2154
4s│ 10│ 1│ 2662│ 0.25│ 55.51│ 3.70│39823.69│ 4,183│ 1,045│200:1;429:2662
5s│ 10│ 1│ 3166│ 0.20│ 58.63│ 3.70│49865.16│ 4,183│ 836│200:1;429:3166
03总结
本文作者讲解了服务治理中熔断和限流的基本概念,以及Istio实现熔断和限流的原理,最后演练了Istio中熔断和限流的相关操作。通过阅读本文,读者可掌握熔断和限流的基本概念、原理和配置操作。如需进一步学习熔断和限流相关知识,请参考附录参考文献。