ESFramework 经验分享 -- 故障排查:服务端性能瓶颈
如果基于ESFramework开发的服务端程序在实际运行时,没有达到预期的性能(如在线人数到达一个值后就很难再增加了,或者吞吐量上不去等等 ),那么该如何定位问题了?首先,我们要对ESFramework的核心机制有个大概的了解,这对服务端的整个消息处理流程与模型能有个整体的把控,可以参见这篇文章:ESFramework 开发手册(11) -- 服务端信息处理模型 。
一. 决定吞吐量的核心因素
虽然,决定吞吐量的因素有很多,但最首要也是最核心的因素就是消息处理的快慢(HandleInformation / HandleQuery方法执行的耗时) 。
HandleInformation / HandleQuery 返回得越快,每秒处理的消息数量就越大,即吞吐量也越大。
二. 生产 - 消费 失配
我们可以使用生产-消费模型来分析消息,从网络接收到消息,相当于生产消息,HandleInformation / HandleQuery方法的执行相当于是消费消息。
当生产消息的速度小于或等于消息消费的速度时,服务端的业务处理是从容不迫的。
但是,当生产消息的速度大于或远远大于消息消费的速度时,可以想想会发生什么状况?我们简单推导一下:
(1)线程池中可用的线程数会越来越少(IocpDirectly模型)或任务队列中积累的待处理消息越来越多、内存消耗越来越大(TaskQueue模型)。
(2)而在客户端看来,请求响应的时间越来越长。
(3)如果某些HandleInformation / HandleQuery调用在执行到某个地方时,卡死了,那么该调用所占用的线程将一直无法被归还给线程池。
所以,观察线程池中的可用IOCP线程数或任务队列中的等待处理的消息数,就可以发现生产-消费失配的情形。
三. 发现性能瓶颈
压力测试是发现性能问题的最直接方法,如果结合ESFramework提供的服务端性能诊断功能,就更容易发现问题了。
具体的压力测试要怎么做了?总体上而言,我们可以将基于ESFramework开发的服务端程序分为两层:通信层和信息处理层(包括业务逻辑处理、数据库操作等)。 任何一层出问题都将成为性能的短板:
(1)如果通信层每秒最多只能接收数百个消息,而信息处理层每秒可以处理上万个消息,那问题出在通信层。
(2)反过来,如果通信层每秒能接收上万个消息,而信息处理层每秒最多可以处理数百个消息,那问题就出在信息处理层。
(3)还有一种比较少见的情况,就是通信层和信息处理层的单独压力测试的效果都很好,但是组合到一起后,效果却不理想。
我们可以分别对这三个方面进行针对性的压力测试:单纯的通信层压力测试、单纯的信息处理层压力测试、两层组合后的压力测试(即对服务端整体进行压力测试),按照这个顺序测下来,肯定可以找到性能问题的根源。
1.单纯的通信层压力测试
通信层由ESFramework负责,而正确、高效、稳定地收发消息是ESFramework的优点,做得更好则是我们持续努力的目标,单纯通信层的压力测试可以参见ESFramework 4.0 性能测试。
2.单纯的信息处理层压力测试
信息处理层是基于ESFramework进行二次开发的业务逻辑部分,如何撇开通信层来对其进行单纯的压力测试了?
(1)二次开发时,我们会实现ICustomizeHandler接口,服务端收到的所有消息都会提交给该接口的HandleInformation和HandleQuery方法来处理。
所以,可以将这两个方法看作是信息处理层的总入口,我们只要调用这两个方法,就可以驱动整个信息处理层。
(2)由于撇开了通信层,所以,我们需要模拟消息来调用总入口的两个方法。注意:模拟的消息应该尽可能地能与系统实际运行时接收的消息状况一致。
(3)使用多个线程(如20个、50个 、100个、500个)模拟多个客户端同时发消息。在这多个线程中,分别循环创建模拟的消息,并调用HandleInformation或HandleQuery方法。
(4)统计每秒处理的消息个数。即用模拟的消息调用HandleInformation或HandleQuery方法成功返回一次,就将完成数加1,并每隔一秒就小计一次,如此就得出了信息处理层的处理能力。
3.服务端整体压力测试
只有当前面两层的压力测试都达到了我们的预期效果时,做整体压力测试才更有意义。服务端整体压力测试,最接近于系统实际运行的情况。即运行完整的服务端程序,然后大量的客户端连接上来,进行测试。
四.整体压力测试经验分享
就我们以往基于ESFramework开发C/S系统的诸多经验而言,对于整体压力测试,感觉最好用的是使用日志记录的方式,将一些重要的数据记录下来,之后进行详细分析以发现前两个压力测试没有暴露的更为隐蔽的问题。我们建议:
1.开启服务端性能诊断功能
参考ESFramework 开发手册(12) -- 服务端性能诊断这篇文章所描述的,将诊断功能开启,并将诊断信息记录到日志文件中。
2. 记录用户上下线日志
(1)预定IUserManager的SomeOneConnected事件,记录用户的ID及上线时间。
(2)预定IUserManager的SomeOneDisconnected事件,记录用户的ID、下线时间、以及连接断开的原因(对应事件的参数DisconnectedType)。
3. 如何记录日志?
(1) 方法执行的耗时(精确到毫秒)记录,可以使用Stopwatch类,其比直接使用DateTime更准确。
(2) 日志记录可以直接在服务端的运行目录下生成一个txt日志文件。
(3) 为日志记录的启用/禁用加上一个开关控制,可以通过配置来开启或关闭日志记录功能。
(4) 如果没有可复用的写txt文件的类,可以考虑ESBasic.dll提供的ESBasic.Loggers.FileAgileLogger类,使用其LogWithTime方法即可。
------ 详细分析服务端记录的日志信息,90%以上的性能问题应该都可以被发现,然后,就可以进行针对性的优化。
五. 技术顾问服务
如果通过以上步骤的排查,还是找不到问题所在,那我们可以为您提供技术顾问性质的有偿服务,该服务将针对您项目的实际情况(我们会深入了解项目的业务需求和现有源码实现等细节信息),提供专业的更具针对性的排查指导和性能优化建议。
----------------------------------------------------------------------------------------------------------------------------
Q Q:168757008