最近的一个线上项目(认证服务器)老是出现服务延迟的情况。具体的问题描述:
(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、减少调用第三方接口的超时时间,让处理线程尽早回归线程池。
(友情提示:本博文章欢迎转载,但请注明出处:hankchen,http://www.blogjava.net/hankchen)
相关推荐
Socket长连接和Netty框架是网络编程中的重要概念,尤其在Android应用开发和服务器通信中扮演着关键角色。本文将深入探讨这两个主题,并通过提供的源码进行实例解析。 首先,让我们了解一下Socket。Socket是网络通信...
基于Netty实现的分布式锁,利用了Netty作为高效的异步事件驱动网络应用框架,可以提供长连接服务,降低网络开销,提高系统的吞吐量和响应速度。 Netty是Java领域的一款优秀网络通信框架,它提供了高度可定制化的...
自定义 Handler 需要继承 ChannelInboundHandlerAdapter 或 ChannelOutboundHandlerAdapter,根据处理的是入站事件(如连接建立、数据接收)还是出站事件(如数据写入、连接关闭)来选择。这些适配器提供了基本的空...
总结来说,Netty的Pipeline机制提供了一种强大的方式来处理网络通信中的各种事件,其顺序管理是确保网络应用正确运行的关键。通过精心设计和测试Pipeline,我们可以构建出高效且可扩展的网络服务。理解并掌握...
总的来说,Netty4 的事件处理传播机制是通过 `ChannelPipeline` 中的处理器链实现的,它确保了事件按照预定义的顺序在各个处理器之间传播。开发者可以通过添加、移除和替换 `ChannelHandler` 来动态调整处理逻辑,以...
Pipeline则是一个处理链,每个连接都有一个自己的Pipeline,其中包含多个处理器(ChannelHandler),这些处理器按顺序处理进出的数据。 ChannelHandlerContext是Netty中的另一个重要概念,它是Handler与Pipeline...
Netty的核心设计理念是基于Reactor模式,这是一种事件驱动的设计模式,它使得Netty能够高效地处理并发连接。在Netty中,EventLoop(事件循环)负责处理I/O事件,而Channel(通道)则代表网络连接,它们是进行数据...
10. **源码分析**:通过对Netty5源码的分析,读者能深入理解Netty内部的工作机制,如如何处理I/O事件、如何调度任务、如何进行数据传输等,这对于优化代码和解决实际问题非常有帮助。 通过阅读《Netty权威指南》第...
1. **异步I/O**:Netty使用非阻塞I/O模型,允许同时处理多个连接,提高了服务器的并发性能。 2. **Channel**:Netty中的Channel代表一个到另一端点的连接,可以进行读写操作。在TCP调试中,你可以通过Channel监控和...
总结来说,这个“netty实现简单的聊天”项目涵盖了Netty框架的基本用法,包括网络连接的建立与管理、消息的编码与解码、事件处理以及Android环境下的兼容性问题。通过学习和实践,我们可以深入理解Netty在网络编程中...
2. **Netty架构**:Netty采用了反应器模式,包含Bootstrap(引导类)、ServerBootstrap(服务器引导类)、Channel(通道)、EventLoop(事件循环)、Pipeline(处理链)等组件,构建了高效的事件驱动模型。...
而Netty采用了一种称为“事件循环”(Event Loop)的设计,通过少量的线程就能处理大量并发连接,极大地提高了系统的性能和效率。 Netty的事件模型基于“Reactor”模式,其中包含了四个主要组件:处理器(Handler)...
ChannelHandler是Netty中的处理逻辑组件,负责处理I/O事件或拦截数据传输。每个Handler都有一个特定的任务,如解码、业务逻辑处理、异常处理等。ChannelPipeline是Handler的容器,它按照添加的顺序执行Handler,形成...
Netty 是基于Future-Listener模型的异步框架,所有的网络操作都是非阻塞的,这使得Netty能够处理大量并发连接,而不会因为阻塞导致性能下降。 9. **Netty 的安全性** Netty 提供了SSL/TLS支持,可以安全地进行...
Reactor 模式允许 Netty 高效地处理大量并发连接,通过非阻塞 I/O 和事件分发机制来实现。在 Netty 中,我们定义 ChannelHandler 来处理网络事件,如连接建立、数据读取和写入等。ChannelPipeline 是 ChannelHandler...
3. **EventLoop**: 事件循环,负责处理I/O事件,Netty通过线程池实现事件的异步处理。 4. **ChannelHandlerContext**: 上下文对象,用于注册和触发ChannelHandler,是处理事件的核心。 5. **ChannelPipeline**: 通道...
- 在 Netty1.zip 中的 demo 可能包括了自定义的 ChannelInboundHandler 和 ChannelOutboundHandler,用于处理入站和出站事件,比如读取数据、写入数据、连接建立和关闭等。 5. **Netty 的线程模型**: - Netty ...
Netty中的ChannelHandlerContext类是事件处理的核心,它提供了发送和接收数据的方法。对于TCP推送功能,我们可以创建一个自定义的ChannelInboundHandler,当有新的数据到达时,该处理器会被调用。 接着,我们谈论...
4. **EventLoop**:EventLoop 是 Netty 的事件循环,负责处理 I/O 事件并调度处理器链中的任务。Netty 使用多线程模型,每个 EventLoop 负责一组 Channel,确保高效并发处理。 在"ChatNetty"这个例子中,我们将创建...
当接收到数据时,Netty会按照添加的顺序逐个调用处理器。这样,我们可以根据协议头信息决定数据应由哪个处理器进行解析。 5. **DataRecvCenterByNetty**: 这个文件名可能是项目的核心类,它实现了DTU数据接收中心...