Q?Q客L(fng)发送一个请求AQ长q接Q,在服务器端的业务层需?0U以上才能接收到?/p>
Q?Q客L(fng)发送一个请求BQ端q接Q,在服务器端的业务层可以迅速接收到?/p>
从现象大致知道问题出在服务器端的|络接收层,大量通过长连接发送过来的h都堵塞在|络层得不到处理Q在|络层排队,q没到应用层Q?/p>
Q友情提C:(x)本博文章Ƣ迎转蝲Q但h明出处:(x)hankchenQ?a href="http://www.aygfsteel.com/hankchen">http://www.aygfsteel.com/hankchenQ?/strong>
后来l过排查Q发现是Netty中的OrderedMemoryAwareThreadPoolExecutor原因。相关代码如下:(x)
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;
}
先介l下背景知识Q再来分析问题?/p>
大家都知道,Netty是一个基于事件的NIO框架。在Netty中,一切网l动作都是通过事g来传播ƈ处理的,例如QChannel诅RChannel写等{。回忆下Netty的流处理模型Q?/p>
BossU程Q一个服务器端口对于一个)(j)---接收到客L(fng)q接---生成Channel---交给WorkU程池(多个WorkU程Q来处理?/strong>
具体的WorkU程---d已接收的数据到ChannelBuffer---触发ChannelPipeline中的ChannelHandler链来处理业务逻辑?/strong>
注意Q执行ChannelHandler铄整个q程是同步的Q如果业务逻辑的耗时较长Q会(x)导致WorkU程长时间被占用得不到释放,从而媄(jing)响了整个服务器的q发处理能力?/p>
所以,Z提高q发敎ͼ一般通过ExecutionHandlerU程池来异步处理ChannelHandler链(workerU程在经qExecutionHandler后就l束了,它会(x)被ChannelFactory的workerU程池所回收Q。在Netty中,只需要增加一行代码:(x)
publicChannelPipeline
getPipeline() { returnChannels
.pipeline( new DatabaseGatewayProtocolEncoder(), new DatabaseGatewayProtocolDecoder(), executionHandler, // Must be shared new DatabaseQueryingHandler());
}
例如Q?/pre>ExecutionHandler
executionHandler = newExecutionHandler
( newOrderedMemoryAwareThreadPoolExecutor
(16, 1048576, 1048576))
对于ExecutionHandler需要的U程池模型,Netty提供了两U可选:(x)
1Q?MemoryAwareThreadPoolExecutor 通过对线E池内存的用控Ӟ可控制Executor中待处理d的上限(过上限Ӟ后箋q来的Q务将被阻塞)(j)Qƈ可控制单个Channel待处理Q务的上限Q防止内存溢出错误;
2Q?OrderedMemoryAwareThreadPoolExecutor ?MemoryAwareThreadPoolExecutor 的子cR除了MemoryAwareThreadPoolExecutor 的功能之外,它还可以保证同一Channel中处理的事g的序性,q主要是控制事g在异步处理模式下可能出现的错误的事g序Q但它ƈ不保证同一Channel中的事g都在一个线E中执行Q通常也没必要Q?
例如Q?pre>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) --->
上图表达的意思有几个Q?
Q?Q对整个U程池而言Q处理同一个Channel的事Ӟ必须是按照顺序来处理的。例如,必须先处理完Channel A (Event A1) Q再处理Channel A (Event A2)、Channel A (Event A3)
Q?Q同一个Channel的多个事Ӟ?x)分布到U程池的多个U程中去处理?
Q?Q不同Channel的事件可以同时处理(分担到多个线E)(j)Q互不媄(jing)响?nbsp;
OrderedMemoryAwareThreadPoolExecutor 的这U事件处理有序性是有意义的Q因为通常情况下,h发送端希望服务器能够按照顺序处理自qhQ特别是需要多ơ握手的应用层协议。例如:(x)XMPP协议?
现在回到具体业务上来Q我们这里的认证服务也用了OrderedMemoryAwareThreadPoolExecutor。认证服务的其中一个环节是使用长连接,不断处理来自另外一个服务器的认证请求。通信的数据包都很,一般都?00个字节以内。一般情况下Q处理这个过E很快,所以没有什么问题。但是,׃认证服务需要调用第三方的接口,如果W三Ҏ(gu)口出现gq,导致这个过E变慢。一旦一个事件处理不完,׃要保持事件处理的有序性,其他事g全部堵塞了Q而短q接之所以没有问题,是因为短q接一个Channel׃个请求数据包Q处理完Channel关闭了Q根本不存在序的问题,所以在业务层可以迅速收到请求,只是׃同样的原因(W三Ҏ(gu)口)(j)Q处理时间会(x)比较ѝ?
其实Q认证过E都是独立的h数据包(单个帐号Q,每个h数据包之间是没有M关系的,保持q样的顺序没有意义!
最后的改进措施Q?
1、去掉OrderedMemoryAwareThreadPoolExecutorQ改用MemoryAwareThreadPoolExecutor?
2、减调用第三方接口的超时时_(d)让处理线E尽早回归线E池?
Q友情提C:(x)本博文章Ƣ迎转蝲Q但h明出处:(x)hankchenQ?a href="http://www.aygfsteel.com/hankchen">http://www.aygfsteel.com/hankchenQ?/strong>