Netty搬运工 EventLoop与Bootstrap
Posted nullPoint水
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Netty搬运工 EventLoop与Bootstrap相关的知识,希望对你有一定的参考价值。
EventLoop
在Netty4中,所有的IO操作和事件都由已经分配给了EventLoop的Thread处理。EventLoop线程主要需要处理IO事件、定时任务和一般任务。Netty提供可一个参数ioRatio用于调整单线程对于IO处理事件和任务处理事件的分配比率。这样尽管一个EventLoop会关联多个Channel,这些Channel在单个线程下并不会出现并发问题,同时对于异步任务的处理也一样,Netty这样设计即免去了并发问题的烦恼,有减少了多线程上下文切换带来的性能损耗,同时基于EventLoopGroup实现的有限的线程数能够充分利用CPU处理能力。
Netty的EventLoop扩展了ScheduledExecutorService,所以它提供了JDK实现可用的所有方法,包括schedule()和scheduleAtFixedRate()方法。以下为EventLoop的继承关系类图
Netty线程模型的性能优越取决于对于当前执行的Thread的身份的确定。如果当前调用线程正式支撑EventLoop的线程,那么所提交的代码块将被直接执行。否则EventLoop将它放入到内部队列中,以便稍后执行。当EventLoop下次处理事件时将会处理队列中的任务。每个EventLoop都有他自己的任务队列,独立于任何其他的EventLoop。
EventLoop与线程的分配
异步传输
异步传输实现只使用了少量的EventLoop以及和他们相关联的Thread,而且在当前的线程模型中,他们可能会被多个Channel共享。
EventLoopGroup负责为每个新创建的Channel分配一个EventLoop。使用顺序循环的方式进行分配以获取一个均衡的分布,相同的EventLoop可能会被分配给多个Channel。一旦一个Channel被分配给一个EventLoop,它的整个生命周期都将使用这个EventLoop。
阻塞传输
阻塞传输每一个Channel都将被分配给一个EventLoop以及它的Thread。这样每个Channel的IO事件都将只会被一个Thread处理。
总结:Netty基于单线程设计的EventLoop能够同时处理成千上万的客户端连接的IO事件,缺点是单线程不能够处理时间过长的任务,这样会阻塞使得IO事件的处理被阻塞,严重的时候回造成IO事件堆积,服务不能够高效响应客户端请求。所谓时间过长的任务通常是占用CPU资源比较长的任务,也即CPU密集型,对于业务应用也可能是业务代码的耗时。
Bootstrap
之前简单说明了一下Channel、ChannelPipeline、ChannelHandler和EventLoop之后。在使用netty编程时需要Bootstrap来将这些部分组合到一起来工作。
先看一下Bootstrap使用的例子:
EventLoopGroup group = new NioEventLoopGroup();
Bootstrap bootstrap = new Bootstrap();
bootstrap.group(group).channel(NiosocketChannel.class)
.option(ChannelOption.TCP_NODELAY,true)
.handler(new ChannelInitializer<SocketChannel>() {
@Override
protected void initChannel(SocketChannel socketChannel) throws Exception {
}
});
如果对设计模式有所了解的话,应该可以看出这是Builder模式。
引导客户端和无连接协议
Bootstrap类被用于客户端或者使用了无连接协议的程序中。以下是一些常用方法:
名称 |
描述 |
Bootstrap group() |
设置处理Channel所有事件的EventLoopGroup |
Bootstrap channel() Bootstrap channelFactory() |
channel()方法指定了Channel的实现类。如果该实现类没有提供默认的构造函数,可以通过channelFactory()方法指定工厂类,它会被bind()方法调用 |
SocketAddress localAddress() |
|
<T> Bootstrap option() |
设置ChannelOption,将被应用到每个新创建的Channel的ChannelConfig。这个方法在Channel已经被创建后再调用将不会有效果。 |
<T>Bootstrap attr() |
指定新创建的Channel的属性值。这个方法在Channel已经被创建后再调用将不会有效果。 |
Bootstrap handler() |
设置将被添加到ChannelPipeline以接收事件通知的ChannelHandler |
Bootstrap clone() |
创建当前Bootstrap的克隆。 |
Bootstrap remoteAddress() |
|
ChannelFuture connect() |
|
ChannelFuture bind() |
绑定Channel并返回一个ChannelFuture,其将会在连接操作完成后收到通知 |
示例:
EventLoopGroup group = new NioEventLoopGroup();
Bootstrap bootstrap = new Bootstrap();
bootstrap.group(group).channel(NioSocketChannel.class)
.handler(new ChannelInitializer<SocketChannel>() {
@Override
protected void initChannel(SocketChannel socketChannel) throws Exception {
}
});
ChannelFuture future = bootstrap.connect(new InetAddress("localhost",80));
future.addListener(new ChannelFutureListener() {
@Override
public void operationComplete(ChannelFuture channelFuture) throws Exception {
if (channelFuture.isSuccess()){}
}
});
需要注意的是:对于NIO和OIO来说都有相关的EventLoopGroup和Channel实现,不能混用不同前缀的组件。NioEventLoopGroup必须和Nio开头的Channel使用。
对于无连接的协议与基于TCP协议的连接,区别就在于是需要调用bind()方法而不需要调用connect()方法。
引导服务端
ServerBootstrap类被用于服务端程序中,以下是常用方法。
名称 |
描述 |
ServerBootstrap group() |
设置ServerBootstrap要用的EventLoopGroup。这个EventLoopGroup将用于ServerChannel和被接受的子Channel的IO处理 |
ServerBootstrap channel() |
设置将要被实例化的ServerChannel类 |
ServerBootstrap channelFactory() |
如果不能通过默认的构造函数创建Channel,那么可以提供ChannelFactory |
SocketAddress localAddress() |
|
<T> ServerBootstrap option() |
指定要应用到新创建的ServerChannel的ChannelConfig的ChannelOption。这些选项将通过bind()方法设置到Channel。在bind()方法被调用之后,设置或者改变ChannelOption都不会有效果。 |
<T> ServerBootstrap childOption() |
指定当子Channel被接受时,应用到子Channel的ChannelConfig的ChannelOption。 |
<T> ServerBootstrap attr() |
指定ServerChannel的属性。这些选项将通过bind()方法设置到Channel。在bind()方法被调用之后,设置或者改变ChannelOption都不会有效果。。 |
<T> ServerBootstrap childAttr() |
将属性设置给已经被接受的子Channel。接下来的调用将不会有效果 |
ServerBootstrap handler() |
设置将被添加到ServerChannel的ChannelPipeline中的ChannelHandler。 |
ServerBootstrap childHandler() |
设置将被添加到已被接受的子Channel的ChannelPipeline中的ChannelHandler。handler()和childHandler()的区别:前者添加的ChannelHandler由接受子Channel的ServerChannel处理,而childHandler()添加的ChannelHandler由已被接受的子Channel处理。 |
ServerBootstrap clone() |
创建当前ServerBootstrap的克隆。 |
ChannelFuture bind() |
绑定ServerChannel并返回一个ChannelFuture,其将会在连接操作完成后收到通知 |
ServerChannel的实现负责创建子Channel,这些子Channel代表已被接受的连接。
从Channel引导客户端
通过将已被接受的子Channel的EventLoop传递给Bootstrap的group()方法来共享该EventLoop。因为分配给EventLoop的所有Channel都使用同一个线程,所以这避免了创建额外的线程以及上下文切换。
尽可能的重用EventLoop,以减少创建线程带来的开销。
ChannelOption
在每个Channel创建时都手动配置它的属性太过繁琐。使用option()方法将ChannelOption应用到Bootstrap,所做的配置将被自动应用到Bootstrap所创建的所有的Channel。
以上是关于Netty搬运工 EventLoop与Bootstrap的主要内容,如果未能解决你的问题,请参考以下文章