转载自:http://www.blogjava.net/hankchen
Netty长连接的事件处理顺序问题
博客分类: 网络开发+Mina+Netty
最近的一个线上项目(认证服务器)老是出现服务延迟的情况。具体的问题描述:
(1)客户端发送一个请求A(长连接),在服务器端的业务层需要20秒以上才能接收到。
(2)客户端发送一个请求B(端连接),在服务器端的业务层可以迅速接收到。
从现象大致知道问题出在服务器端的网络接收层,大量通过长连接发送过来的请求都堵塞在网络层得不到处理(在网络层排队,还没到应用层)。
(友情提示:本博文章欢迎转载,但请注明出处:hankchen,http://www.blogjava.net/hankchen)
后来经过排查,发现是Netty中的OrderedMemoryAwareThreadPoolExecutor原因。相关代码如下:
MemoryAwareThreadPoolExecutor executor = new OrderedMemoryAwareThreadPoolExecutor(threadNums, maxChannelMemorySize,
maxTotalMemorySize, keepAliveTime,
TimeUnit.SECONDS);
ExecutionHandler executionHandler = new ExecutionHandler(executor);
public ChannelPipeline getPipeline() throws Exception
{
ChannelPipeline pipeline = pipeline();
pipeline.addLast("decoder", new AuthDecoder());
pipeline.addLast("encoder", new AuthEncoder());
pipeline.addLast("executor", executionHandler);
pipeline.addLast("handler", new AuthServerHandler(commandFactory));
return pipeline;
}
先介绍下背景知识,再来分析问题。
大家都知道,Netty是一个基于事件的NIO框架。在Netty中,一切网络动作都是通过事件来传播并处理的,例如:Channel读、Channel写等等。回忆下Netty的流处理模型:
Boss线程(一个服务器端口对于一个)---接收到客户端连接---生成Channel---交给Work线程池(多个Work线程)来处理。
具体的Work线程---读完已接收的数据到ChannelBuffer---触发ChannelPipeline中的ChannelHandler链来处理业务逻辑。
注意:执行ChannelHandler链的整个过程是同步的,如果业务逻辑的耗时较长,会将导致Work线程长时间被占用得不到释放,从而影响了整个服务器的并发处理能力。
所以,为了提高并发数,一般通过ExecutionHandler线程池来异步处理ChannelHandler链(worker线程在经过ExecutionHandler后就结束了,它会被ChannelFactory的worker线程池所回收)。在Netty中,只需要增加一行代码:
public ChannelPipeline getPipeline() {
return Channels.pipeline(
new DatabaseGatewayProtocolEncoder(),
new DatabaseGatewayProtocolDecoder(),
executionHandler, // Must be shared
new DatabaseQueryingHandler());
}
例如:
ExecutionHandler executionHandler = new ExecutionHandler(
new OrderedMemoryAwareThreadPoolExecutor(16, 1048576, 1048576))
对于ExecutionHandler需要的线程池模型,Netty提供了两种可选:
1) MemoryAwareThreadPoolExecutor 通过对线程池内存的使用控制,可控制Executor中待处理任务的上限(超过上限时,后续进来的任务将被阻塞),并可控制单个Channel待处理任务的上限,防止内存溢出错误;
2) OrderedMemoryAwareThreadPoolExecutor 是 MemoryAwareThreadPoolExecutor 的子类。除了MemoryAwareThreadPoolExecutor 的功能之外,它还可以保证同一Channel中处理的事件流的顺序性,这主要是控制事件在异步处理模式下可能出现的错误的事件顺序,但它并不保证同一Channel中的事件都在一个线程中执行(通常也没必要)。
例如:
Thread X: --- Channel A (Event A1) --. .-- Channel B (Event B2) --- Channel B (Event B3) --->
\ /
X
/ \
Thread Y: --- Channel B (Event B1) --' '-- Channel A (Event A2) --- Channel A (Event A3) --->
上图表达的意思有几个:
(1)对整个线程池而言,处理同一个Channel的事件,必须是按照顺序来处理的。例如,必须先处理完Channel A (Event A1) ,再处理Channel A (Event A2)、Channel A (Event A3)
(2)同一个Channel的多个事件,会分布到线程池的多个线程中去处理。
(3)不同Channel的事件可以同时处理(分担到多个线程),互不影响。
OrderedMemoryAwareThreadPoolExecutor 的这种事件处理有序性是有意义的,因为通常情况下,请求发送端希望服务器能够按照顺序处理自己的请求,特别是需要多次握手的应用层协议。例如:XMPP协议。
现在回到具体业务上来,我们这里的认证服务也使用了OrderedMemoryAwareThreadPoolExecutor。认证服务的其中一个环节是使用长连接,不断处理来自另外一个服务器的认证请求。通信的数据包都很小,一般都是200个字节以内。一般情况下,处理这个过程很快,所以没有什么问题。但是,由于认证服务需要调用第三方的接口,如果第三方接口出现延迟,将导致这个过程变慢。一旦一个事件处理不完,由于要保持事件处理的有序性,其他事件就全部堵塞了!而短连接之所以没有问题,是因为短连接一个Channel就一个请求数据包,处理完Channel就关闭了,根本不存在顺序的问题,所以在业务层可以迅速收到请求,只是由于同样的原因(第三方接口),处理时间会比较长。
其实,认证过程都是独立的请求数据包(单个帐号),每个请求数据包之间是没有任何关系的,保持这样的顺序没有意义!
最后的改进措施:
1、去掉OrderedMemoryAwareThreadPoolExecutor,改用MemoryAwareThreadPoolExecutor。
2、减少调用第三方接口的超时时间,让处理线程尽早回归线程池。
分享到:
相关推荐
NULL 博文链接:https://gjp014.iteye.com/blog/2390925
netty的长连接.. 连接断开之后 自动重连demo
Netty4长连接、断开重连、心跳检测、Msgpack编码解码 http://blog.csdn.net/giousa/article/details/72846303#t2
netty的简介,netty是一种socket技术,实现长连接,此技术更加成熟
里面有android前端和java服务器源码,主要演示socket长连接netty框架的简单使用,想学习socket的同学可以下来看看,
Netty通过WebSocket编程实现服务器和客户端长连接Demo
1、下载后导入eclipse(maven工程) 2、先运行ServerTest 3、再运行ClientTest,即可看到输出结果 4、保证可用,亲手编写,欢迎大家指正不足
用netty实现长连接和心跳监测的示例代码
java作为服务端,websocket利用netty做连接,解压之后内有html前端,把项目导入eclipse,在main方法启动即可使用。
具体信息查看我的博客:https://blog.csdn.net/qq_37437983/article/details/86585079
netty-leanring netty长连接复用的demo
概述 在实际的生产项目中,尤其是soa架构的系统。...netty简单的长连接 pom文件 <groupId>org.springframework.boot <artifactId>spring-boot-starter-parent <version>1.5.1.RELEASE <!--
Netty实现长连接通讯,连接协议为了简单json封装,如果需要协议变化可以自己封装,有问题可以联系邮箱:lmjlimj@foxmail.com
使用netty4,客户端断线重连,使用message收发数据,重写方法
Netty中使用WebSocket实现服务端与客户端的长连接通信发送消息示例代码;Netty中使用WebSocket实现服务端与客户端的长连接通信发送消息示例代码;Netty中使用WebSocket实现服务端与客户端的长连接通信发送消息示例代码
分布式锁,基于Netty长连接实现,自定义协议,高性能锁,内置等待队列,服务下线立刻感知,无需等待超时.支持优雅关闭.规避Redis分布式锁缺陷
Netty测试代码,尤其对于Pipeline顺序
这是一个java web项目集成了netty websocket的...初始化握手对象时指定了maxFramePayloadLength 的长度、以及通过配置netty内置解码器处理数据半包等方法,均无效。以下是终极解决办法,供大家参考和解决这样的问题。
netty心跳连接代码
Netty4事件处理传播机制,java高级开发工程师要求(csdn)————程序