之前看了重构的书,最近在公司内部自学网站学习重构的课程,简单记录一些核心要点。
postman请求前脚本,常用于生成token和时间戳
postman请求前脚本,常用于生成token和时间戳
JMeter超大入参返回
JMeter超大入参返回
入参太大,StackOverFlow
如果入参太大,比如几千行的json,http sample直接无法执行,查看日志显示StackOverFlow. 从hashcode方法开始抛出的,怀疑是放入本地变量了。
windows解决方案,新增文件%JMETER_HOME%\bin\setenv.bat,可以容纳近10m的入参。
1 | set HEAP=-Xss10m |
返回太大,Heap OOM
如果返回的内容太大,除了要禁用查看结果树、开启gzip压缩等,还可以调大堆区,这里同样给出windows解决方案。
windows解决方案,新增文件%JMETER_HOME%\bin\setenv.bat,亲测大约5m以下json返回,GUI不会崩。
1 | set HEAP=-Xms4g -Xmx5g -XX:MaxMetaspaceSize=512m |
拦截器+信号量做接口429限流
高可用之接口限流
限流方案
- 请求存消息队列,实例根据自己处理能力,拉取消息消费
- 线程池控制,排满线程池队就直接失败
- 请求拦截器,维护一个信号量控制同时并发处理数,超出排队值(require排队)就返回429状态码
信号量方案
Spring管理唯一bean,内含唯一的限流拦截器。以下是关键变量
Semaphore current_model_semaphore 所有请求都共用一个信号量;
int semaphoreSize 信号量有多少permits
int semaphoreMaxQueueSize 设定多少请求可以排队,超了就抛异常了
List
ConcurrentMap<String, AtomicInteger> concurrents 接口对应有多少个当前处理数
Boolean returnTooManyRequestError 是否启用429返回
处理逻辑
- 判断当前请求的url是否匹配apiFilterList其中一个,不匹配返回filter chain后续处理
- 如果未启动429返回,则阻塞地获取信号量。获取成功则继续处理,超时会触发 InterruptedException ,捕捉后抛业务异常
- 如果请求头没有标识本次请求为最后一次重试,调用tryAcquire()获取信号量的permits,如果没有现成可用的permits,立即返回,接着就返回429状态码,结束请求
- 如果已达最大重试次数,那么本次请求应该更加“倔强”地等。如果排队获取信号量的线程还没达到设定的最长semaphoreMaxQueueSize,那么就acquire等待。如果队伍已满,只能放弃等待直接返回429.
- 所有acquire()后,在finally块做信号量的release()
graph TB 1{判断当前请求的url是否匹配apiFilterList其中一个} 2[拦截器返回交给filter chian后续处理] 3{是否启用429返回} 4{acquire阻塞地获取信号量} 5[捕获InterruptedException后抛业务异常] 6[正常走业务逻辑] 7{是否达到最大重试次数} 8{tryAcquire有无现成的permits} 9[快速失败返回429] 10{是否达到最大排队长度} 1--无需限流-->2 1--需要限流-->3 3--未启用429-->4 4--超时-->5 4--成功获取-->6 3--启用429-->7 7--否-->8 8--无-->9 8--有-->6 7--是-->10 10--是-->9 10--否-->4
画瓢系列:玩具级别的电商系统的基本功能设计
业务先行,先抄一个业务功能的设计。
画瓢系列:山寨一下公司的IT能力,做一个玩具级别的电商系统
在菊厂中做着卑微的螺丝钉,日常经常使用很多很多IT的系统、框架、公共能力,接触的微服务架构。每天都只是简单地使用,了解特性,总是对其中的实现和原理抱有很大的好奇。
画瓢-Springboot骨架
在菊厂中做着卑微的螺丝钉,日常经常使用很多很多IT的系统、框架、公共能力,接触的微服务架构。每天都只是简单地使用,了解特性,总是对其中的实现和原理抱有很大的好奇。画瓢系列第一章就是Java web常用Springboot骨架的尝试。直接抄袭公司的东西是不对的,直接用开源的组件模拟一下大概的意思应该是可以的。
画瓢-Springboot骨架
在菊厂中做着卑微的螺丝钉,日常经常使用很多很多IT的系统、框架、公共能力,接触的微服务架构。每天都只是简单地使用,了解特性,总是对其中的实现和原理抱有很大的好奇。画瓢系列第一章就是Java web常用Springboot骨架的尝试。直接抄袭公司的东西是不对的,直接用开源的组件模拟一下大概的意思应该是可以的。
Jmeter测试时间不准?开发拿Postman结果说事
Jmeter测试时间不准?开发拿Postman结果说事
术语解释
Jmeter术语
Latency:请求发出到接收到第一个返回的字节的时间
Response time (= Sample time = Load time = Elapsed time):请求发出到接收完最后一个字节的时间
可以看到响应时间总是比Latency大
Postman术语
Socket Initialization: 打开socket时间,一般常数时间
DNS Lookup:将请求域名换成ip的时间,一般也很短,太慢也存在优化空间
TCP Handshake:TCP握手时间,受网络状态影响
Transfer Start:请求发出到接收到第一个返回的字节的时间。等于Jmeter的Latency,等于Chrome的TTFB(Time To First Byte)
Download:整个返回体的下载(接收)时间
Process:基本是常数时间,接收后Postman做请求头分离等时间
场景复现
同一个请求,返回体Postman显示900k,耗时大约300ms,Jmeter要500+ms。定位问题使用Dynatrace,发现服务器处理时间260+,接近Postman。此时开发就在嘟囔,测试的时间不知道怎么搞的。这时候作为测试负责人,心里窝火,但是事实摆在眼前。只能认真研究为啥。
首先,我们看下具体展开的时间
工具 | 总时间 | TTFB | Download |
---|---|---|---|
Postman | 194 | 162 | 26 |
Jmeter | 537 | 172 | 365 |
TTFB/Latency时间差不多,在Download时间上差距很大,为什么Postman这么快呢?经前辈指点说Jmeter要手动添加gzip。
立马查看Jmeter请求的头和Postman请求的头,原来Postman的request header自动加上了Accept-Encoding: gzip, deflate,而JMeter没有。
打开JMeter的Debug级别日志可以看见返回体已经不是明文,实测一下:
工具 | 总时间 | TTFB | Download |
---|---|---|---|
Postman | 194 | 162 | 26 |
Jmeter with gzip | 177 | 171 | 6 |
扩展: Accept-Encoding & Content-Encoding
HTTP 请求头 Accept-Encoding 会将客户端能够理解的内容编码方式——通常是某种压缩算法——进行通知(给服务端)。通过内容协商的方式,服务端会选择一个客户端提议的方式,使用并在响应头 Content-Encoding 中通知客户端该选择。
即使客户端和服务器都支持相同的压缩算法,在 identity 指令可以被接受的情况下,服务器也可以选择对响应主体不进行压缩。导致这种情况出现的两种常见的情形是:
要发送的数据已经经过压缩,再次进行压缩不会导致被传输的数据量更小。一些图像格式的文件会存在这种情况;
服务器超载,无法承受压缩需求导致的计算开销。通常,如果服务器使用超过80%的计算能力,微软建议不要压缩。
建议&讨论
- 请求体可以常规地加上 Accept-Encoding: gzip, deflate
- 如果Sample Time在开启压缩后还是比Latency大很多,说明这个接口返回很大数据。此时我们要考虑服务器的内存使用量,因为大数据量返回的接口常规优化方式就是改大数据库一次的Fetch Size,此时服务器的内存占用量会大幅上升,严重时可以导致OOM。我们在回归性能问题单的同时,要观察服务器的内存使用量。
- 大数据量的接口从业务系统同步接口返回可能不是一个最佳的方案,会扰动业务系统的性能。应该通过别的途径,以非实时批处理的方式取大量数据更加符合分析统计的场景。
- 开发在优化时往往对Download Time没有特别好的优化方法,我们在确定返回体已经开启压缩后,可以用Latency作为指标。但是聚合报告聚合的是Sample Time,此时我们可以在
查看结果树
或用表格查看结果
中配置保存到csv文件,后期用Excel分析平均值或90%值。
连接池监控
连接池
数据源是各种数据库,甚至你可以认为excel都是一个数据源。连接池是为了复用连接数据源的链接的管理池。
连接池的监控可以反应一部分的业务高峰,形成告警。持续的监控指标也能作为应用崩溃的复盘数据。
监控原理
公司平台使用普罗米修斯采集Java暴露的JMX数据,作为产品研发团队,只需要在程序启动后,将每个数据源包装一个Listener,多个数据源打包成map,交给MBeanExporter
1 | MBeanExporter exporter = new MBeanExporter(); |
指标设置
样例代码中,指标的计算是这样的:
最大上限: maxSize
当前创建: poolSize(池中总连接)
空闲数: available(池中可用连接)
已使用: poolSize - available
利用率: (poolSize - available) / poolSize
一开始我是觉得不太合理,监控的时候,不太好监控。仔细思考一下,也可以组合一下用当前暴露的指标做监控:
- 当前创建>90 (硬指标,不方便)
- 利用率在多个采集周期都>90% (持续高涨,当前创建数应该会增加)
其实如果不是非常学院派,常人理解的指标应该是这样计算:
最大上限: maxSize
当前创建: poolSize
空闲数: maxSize - poolSize
已使用: poolSize - available
利用率: poolSize / maxSize