from http://ggsonic.iteye.com/blog/1160827
一切从ServerBootstrap开始
ServerBootstrap负责初始话netty服务器,并且开始监听端口的socket请求。
Java代码
bootstrap bootstrap = new ServerBootstrap(
new NioServerSocketChannelFactory(
Executors.newCachedThreadPool(),//boss线程池
Executors.newCachedThreadPool()//worker线程池
)
);
bootstrap.setPipelineFactory(new HttpChannelPipelineFactory());
bootstrap.setOption("child.tcpNoDelay", true);
bootstrap.setOption("child.keepAlive", true);
bootstrap.bind(new InetSocketAddress(httpPort));//端口开始监听
ServerBootstrap用一个ServerSocketChannelFactory 来实例化。ServerSocketChannelFactory 有两种选择,一种是NioServerSocketChannelFactory,一种是OioServerSocketChannelFactory。前者使用NIO,后则使用普通的阻塞式IO。它们都需要两个线程池实例作为参数来初始化,一个是boss线程池,一个是worker线程池。
ServerBootstrap.bind(int)负责绑定端口,当这个方法执行后,ServerBootstrap就可以接受指定端口上的socket连接了。一个ServerBootstrap可以绑定多个端口。
boss线程和worker线程
可以这么说,ServerBootstrap监听的一个端口对应一个boss线程,它们一一对应。比如你需要netty监听80和443端口,那么就会有两个boss线程分别负责处理来自两个端口的socket请求。在boss线程接受了socket连接求后,会产生一个channel(一个打开的socket对应一个打开的channel),并把这个channel交给ServerBootstrap初始化时指定的ServerSocketChannelFactory来处理,boss线程则继续处理socket的请求。
ServerSocketChannelFactory则会从worker线程池中找出一个worker线程来继续处理这个请求。
如果是OioServerSocketChannelFactory的话,那个这个channel上所有的socket消息消息,从开始到channel(socket)关闭,都只由这个特定的worker来处理,也就是说一个打开的socket对应一个指定的worker线程,这个worker线程在socket没有关闭的情况下,也只能为这个socket处理消息,无法服务器他socket。
如果是NioServerSocketChannelFactory的话则不然,每个worker可以服务不同的socket或者说channel,worker线程和channel不再有一一对应的关系。
显然,NioServerSocketChannelFactory只需要少量活动的worker线程及能很好的处理众多的channel,而OioServerSocketChannelFactory则需要与打开channel等量的worker线程来服务。
线程是一种资源,所以当netty服务器需要处理长连接的时候,最好选择NioServerSocketChannelFactory,这样可以避免创建大量的worker线程。在用作http服务器的时候,也最好选择NioServerSocketChannelFactory,因为现代浏览器都会使用http keepalive功能(可以让浏览器的不同http请求共享一个信道),这也是一种长连接。
worker线程的生命周期(life circle)
当某个channel有消息到达或者有消息需要写入socket的时候,worker线程就会从线程池中取出一个。在worker线程中,消息会经过设定好的ChannelPipeline处理。ChannelPipeline就是一堆有顺序的filter,它分为两部分:UpstreamHandler和DownStreamHandler。本文着重介绍netty的线程模型,所以关于pipeline的内容从简说明。
客户端送入的消息会首先由许多UpstreamHandler依次处理,处理得到的数据送入应用的业务逻辑handler,通常用SimpleChannelUpstreamHandler来实现这一部分。
Java代码
public class SimpleChannelUpstreamHandler{
public void messageReceived(ChannelHandlerContext c,MessageEvent e) throws Exception{
//业务逻辑开始掺入
}
}
对于Nio当messageReceived()方法执行后,如果没有产生异常,worker线程就执行完毕了,它会被线程池回收。业务逻辑hanlder会通过一些方法,把返回的数据交给指定好顺序的DownStreamHandler处理,处理后的数据如果需要,会被写入channel,进而通过绑定的socket发送给客户端。这个过程是由另外一个线程池中的worker线程来完成的。
对于Oio来说,从始到终,都是由一个指定的worker来处理。
减少worker线程的处理占用时间
worker线程是由netty内部管理,统一调配的一种资源,所以最好应该尽快的把让worker线程执行完毕,返回给线程池回收利用。worker线程的大部分时间消耗在在ChannelPipeline的各种handler中,而在这些handler中,一般是负责应用程序业务逻辑掺入的那个handler最占时间,它通常是排在最后的UpstreamHandler。所以通过把这部分处理内容交给另外一个线程来处理,可以有效的减少worker线程的周期循环时间。一般有两种方法:
messageReceived()方法中开启一个新的线程来处理业务逻辑
Java代码
public void messageReceived(ChannelHandlerContext ctx, MessageEvent e) throws Exception{
...
new Thread(...).start();
}
在messageReceived()中开启一个新线程来继续处理业务逻辑,而worker线程在执行完messageReceived()就会结束了。更加优雅的方法是另外构造一个线程池来提交业务逻辑处理任务。
利用netty框架自带的ExecutionHandler
基本使用方法
Java代码
public class DatabaseGatewayPipelineFactory implements ChannelPipelineFactory {
private final ExecutionHandler executionHandler;
public DatabaseGatewayPipelineFactory(ExecutionHandler executionHandler) {
this.executionHandler = executionHandler;
}
public ChannelPipeline getPipeline() {
return Channels.pipeline(
new DatabaseGatewayProtocolEncoder(),
new DatabaseGatewayProtocolDecoder(),
executionHandler, // 多个pipeline之间必须共享同一个ExecutionHandler
new DatabaseQueryingHandler());//业务逻辑handler,IO密集
}
}
把共享的ExecutionHandler实例放在业务逻辑handler之前即可,注意ExecutionHandler一定要在不同的pipeline之间共享。它的作用是自动从ExecutionHandler自己管理的一个线程池中拿出一个线程来处理排在它后面的业务逻辑handler。而worker线程在经过ExecutionHandler后就结束了,它会被ChannelFactory的worker线程池所回收。
它的构造方法是ExecutionHandler(Executor executor) ,很显然executor就是ExecutionHandler内部管理的线程池了。netty额外给我们提供了两种线程池:
MemoryAwareThreadPoolExecutor和OrderedMemoryAwareThreadPoolExecutor,它们都在org.jboss.netty.handler.execution 包下。
MemoryAwareThreadPoolExecutor确保jvm不会因为过多的线程而导致内存溢出错误,OrderedMemoryAwareThreadPoolExecutor是前一个线程池的子类,除了保证没有内存溢出之外,还可以保证channel event的处理次序。具体可以查看API文档,上面有详细说明
分享到:
相关推荐
**Netty线程模型和EventLoop** Netty的线程模型是基于NIO(非阻塞I/O)的EventLoop设计。EventLoop是Netty的核心组件,它是一个单线程执行任务的循环,负责处理I/O事件并分发到对应的处理器。每个EventLoop都包含一...
本资料包"05.Netty线程模型.rar"主要探讨了Netty如何有效地管理和调度线程,以实现高效的网络通信。 Netty的线程模型基于NIO(非阻塞I/O)的概念,其核心组件包括BossGroup、WorkerGroup以及EventLoop。BossGroup...
【Netty线程模型源码剖析】 Netty是一款高性能、异步事件驱动的网络应用程序框架,广泛应用于分布式系统、微服务、游戏服务器等领域。它的线程模型是其高性能的关键因素之一。Netty采用主从Reactor线程模型,旨在...
《深入Hotspot源码与Linux内核理解NIO与Netty线程模型》这份资料主要探讨了Java的Hotspot虚拟机源码、Linux内核、非阻塞I/O(NIO)以及Netty框架的线程模型。这些知识点在构建高性能、高并发的网络应用中至关重要。 ...
这是一本详细介绍了netty线程模型的书籍,包括 nio核心
Netty线程模型和EventLoop线程模型概述EventLoop事件循环任务调度JDK任务调度EventLoop任务调度线程管理线程分配非阻塞传输阻塞传输N
从这篇文章中,大家可以学习到如下知识:什么是I/O多路复用Reactor三种线程模型Netty线程模型NioEventLoop源码分析JDKepollbug学习I/O多路复用之前,我们先来了解如下几个概念:阻塞I/O:客户端从socket中读取数据或...
Netty服务器线程模型概览_线程模型
本文将详细介绍Reactor的三种线程模型,并结合Netty的线程模型进行解析。 ### 1. Reactor 单线程模型 Reactor单线程模型是指所有的I/O操作都在同一个NIO线程中完成。这个线程负责接收客户端连接、发起连接、读取...
在第五课“Netty线程模型源码分析(二)”中,我们可能会深入探讨以下几个方面: 1. **NIO(非阻塞I/O)基础**:Netty是基于Java NIO构建的,因此理解NIO的基本原理至关重要。NIO允许我们进行非阻塞读写,提高了...
"第四课netty线程模型源码分析(一)"则深入探讨了Netty的线程模型。Netty使用EventLoopGroup作为其多线程模型的基础,通常包含一个BossGroup负责接受新的连接,而多个WorkerGroup则处理已连接的通道上的读写事件。每...
### Netty线程模型 #### Java线程模型的演进 - **单线程**:早期简单的应用程序可能仅使用一个主线程完成所有任务,这种方式简单但无法充分利用多核CPU资源。 - **多线程**:随着计算机硬件的发展,多线程成为提高...
在深入探讨Netty的Reactor线程模型源码之前,我们需要先理解Reactor模式的基本概念。 Reactor模式是一种设计模式,常用于处理并发I/O事件。在多路复用I/O(如epoll、kqueue)中,Reactor模式是关键组成部分,它负责...
总的来说,Netty 是一个强大且灵活的网络通信框架,通过其高效的线程模型、易于使用的 API 和丰富的功能,极大地降低了开发网络应用的复杂性,提高了系统的性能和可扩展性。无论是在微服务架构、游戏后端,还是...
Netty线程模型 * Thread-based architecture:一个请求对应一个线程。 * Event-driven architecture:定义一系列的事件处理器来响应事件的发生,并且将服务端接受连接与对事件的处理分离。 * Reactor模式:处理多个...
#### Netty线程模型 Netty的线程模型是其高性能的关键之一。Netty采用了Reactor模式来处理事件,这一模式分为几种不同的形式: 1. **单线程模型**:所有I/O操作都由一个线程完成。这种模型简单但不适合高并发场景...