spring websocket性能调优-TProfiler实践
来源:互联网 发布:网络播放器哪个平台好 编辑:程序博客网 时间:2024/05/16 08:49
TProfiler工具简介
TProfiler是一个可以在生产环境长期使用的性能分析工具。它同时支持剖析和采样两种方式,记录方法执行的时间和次数,生成方法热点、对象创建热点、线程状态分析等数据,为查找系统性能瓶颈提供数据支持。
TProfiler在JVM启动时把时间采集程序注入到字节码中,整个过程无需修改应用源码.运行时会把数据写到日志文件,一般情况下每小时输出的日志小于50M。
业界同类开源产品都不是针对大型Web应用设计的,对性能消耗较大不能长期使,TProfiler解决了这个问题。目前TProfiler已应用于淘宝的核心Java前端系统。
关于工具的使用,详见wiki:https://github.com/alibaba/TProfiler/wiki
定位性能瓶颈
在线客服触屏版使用了websocket,下图是websocket消息发送的流程图:
根据业务流程,选取关键代码对其进行跟踪采样,在使用Tprofiler时需要设置profiler.properties,部分信息如下,其中includePackageStartsWith是指定哪些包需要受到监控。
includePackageStartsWith = org.springframework.messaging.simp.broker;org.springframework.messaging.support;com.alibaba.dubbo.rpc.proxy;net.dwade.livechat.dubbo.service;net.dwade.livechat.touch.client.controller.stomp
excludePackageStartsWith = com.taobao.sketch;org.apache;com.taobao
在 jvm 启动参数中添加-javaagent:/path/tprofiler.jar -Dprofile.properties=/path/profile.properties,然后重启应用。
Tprofiler实践
需要查看Tprofiler的数据,需要先执行flushMethod操作,将内存中的数据写入日志文件中:java -cp /dwade/tomcat/tomcat_touch_test/bin/TProfiler/tprofiler-1.0.1.jar com.taobao.profile.client.TProfilerClient 10.112.172.125 50001,其中50001是Tprofiler的端口,由profiler.properties指定。
然后对线程和方法调用进行统计:java -cp /dwade/tomcat/tomcat_touch_test/bin/TProfiler/tprofiler-1.0.1.jar com.taobao.profile.analysis.SamplerLogAnalysis tsampler.log stat-method.log stat-thread.log,其中tsampler.log是数据样本,stat-method.log、stat-thread.log是统计输出文件。
再对热点方法和对象进行统计:java -cp /dwade/tomcat/tomcat_touch_test/bin/TProfiler/tprofiler-1.0.1.jar com.taobao.profile.analysis.ProfilerLogAnalysis tprofiler.log tmethod.log top-method.log top-object.log,其中tprofiler.log、tmethod.log是数据样本,top-method.log、top-object.log是统计输出文件。
最后得到stat-method.log、stat-thread.log、top-method.log、top-object.log
TProfiler数据分析
线程数据分析
查看stat-thread.log,可以得到线程的状态统计数据,比如哪些线程经常出现阻塞,哪些线程比较繁忙、哪些线程比较空闲等。下面是部分数据:
3562 New I/O client worker #1-19 RUNNABLE 6187
3196 LiveChat-WebSocketSessionManagerThread-7 WAITING 6187
21219 clientInboundChannel-2728 TIMED_WAITING 38
……
23458 clientInboundChannel-2769 BLOCKED 32
……
由上述数据可知,clientInboundChannel-*线程经常出现阻塞,再结合tsampler.log中的线程栈信息,或者使用jstack命令,很快就可以找到该线程阻塞的原因。如下图所示,在DefaultSubscriptionRegistry.java的第269行出现阻塞。
第269行,如果result为null,则出现synchronized锁,因为在accessCache中获取不到数据,为什么呢?因为在第255行中,如果当前size大于cacheLimit,则将最少访问的数据删除。
热点方法分析
查看stat-method.log,可以看到在采样期间目标方法被调用的次数,主要是关注哪些方法用的较多,下面是部分数据:
org.apache.tomcat.util.threads.TaskThread$WrappingRunnable.run(TaskThread.java:61) 77921
org.jboss.netty.util.ThreadRenamingRunnable.run(ThreadRenamingRunnable.java:108) 64885
org.jboss.netty.util.internal.DeadLockProofWorker$1.run(DeadLockProofWorker.java:44) 64885
org.jboss.netty.channel.socket.nio.NioWorker.run(NioWorker.java:163) 64861
org.jboss.netty.channel.socket.nio.SelectorUtil.select(SelectorUtil.java:38) 64861
org.apache.tomcat.util.threads.TaskQueue.take(TaskQueue.java:32) 50518
org.apache.tomcat.util.threads.TaskQueue.take(TaskQueue.java:104) 50518
org.apache.tomcat.util.threads.TaskQueue.poll(TaskQueue.java:86) 26948
org.apache.tomcat.util.threads.TaskQueue.poll(TaskQueue.java:32) 26948
top方法数据分析
查看top-method.log,可以看到目标方法在采样期间的调用总时长、调用次数、平均耗时,下面是部分数据:
net/dwade/livechat/cache/ClientCache:getClientBySessionId:111 120647 9 1051500
net/dwade/livechat/cache/ChatSessionCache:getSessionById:45 105245 9 991919
net/dwade/livechat/cache/MessageCache:saveMessage:122 39860 22 871732
net/dwade/livechat/cache/MessageCache:getMessageById:64 36666 16 596621
net/dwade/livechat/cache/ChatRequestCache:getChatRequest:128 85844 6 477680
整理后的数据如下,具体的信息请参照下方的Excel文档:
livechat-touch-client:
方法信息
样本数
平均时长(ms)
总时长
com/alibaba/dubbo/rpc/proxy/InvokerInvocationHandler:invoke:52
74028
72
5328399
net/dwade/livechat/touch/client/controller/stomp/WebSocketNotifyController:notifyMsg:86
280
42
11851
net/dwade/livechat/touch/client/controller/stomp/WebSocketMessageController:sendMessage:78
73441
0
8166
net/dwade/livechat/touch/client/controller/stomp/WebSocketMessageController:subcribeNewMsg:101
50
0
5
livechat-dubbo:
方法信息
样本数
平均时长
总时长
net/dwade/livechat/cache/TenantCache:getAllTenant:55
3
218
653
net/dwade/livechat/dubbo/service/impl/AgentLoginServiceImpl:getTenantTree:278
3
66
198
net/dwade/livechat/dubbo/service/impl/AgentLoginServiceImpl$$EnhancerByCGLIB$$cab70d84:index:0
3
55
164
net/dwade/livechat/cache/MessageCache:getUnreadMessages:42
4931
40
195852
com/alibaba/dubbo/common/serialize/support/hessian/Hessian2ObjectInput:readObject:94
1
36
36
com/alibaba/dubbo/common/utils/ReflectUtils:desc2classArray:772
1
34
34
net/dwade/livechat/dubbo/service/impl/AgentPullServiceImpl:agentPull:213
1891
27
51062
net/dwade/livechat/dubbo/service/impl/AgentLoginServiceImpl:index:250
3
23
70
net/dwade/livechat/cache/MessageCache:saveMessage:122
39860
22
871732
com/alibaba/dubbo/remoting/exchange/support/DefaultFuture:access$000:44
1
21
21
net/dwade/livechat/cache/MessageCache:getMessageById:64
36666
16
596621
net/dwade/livechat/cache/PropertyEhCache$$EnhancerByCGLIB$$fc5340e1:getExactProperty:0
25
15
372
net/dwade/livechat/cache/ChatRequestCache:addNewChatRequest:68
106
11
1122
net/dwade/livechat/cache/ClientCache:getClientBySessionId:111
120647
9
1051500
性能调优
关于tomcat等中间件的优化,这里不再重复,请另行参考相关的帖子。
减少线程BLOCKED
在线程数据分析章节中,我们分析了spring websocket的高并发场景下的线程阻塞问题。项目中,我们使用了spring默认的消息代理,在SimpleBrokerMessageHandler中有一个致命的弱点,就是使用了DefaultSubscriptionRegistry,而在这个里面又使用了DestinationCache,这是一个基于LinkedHashMap实现的LRU缓存策略,如果缓存大小超过了cacheLimit值,则会去将原有旧的数据清理掉,并且再次读取的时候仍然会去读取所有的订阅地址直到找到符合条件的,涉及到写的部分,都加了synchronized重量级锁,所以在压测的过程中这一块的线程BLOCK也是相对较频繁的,程序的并发性能下降。目前,项目中还未使用消息中间件,后续如果接入消息中间件,对应用系统的性能将会有很大提高。
有以下3种优化方案:
1)、增大cacheLimit值,扩大缓存数据量,减少频繁加锁;
2)、使用spring的BeanPostProcessor,改变SimpleBrokerMessageHandler的subscriptionRegistry实现类,这个方案难度比较大而且还有一定的风险,需要经过严格的测试;
3)、使用消息中间件,spring会将订阅操作交给消息中间件处理,这样便可以避免这个问题。
dubbo代码优化
dubbo日志输出
因为dubbo默认使用的log4j日志实现,但是项目里面使用的是logback,导致dubbo框架内部的日志无法输出(包括ERROR日志),不方便后续定位问题,以下日志是非常重要的:
1)、com.alibaba.dubbo.rpc.RpcException: Invoke remote method timeout
2)、c.a.d.c.t.s.AbortPolicyWithReport - [DUBBO] Thread pool is EXHAUSTED!
解决办法:在tomcat的启动脚本中加下以下jvm参数:-Ddubbo.application.logger=slf4j
cache优化
在top方法数据分析里面,我们得到了dubbo服务的方法执行耗时的统计数据,如下图所示:
MessageCache.getUnreadMessages()、ChatRequestCache.getChatRequest()、ChatSessionCache.getSessionById()等,这些方法有一个共同点就是多次使用了hget方法,如下图所示:
因为会涉及多次数据传输,所以这个方法的性能较差。而JedisCluster中提供了hmset、hmget的api,如下图所示。但是考虑到hmset在并发情况下会有线程安全问题,hset方法可以先不改造,并且目前是读多写少的场景。也就是说,将多个hget方法改造成一个hmget,一次性获取多个字段值,从而提升查询性能。
JVM调优
为了提高系统的吞吐量,减少GC停顿时间,采用并行回收机制,同时为了减轻老年代GC压力,将更多的对象预留在新生代,设置了较大的新生代大小,并且提高了新生代触发GC的域值。
MEM_ARGS="-server -Xmx3550m -Xms3550m -Xmn2g -XX:PermSize=256m -XX:MaxPermSize=256m -XX:TargetSurvivorRatio=80 -XX:+UseParNewGC -XX:ParallelGCThreads=16 -XX:+UseConcMarkSweepGC"
JAVA_OPTS="-XX:+HeapDumpOnOutOfMemoryError -XX:HeapDumpPath=/dwade/livechat/log/jvm/touch-test.dump -Ddubbo.application.logger=slf4j $JAVA_OPTS $MEM_ARGS"
可以使用监控工具对主机资源进行监控,也可以使用常用的一些命令,比如top。另外还需要对GC活动进行监控,使用jstat –gcutil命令可以粗粒度地了解GC活动,也可以使用VisualVM进行可视化监控。
参考资料:
1、https://github.com/alibaba/TProfiler/wiki
2、https://my.oschina.net/jsan/blog/210353
- spring websocket性能调优-TProfiler实践
- spring websocket开发实践
- 性能分析工具 TProfiler
- spring websocket性能测试
- TProfiler
- tProfiler
- JVM 性能调优实战之:使用阿里开源工具 TProfiler 在海量业务代码中精确定位性能代码
- JVM 性能调优实战之:使用阿里开源工具 TProfiler 在海量业务代码中精确定位性能代码
- JVM 性能调优实战之:使用阿里开源工具 TProfiler 在海量业务代码中精确定位性能代码 .
- 性能调优实践总结
- JVM性能调优实践
- [实践]基于WebSocket,Spring开发消息系统后台
- 使用TProfiler分析并调优项目中的Fastjson序列化代码
- Spring WebSocket
- spring websocket
- spring websocket
- WebSocket Spring
- Spring WebSocket
- ReactNative——Iamge的大学问
- 新手使用GreenDao
- PHP基础之数组2
- 机器学习——Python(四)
- 使用Gradle构建SpringBoot工程系列:第七篇:使用spring-boot-devtools开启热部署
- spring websocket性能调优-TProfiler实践
- 字符串问题---判断两个字符串是否为变形词
- 5.4.7—二叉树的递归—Populating Next Right Pointers in Ea Node
- C++--运算符的重载
- 六大Web负载均衡原理与实现
- 谈谈有限域那些事儿
- oracle之 oradebug 命令用法
- 20170816_二叉树的深度
- 消息中间件(MQ)能给我们带来什么