您的位置:首页 > 其它

Netty线程模型

2017-11-03 11:37 555 查看
Reactor模型
单线程Reactor

多线程Reactor

主从Reactor

Netty线程模型

Reactor模型

一般编写的网络框架以IO操作为主,将IO操作分割为相对独立的事件模块。大多数都是基于Reactor模式进行设计和开发,Reactor模式基于事件驱动,特别适合处理海量的I/O事件。

1 单线程Reactor

Reactor单线程模型:所有的IO操作都在同一个NIO线程上面完成,NIO线程的职责如下:

作为NIO服务端,接收客户端的TCP连接;

作为NIO客户端,向服务端发起TCP连接;

读取通信对端的请求或者应答消息;

向通信对端发送消息请求或者应答消息。



Reactor模式使用的是异步非阻塞IO,所有的IO操作都不会导致阻塞,理论上一个线程可以独立处理所有IO相关的操作(JDK类库Selector即为该线程模型)。然后在并发场景下,某些IO事件cpu耗时较长,在单线程环境下线程阻塞导致其他任务等待,导致系统性能较差。

2 多线程Reactor

Reactor多线程模型:Acceptor线程(NIO)用于监听服务端,接收客户端的TCP连接请求;网络IO操作-读、写等由一个NIO线程池负责;单个NIO线程可以同时处理N条链路,但是1个链路只对应1个NIO线程,防止发生并发操作问题。



线程池采用多线程处理IO事件,基本解决单个IO时间阻塞导致系统性能下降的问题。然而,并发百万客户端连接,或者服务端对客户端握手进行安全认证,但是认证本身非常损耗性能在,同时单个线程accept处理客户端请求将导致系统延迟,或客户端无法接入等问题。

3 主从Reactor

主从Reactor线程模型:服务端采用独立NIO线程池接收客户端连接。Acceptor接收到客户端TCP连接请求处理完成后,将新创建的SocketChannel注册到IO线程池的某个IO线程上,由它负责SocketChannel的读写和编解码工作。Acceptor线程池仅仅只用于客户端的登陆、握手和安全认证,链路建立成功,就将链路注册到后端IO线程池的IO线程上,由IO线程负责后续的IO操作。



通常而言,建议将Accept线程池线程数设置为1,在监听多端口、多种协议等情绪下,才将线程数配置;IO TheadPool 线程次建议线程数一般为(cpu核数+1~2*cpu核数)。

Netty线程模型

作为高并发网络编程框架,Netty线程为主从Reactor模型,其中Accpet Thread负责接收客户端连接,IO ThreadPool负责处理读、写等IO事件。Netty以串行化设计理念,避免任务在多线程并发过程中额外同步操作,将任务绑定与单个线程处理。一个IO事件由单个Netty NIO线程去处理,不过单个NIO线程可以串行化处理一系类事件。

给出一段Netty开发中服务端代码示例:

EventLoopGroup bossGroup = new NioEventLoopGroup();
EventLoopGroup workGroup = new NioEventLoopGroup();
ServerBootstrap b = new ServerBootstrap();
b.group(bossGroup, workGroup).channel(NioServerSocketChannel.class)
.localAddress(new InetSocketAddress(port))
.childHandler(new ChannelInitializer<Channel>() {
@Override
protected void initChannel(Channel ch) throws Exception {
ChannelPipeline channelPipeline = ch.pipeline();
ch.pipeline().addLast(
new ChannelInboundHandlerAdapter() {
@Override
public void channelActive(
ChannelHandlerContext ctx)
throws Exception {
ctx.writeAndFlush(buf.duplicate())
.addListener(
ChannelFutureListener.CLOSE);
}
});
}
});
ChannelFuture f = b.bind().sync();
f.channel().closeFuture().sync();


通常,bossGroup线程组负责处理客户端连接,请求,认证;workGroup负责处理bossGroup认证通过的客户端IO事件。NioEventLoopGroup作为线程池,默认初始化线程数为Cpu核数*2;

DEFAULT_EVENT_LOOP_THREADS = Math.max(1, SystemPropertyUtil.getInt(
"io.netty.eventLoopThreads", NettyRuntime.availableProcessors() * 2));


NioEventLoopGroup线程次中又由一系列(2*cpu核数)NioEventLoop线程组成,每次任务到来时,设计基于轮询的负载均衡策略将任务分配NioEventLoop,代码如下:

private static final class PowerOfTwoEventExecutorChooser implements EventExecutorChooser {
private final AtomicInteger idx = new AtomicInteger();
private final EventExecutor[] executors;
PowerOfTwoEventExecutorChooser(EventExecutor[] executors) {
this.executors = executors;
}
@Override//当executors.length =2的n次幂
public EventExecutor next() {
return executors[idx.getAndIncrement() & executors.length - 1];
}
@Override//当executors.length !=2的n次幂
public EventExecutor next() {
return executors[Math.abs(idx.getAndIncrement() % executors.length)];
}
}


如下图为NioEventLoopGroup分配链示意图



而Netty中的线程组(bossGroup、workGroup)处理的事件是是基于Netty Channel(NIO是面向Channel, 而 Java IO面向Stream)。Netty中的Channel与ChannelPipeline绑定,将事件处理业务交由一系列handler链组成,这些ChannelHandler由ChannelHandlerContext组织。在事件经过Channel时,由ChannelHandlerContext组织的ChannelHandler将对数据进行相应处理。此外,Channel将事件分为入站事件(读,从前往后流经Handler链)和出站事件(写,从后往前流经Hanlder),在事件流过ChannelPipeline时,出站事件将跳过处理入站事件ChannelHandler,入站事件将跳过出站事件的ChanneHanlder。下图给出一个Channel,ChannelPipeline,ChannelHandlerContext,ChannelHandler关系图:



此外,Netty除了使用reactor来提升性能,零拷贝,IO性能优化,通信上的粘包拆包,同步的设计。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签:  线程 多线程 框架