高并发性能调优

来源:互联网 发布:软件脱壳教程 编辑:程序博客网 时间:2024/06/09 03:22

下面是项目大概的一个模型:


从用户角度看,完成request到接收response的时间为jvm内部处理时间+http请求Redis 网络IO时间 + https请求远程服务器 网络IO时间 +用户本身request和服务器response返回的网络IO时间。


实际本地测试,https请求响应时间大约110ms~350ms 波动。使用

REST Client工具测试。



第一阶段:

使用多线程Future模型+普通HttpClient请求。

关键代码:

将Callable提交到线程池里,返回Future对象。Future一旦有值时,就通过调用Jfinal的renderText()输出

下面就是使用HttpClient进行请求

测试结果:

通过实际测试,并发从一开始36/sec逐渐降到10/sec。在控制台看到不少链接超时的异常信息。不少远程http请求Redis服务耗时竟然超过30s。


原因分析:Http连接Redis服务,虽然在正常情况下每次都能迅速响应,大约80ms-120ms(本地环境)。生产环境可以保证Redis和项目响应速度更快,但是在大量http请求下,由于目前代码效率不高,请求若是不能及时处理完,就会导致后面http请求延迟响应。


改善:引入google guava框架,利用其中的Cache功能,因为带有expireAfterWrite功能。把guava cache服务当成1级缓存,Redis服务当成2级缓存。并且增加保证cache内各对象失效时间早于Redis内存储的失效时间,并且cache能够及时从Redis更新.这样减少了Http请求Redis的网络开销。


第二阶段:

引入guava框架后,改善显著。

关键代码:

一开始预设1000秒过期。


cache 内的对象如果过期了,需要重新设置过期时间,过期时间需要动态计算服务器当前时间和Redis内保存的时间差值,保证cache内的对象不会旧于Redis。这样在多实例中,guava的cache充当了1级缓存。


测试结果: 并发果然大大提高,大约可以上到57/sec.

本来两次Http请求,由于引入了guava缓存,变成了一次https请求。效率当然提高了。但是仍然不是很满意,因为测试过程中error率大约有0.3%。而且观察日志,不少请求费时有50s,甚至有120s。测试时发现jetty在运行过程中,会发生停顿现象,所谓的停顿就是完全没有响应。熟悉jvm垃圾回收的朋友应该知道,这个时候jvm发生了Full GC,GC过程中会对导致程序运行中断。


原因分析:

1.大量http请求堆积,需要限制访问httpClient.execute方法的数量。避免引起阻塞。

2.降低或避免Full GC,要对jvm运行参数进行调优。但是在生产环境中(微软云,Paas服务),却无法配置jvm运行参数(可能我暂时不知道吧)。


措施:

引入Java多线程信号量Semaphore


Semaphore(信号量)是用来控制同时访问特定资源的线程数量,它通过协调各个线程,以保证合理的使用公共资源


通俗的讲:就是公园大门就10扇门,来了1000人都争先恐后的挤破脑袋想进去,结果都挤在一堆了乱哄哄的,没见的有多少人能进去。

Semaphore就是做了一个控制管理,比如规定每次只能放10人进行来,其它人只能等,处理完一个,后面就跟上,直到全部处理完。这样比一堆人哄在门口要有效率的多。

(Semaphore 在本公众号早些时候有专门文章讲解的)


对于本应用来说,Semaphore的值太低是不行的,太高也不行。

关键代码:

采用了公平模式



acquire() 获得锁

release() 释放锁




测试了几个数字发现当Semaphore = 200时,并发数比较高。


测试结果:公平模式大约68.7/sec,峰值可以上到110/sec,错误率0%,果然通过控制访问httpClient.execute方法的数量,能提高并发数。在测试过程中,仍然发生了好几次Full gc(),把并发数给降了下来。


第三阶段:

上Apache网站上发现HttpAsyncClient框架


HttpAsyncClient may be of interest to anyone building HTTP-aware client applications based on asynchronous, event driven I/O model.


OSChina上这样介绍的

HttpAsyncClient 的出现并不是为了替换 HttpClient,而是作为一个补充用于需要大量并发连接,对性能要求非常高的基于HTTP的原生数据通信,而且提供了事件驱动的 API。


关键代码:

改造Post请求方法



连接池初始化以及单例模式


直接返回Future对象!查看源码,还有另外一个惊喜,不需要再对SSL写一堆东西了。注意:使用HttpAsyncClient后不再使用Semaphore了。


测试结果:每秒稳定80/sec~85/sec,而且jvm几乎没有停顿过。5000个请求很快就跑完了。而且最大延迟也就7s.

0 0