随着掌门用户数量,业务的快速迭代,服务发布出现的事故影响面越来越大,有可能是新业务流程与老业务流程的互相兼容的问题,或者是前后端一些较小的手误导致应用故障(中台受此风险的影响更大)。
在不断的挖坑填坑中,我们总结出以下的问题:
影响范围不可控。一旦发布到生产,特别是一些特别重要的服务的发布,如登录、首页、组织架构、消息中心等。这些服务一旦出问题,会导致将近90%业务不可连续。
发布后验证的时间节点。为了保证上线后第一时间进行生产验证,一般在非业务高峰期(半夜12点后)进行发布和回归验证。深夜发布,不仅参与发版的开发,测试和运维同学较为疲劳,而且发布过程中遇到的各种小问题导致发布完成大多在深夜2点,这个时候因为发布人员都比较疲劳,在进行线上回归验证时难以保持足够的警惕心,容易产生漏测的情况。
针对以上背景,测试需要对已有的两套方案分别进行压测。综合评估,选取最优方案作为公司内部使用。
方案一:Java Agent
方案二:Solar(Java SDK)
Java Agent :是运行在 main方法之前的拦截器,内定的方法名是 premain ,原理是先执行 premain 方法然后再执行 main 方法。在加载java文件之前做拦截修改字节码,实现了按流量匹配灰度和按条件匹配灰度2种方式。
Java SDK:Solar运用SDK实现了基于http header传递和规则订阅的全链路发布。采用配置中心配置路由规则映射在网关过滤器中植入Header信息,路由规则传递到全链路服务中而实现。灰度路由方式主要有:多版本匹配灰度和多版本权重匹配灰度。
1.按流量匹配灰度:
支持完全随机和路径随机,通过规则中设置百分比实现。如:
参数来源 | 参数 | 流量比 |
---|---|---|
header | gray | 30 |
2.按条件匹配灰度:
支持header,param,cookie,body参数来源,通过设置equals,in,notIn,range, startWith,endWith条件,配置的key,value的值。如:
参数来源 | 参数 | 条件 | 条件值 |
---|---|---|---|
header | gray | in | 1 |
只要请求满足规则配置,则路由灰度机器,否则路由common 机器。
路由架构图:
1.多版本匹配灰度
灰度和common环境分别是2个不同版本号,配置中设置某个服务路由哪个版本号,配置文件中这样配置:
{"solar-service-a":"1.0",
"solar-service-b":"1.1"}
2.多版本权重匹配灰度
配置中设置不同版本的权重(流量比例),配置文件中这样配置:
{"solar-service-a":"1.0=90;1.1=10", "solar-service-b":"1.0=90;1.1=10"}
按服务指定版本号和权重路由对应权重到版本号。
路由架构图:
公司业务复杂度请参考下图,这是内部某业务的局部拓补图:
由于业务调用链的复杂性,为了尽可能覆盖生产场景,又要做到不冗余。测试设计选用了1个网关+2个微服务的策略进行压测,详情见下文。
1.压测阶段:集成测试-系统测试-验收测试-回归测试-性能测试
2.压测方法:逻辑覆盖法、基本路径测试法,场景法等
3.压测机器:所有实例选用阿里云独享型机器(1个网关+2个微服务)
多核独享 | CPU | Memory | Environment | 个数(台) |
---|---|---|---|---|
压测机 | 4C | 8G | UAT | 1 |
网关 | 8C | 16G | UAT | 1 |
微服务A | 4C | 8G | UAT | 3 |
微服务B | 4C | 8G | UAT | 4 |
4.压测分类:spring cloud原生,加包无灰度,加包有灰度
5.压测链路:网关-A-B,A-B
6.压测规则:为了保持2个方案条件一致性,统一选用权重30%的灰度流量作为规则
7.压测工具:
不是大家喜闻乐见的jMeter而是wrk。由于wrk具有一个良好的特性:很少的线程能够压出很大的并发量
以下是jmeter与wrk的实现原理的对比:
-------------------------------------------------------------------------------------------------------
其次,在同等压力下,wrk占用的资源远远小于jMeter,故本次压测中我们采用了wrk
简单介绍一下wrk的用法:
wrk配合lua脚本使用,属于命令行工具。命令格式:./wrk -c1 -t1 -d10m -T8s -s ./scripts/wrk.lua --latency "http://10.25.174.21:8080"
参数解析:
1-c --connections(连接数)
2-d --duration(测试持续时间)
3-t --threads(线程)
4-s --script(脚本)
5--lantency (响应信息。包括平均值, 标准偏差, 最大值等)
8.压测参数:
-c =1000, -t=16, -d =3m
9.压测脚本:
1local getq = "/gray-demo-a/go?name=a"
2function request()
3 req = wrk.format("GET",getq)
4 return req
5end
6function done (summary, latency, requests)
7 local durations=summary.duration / 1000000
8 local errors=summary.errors.status
9 local requests=summary.requests
10 local valid=requests-errors
11 io.write("Durations: "..string.format("%.2f",durations).."s".."\n")
12 io.write("Requests: "..summary.requests.."\n")
13 io.write("Avg RT: "..string.format("%.2f",latency.mean / 1000).."ms".."\n")
14 io.write("Max RT: "..(latency.max / 1000).."ms".."\n")
15 io.write("Min RT: "..(latency.min / 1000).."ms".."\n")
16 io.write("Error requests: "..errors.."\n")
17 io.write("Valid requests: "..valid.."\n")
18 io.write("QPS: "..string.format("%.2f",valid / durations).."\n")
19 io.write("--------------------------\n")
20End
10.压测返回:为了避免处理复杂的逻辑,设计只返回一行简单的字符串
压测QPS | G-A-B | A(1台)-B |
---|---|---|
spring cloud原生 | 5434 | 7850 |
agent无灰度 | 5223 | 7652 |
agent有灰度 | 4957 | 6174 |
无灰度性能下降百分比(和spring cloud 原生比较) | -3.88% | -2.52% |
有灰度性能下降百分比(和spring cloud 原生比较) | -8.77% | -21.35% |
----- | ----- | ----- |
spring cloud 原生 | 6244 | 5529 |
solar无灰度 | 6120 | 5100 |
solar有灰度 | 5830 | 4600 |
无灰度性能下降百分比(和spring cloud 原生比较) | -1.98% | -7.75% |
有灰度性能下降百分比(和spring cloud 原生比较) | -6.63% | -16.80% |
测试过程中发现,同样的脚本,同样的场景,同样的参数在不同时间点,timeout个数较多,QPS波动较大,且qps较低,始终达不到预期。根据问题采取如下措施逐步得以解决(以原生为例):
分别在源和目标主机同时抓包:tcpdump -i any host x.x.x.x -w /x/x.pcap
,分析比较有没有丢包。发现target没有响应SYN,想到查看TCP accept queue是否满了
查看接收端的系统日志/var/log/message
有没有报错,结果并未有error信息
使用命令:netstat -s | egrep "listen|LISTEN"
,果然,有大量连接溢出
查看网关cat /proc/sys/net/ipv4/tcp_max_syn_backlog
查看网关cat /proc/sys/net/core/somaxconn
,发现somaxconn和max_syn_backlog两个值较小,需要调整
登录网关调整参数,步骤:
1、vi /etc/sysctl.conf
2、调整如下2个参数值:
net.core.somaxconn=128 调整为1280
net.ipv4.tcp_max_syn_backlog=1024 调整为10240
3、生效
sysctl -p
网关尝试换机器:
网关实例规格由sn1(较老规格,CPU:8c)变更至 c6(新规格,CPU:8c)
说明:
同样核数的实例,新规格一般较停售的旧规格性能好。esc.c6具有2大优良特性:开启numa,CPU更厉害。
换机器后QPS有部分提升但还是达不到预期。
通过grafana看板,查看tcp连接数分析到网关到微服务的有效连接数接近20,几乎等于默认值。考虑查看:zuul.host.maxTotalConnections,zuul.semaphore.max-semaphores,zuul.host.maxPerRouteConnections这三个参数的值。
说明:
zuul.host.maxTotalConnections
适用于ApacheHttpClient,如果是okhttp无效。每个服务的http客户端连接池最大连接,默认是200。
zuul.host.maxPerRouteConnections
适用于ApacheHttpClient,如果是okhttp无效。每个route可用的最大连接数,默认值是20。
zuul.semaphore.max-semaphores
Hystrix 最大的并发请求
execution.isolation.semaphore.maxConcurrentRequests,这个值并非TPS、QPS、RPS等都是相对值,指的是1秒时间窗口内的事务/查询/请求,semaphore.maxConcurrentRequests是一个绝对值,无时间窗口,相当于亚毫秒级的。当请求达到或超过该设置值后,其其余就会被拒绝。默认值是100
修改zuul的参数:
zuul.host.maxTotalConnections=20000
zuul.semaphore.max-semaphores=5000
MaxConnectionsPerHost=2000
测试结果数据:
压测QPS | G-B | G-A-B | A(1台)-B |
---|---|---|---|
网关:ecs.c6 1台,A:独享型3 台,B:独享型 4台 | 9012 | 8512 | 7418 |
网关:ecs.c6 1台,A:ecs.c6 1台,B:ecs.c6 1台 | 8932 | 8486 | 11115 |
采取以上措施,最终timeout个数减少,所占比例不到0.1%。QPS达到9000左右,符合预期。
性能测试过程中,CPU运行稳定,内存没有溢出现象。综合分析以上测试数据,由于solar 性能优于agent 5%左右, 未来将选用solar作为公司灰度方案,方便业务线灰度发布。
本文作者