自开发SocketServer阻塞导致处理能力低 现象
在性能测试时,用jmeter往应用系统发送请求,项目组自开发了socketserver接收请求,是BIO模型的。在测试时发现,随着并发增大,响应时间线性增长,但TPS达到一定程度就不增长了。而应用服务器和数据库服务器的资源消耗都不大,低于50%。而从服务器抓取的日志,每笔业务在应用服务器处理耗时在20毫秒左右。
分析
业务的请求路径是"ucr---电子回单应用服务器---电子回单数据库服务器---电子回单应用服务器---ucr",整个交易的耗时随着压力增大线性增长。从应用服务器日志分析服务端的处理时间相对平稳,基本是在20毫秒左右,也就是“电子回单应用服务器---电子回单数据库服务器---电子回单应用服务器”这部分处理是20毫秒,那么时间主要消耗在"ucr---电子回单应用服务器“往返路径中。
考虑到是项目组自开发的socketserver,并且是BIO的,分析代码:
server = new serversocket(port)这是创建一个新的serversocket对象,socket socket = server.accept() 让这个serversocket对象阻塞监听客户端连接的指定的端口,一但有新的连接请求则做出处理。此时只要来一个连接服务端就阻塞做出处理,包括增加线程数、生成新的线程、启动线程等步骤,等到处理结束之后重新处于监听端口状态,要是又来一个新的连接请求则......一直往复处理动作。明显的缺点是效率低,每次只能处理一个请求,并且是阻塞处理状态。只有接收了请求,才会打印相应日志如:“客户端。。。。”,接收请求的时候是单线程。所以随着并发增大,大量的请求在排队堵塞在server.accept() 之前,而响应时间的计算是从压力机发起请求到接收到返回报文后的全部时间。
优化
- bio中开启多线程,每来一个请求服务端就开启一个线程处理。(优点是可以提高效率,但是缺点也很明显:优点大量几乎同时入站连接可能导致它生成极大数量的线程。最终,java虚拟机会消耗内存二崩溃)。
- 对于上面的方案做出改进:bio中使用线程池管理线程。(可以处理效率同时避免了服务器因为高并发的连接而崩溃)。
- 使用nio。(nio虽然也是单个线程去处理连接请求,但是不会阻塞,会不停地轮询是否有就绪的事件,所以处理的顺序和连接请求的先后顺序无关,与请求数据到来的先后顺序有关。一个线程中就可以调用多路复用接口(select)阻塞同时监听来自多个客户端的io请求,一旦有收到io请求就调用对应函数处理)