- 一个技术,为什么要用它,解决了那些问题?
- 如果不用会怎么样,有没有其它的解决方法?
- 对比其它的解决方案,为什么最终选择了这种,都有何利弊?
- 你觉得项目中还有那些地方可以用到,如果用了会带来那些问题?
- 这些问题你又如何去解决的呢?
本文基于Netty 4.1.45.Final-SNAPSHOT
从本开始正式对Netty源码下手。激动的心,颤抖的手。
简单的介绍一下netty。
Netty 是一款提供异步的、事件驱动的网络应用程序框架和工具,用以快速开发高性能、高可靠性的网络服务器和客户端程序。
也就是说,Netty 是一个基于 NIO 的客户、服务器端编程框架。使用 Netty 可以确保你快速和简单地开发出一个网络应用,例如实现了某种协议的客户,服务端应用。Netty 相当简化和流线化了网络应用的编程开发过程,例如,TCP 和 UDP 的 socket 服务开发。
源自《百度百科》
在了解Netty源码之前推荐先了解Netty的基础架构,和核心组件,了解Netty中核心组件是如何配置使用的。这里推荐阅读《Netty In Action》第一章的第三小节,详细的介绍了Netty的组件和涉及。
说会正题,在使用Netty时,首先接触的就是程序的启动。所以最先涉及到的就是Netty提供的启动服务io.netty.bootstrap.Bootstrap
和io.netty.bootstrap.ServerBootstrap
,这两个启动器组件。位置如下
类图如下:
example
包下的io.netty.example.echo.EchoServer
/**
* Echoes back any received data from a client.
* 回显从客户端接受到的数据
*/
public final class EchoServer {
static final boolean SSL = System.getProperty("ssl") != null; // 获取SSL验证
static final int PORT = Integer.parseInt(System.getProperty("port", "8007"));
public static void main(String[] args) throws Exception {
// Configure SSL. 配置SSL
final SslContext sslCtx;
if (SSL) {
SelfSignedCertificate ssc = new SelfSignedCertificate();
sslCtx = SslContextBuilder.forServer(ssc.certificate(), ssc.privateKey()).build();
} else {
sslCtx = null;
}
// Configure the server.
// 创建两个EventLoopGroup对象 boosGroup 用于处理父类Handler workerGroup用于处理 childHandler
/**
* 创建的两个线程组:
* boos线程组:用于服务端接受客户端的连接
* worker线程组:用于进行客户端的SocketChannel的数据读写
*
* 这里提个疑问:
* 为什么要使用两个线程组???
* // TODO
*/
EventLoopGroup bossGroup = new NioEventLoopGroup(1);
EventLoopGroup workerGroup = new NioEventLoopGroup();
// 创建EchoServerHandler 对象
final EchoServerHandler serverHandler = new EchoServerHandler();
try {
// 创建 ServerBootstrap对象用于设置服务端的启动配置
ServerBootstrap b = new ServerBootstrap();
b.group(bossGroup, workerGroup) // 设置创建的工作EventLoopGroup
.channel(NioServerSocketChannel.class) // 设置要被实例化的为 NioServerSocketChannel 类
.option(ChannelOption.SO_BACKLOG, 100) // 设置 NioServerSocketChannel 的可选项
.handler(new LoggingHandler(LogLevel.INFO)) // 设置BossGroup使用的日志处理器
.childHandler(new ChannelInitializer<SocketChannel>() {
@Override
public void initChannel(SocketChannel ch) throws Exception {
ChannelPipeline p = ch.pipeline(); // 设置连入服务端的 Client 的 SocketChannel 的处理器 ,用于自定义Handler处理器
if (sslCtx != null) {
p.addLast(sslCtx.newHandler(ch.alloc()));
}
//p.addLast(new LoggingHandler(LogLevel.INFO));
p.addLast(serverHandler);
}
});
// Start the server.
// 绑定端口,并同步等待成功,即启动服务端。
ChannelFuture f = b.bind(PORT).sync(); // 设置为 阻塞等待。
// Wait until the server socket is closed.
// 监听服务端关闭,并阻塞等待
f.channel().closeFuture().sync();
} finally {
// Shut down all event loops to terminate all threads.
// 优雅关闭
bossGroup.shutdownGracefully();
workerGroup.shutdownGracefully();
}
}
}
源码分析启动类的公共父类io.netty.bootstrap.AbstractBootstrap
。
所有源码注意都push 到Git上。
/**
* {@link AbstractBootstrap} is a helper class that makes it easy to bootstrap a {@link Channel}. It support
* method-chaining to provide an easy way to configure the {@link AbstractBootstrap}.
*
* {@link AbstractBootstrap}是一个帮助程序类,可以很容易地启动{@link Channel}。
* 它支持方法链,以提供一种简便的方法来配置{@link AbstractBootstrap}。
*
* When not used in a {@link ServerBootstrap} context, the {@link #bind()} methods are useful for connectionless
* transports such as datagram (UDP).
*
* {@link #bind()}方法在{@link ServerBootstrap}上下文中不使用时,
* 对于诸如数据报(UDP)之类的无连接传输非常有用。 p>
*/
public abstract class AbstractBootstrap<B extends AbstractBootstrap<B, C>, C extends Channel> implements Cloneable {
@SuppressWarnings("unchecked")
static final Map.Entry<ChannelOption<?>, Object>[] EMPTY_OPTION_ARRAY = new Map.Entry[0];
@SuppressWarnings("unchecked")
static final Map.Entry<AttributeKey<?>, Object>[] EMPTY_ATTRIBUTE_ARRAY = new Map.Entry[0];
/**
* EventLoopGroup 对象
*/
volatile EventLoopGroup group;
/**
* Channel 工厂 用于创建Channel 工厂。
*/
@SuppressWarnings("deprecation")
private volatile ChannelFactory<? extends C> channelFactory;
/**
* 本地地址
*/
private volatile SocketAddress localAddress;
/**
* 可选项集合
* ConcurrentHashMap 保证
*/
private final Map<ChannelOption<?>, Object> options = new ConcurrentHashMap<ChannelOption<?>, Object>();
/**
* 属性集合
* 这里需要注意的是使用的集合是:ConcurrentHashMap
*/
private final Map<AttributeKey<?>, Object> attrs = new ConcurrentHashMap<AttributeKey<?>, Object>();
/**
* 处理器
*/
private volatile ChannelHandler handler;
AbstractBootstrap() {
// Disallow extending from a different package.禁止从其他程序包扩展。
}
AbstractBootstrap(AbstractBootstrap<B, C> bootstrap) {
group = bootstrap.group;
channelFactory = bootstrap.channelFactory;
handler = bootstrap.handler;
localAddress = bootstrap.localAddress;
options.putAll(bootstrap.options);
attrs.putAll(bootstrap.attrs);
}
B``C
两个泛型。
ConcurrentHashMap
。在Netty追求性能的情况下,为什么要使用这个。(可能这两个属性集合存在多线程修改的情况。为了数据安全,使用并发包下的集合类。) 设置EventLoopGroup到group中去
/**
* 设置 EventLoopGroup 到group中
* The {@link EventLoopGroup} which is used to handle all the events for the to-be-created
* {@link Channel}
* {@link EventLoopGroup},用于处理要创建的所有事件{@link Channel}
*/
public B group(EventLoopGroup group) {
ObjectUtil.checkNotNull(group, "group");
if (this.group != null) {
throw new IllegalStateException("group set already");
}
this.group = group;
return self();
}
#self
方法,返回自己。
/**
* 这里的B 就是类上声明的 B 泛型
* @return 返回自身对象(自己)
*/
@SuppressWarnings("unchecked")
private B self() {
return (B) this;
}
设置要被实例化的Channel类
/**
* 设置要被实例化的Channel类
*
* The {@link Class} which is used to create {@link Channel} instances from.
* You either use this or {@link #channelFactory(io.netty.channel.ChannelFactory)} if your
* {@link Channel} implementation has no no-args constructor.
*
* {@link Class},用于从中创建{@link Channel}实例。
* 如果您的{@link Channel}实现没有no-args构造函数,
* 则可以使用this或{@link #channelFactory(io.netty.channel.ChannelFactory)}
*/
public B channel(Class<? extends C> channelClass) {
return channelFactory(new ReflectiveChannelFactory<C>(
ObjectUtil.checkNotNull(channelClass, "channelClass")
));
}
io.netty.channel.ReflectiveChannelFactory#ReflectiveChannelFactory
,使用反射机制实例化Channel/**
* A {@link ChannelFactory} that instantiates a new {@link Channel} by invoking its default constructor reflectively.
* 使用默认的构造方法 利用反射创建ChannelFactory实例。
*/
public class ReflectiveChannelFactory<T extends Channel> implements ChannelFactory<T> {
/**
* 成员变量为 构造器
*/
private final Constructor<? extends T> constructor;
/**
* 使用 clazz.getConstructor()获取构造器
* @param clazz Channel类的class对象
*/
public ReflectiveChannelFactory(Class<? extends T> clazz) {
ObjectUtil.checkNotNull(clazz, "clazz");
try {
this.constructor = clazz.getConstructor();
} catch (NoSuchMethodException e) {
throw new IllegalArgumentException("Class " + StringUtil.simpleClassName(clazz) +
" does not have a public non-arg constructor", e);
}
}
/**
* 使用构造器反射 实例化Channel
* @return Channel实例
*/
@Override
public T newChannel() {
try {
// 反射调用构造器方法
return constructor.newInstance();
} catch (Throwable t) {
throw new ChannelException("Unable to create Channel from class " + constructor.getDeclaringClass(), t);
}
}
/**
* 建议使用channel包下的 ChannelFactory
* @deprecated Use {@link #channelFactory(io.netty.channel.ChannelFactory)} instead.
*/
@Deprecated
public B channelFactory(ChannelFactory extends C> channelFactory) {
// 检查是否为空, 为空throw new NullPointerException(text);
ObjectUtil.checkNotNull(channelFactory, "channelFactory");
if (this.channelFactory != null) { // 不允许重复设置
throw new IllegalStateException("channelFactory set already");
}
this.channelFactory = channelFactory;
return self();
}
@SuppressWarnings({ "unchecked", "deprecation" })
public B channelFactory(io.netty.channel.ChannelFactory extends C> channelFactory) {
return channelFactory((ChannelFactory) channelFactory);
}
这里建议使用io.netty.channel.ChannelFactory
。
Channel 工厂接口,用于创建 Channel 对象。代码如下
@SuppressWarnings({
"ClassNameSameAsAncestorName", "deprecation" })
public interface ChannelFactory<T extends Channel> extends io.netty.bootstrap.ChannelFactory<T> {
/**
* Creates a new channel.
* 返回的是 T 泛型 , 为channel 对象。
*/
@Override
T newChannel(); // 创建Channel对象
}
用于绑定Channel 本地地址,该方法有四个重载方法
/**
* 用于绑定Channel 本地地址 有四个重载方法。
* The {@link SocketAddress} which is used to bind the local "end" to.
*/
public B localAddress(SocketAddress localAddress) {
this.localAddress = localAddress;
return self();
}
/**
* @see #localAddress(SocketAddress)
*/
public B localAddress(int inetPort) {
return localAddress(new InetSocketAddress(inetPort));
}
/**
* @see #localAddress(SocketAddress)
*/
public B localAddress(String inetHost, int inetPort) {
return localAddress(SocketUtils.socketAddress(inetHost, inetPort));
}
/**
* @see #localAddress(SocketAddress)
*/
public B localAddress(InetAddress inetHost, int inetPort) {
return localAddress(new InetSocketAddress(inetHost, inetPort));
}
其他的源码简单的请移步Git
io.netty.bootstrap.AbstractBootstrap#config
,获取当前AbstractBootstrap 的配置对象
/**
* 获取 当前 AbstractBootstrap 的配置对象
* Returns the {@link AbstractBootstrapConfig} object that can be used to obtain the current config
* of the bootstrap.
* 返回{@link AbstractBootstrapConfig}对象,该对象可用于获取bootstrap的当前配置
*/
public abstract AbstractBootstrapConfig<B, C> config();
从类图可以看出,Config类与bootstrap类一样,分客户端与服务端两种,但是都是继承自同一个父类,所以大致思路都差不多。下面就进去看一下有哪些不同。
io.netty.bootstrap.ServerBootstrapConfig
。服务端的公开配置类
/**
* Exposes the configuration of a {@link ServerBootstrapConfig}.
* 公开{@link ServerBootstrapConfig}的配置。服务端
* 服务端有两个工作线程组。所以存在 父子关系。一般worker线程都是要加载child。
*/
public final class ServerBootstrapConfig extends AbstractBootstrapConfig<ServerBootstrap, ServerChannel> {
ServerBootstrapConfig(ServerBootstrap bootstrap) {
super(bootstrap);
}
/**
* Returns the configured {@link EventLoopGroup} which will be used for the child channels or {@code null}
* if non is configured yet.
* 返回已配置的{@link EventLoopGroup},它将用于子通道;如果尚未配置,则返回{@code null}
*/
@SuppressWarnings("deprecation")
public EventLoopGroup childGroup() {
return bootstrap.childGroup();
}
/**
* Returns the configured {@link ChannelHandler} be used for the child channels or {@code null}
* if non is configured yet.
* 返回已配置{@link ChannelHandler},它将用于子通道;如果未配置,则返回{@code null}
*/
public ChannelHandler childHandler() {
return bootstrap.childHandler();
}
/**
* Returns a copy of the configured options which will be used for the child channels.
* 返回将用于子通道的已配置选项的副本。
* 返回的对象为一个新的map集合。
*/
public Map<ChannelOption<?>, Object> childOptions() {
return bootstrap.childOptions();
}
/**
* Returns a copy of the configured attributes which will be used for the child channels.
* 返回将用于子通道的已配置属性的副本。
* 同上一样
*/
public Map<AttributeKey<?>, Object> childAttrs() {
return bootstrap.childAttrs();
}
io.netty.bootstrap.BootstrapConfig
/**
* Exposes the configuration of a {@link Bootstrap}.
* 公开{@link Bootstrap}配置
*/
public final class BootstrapConfig extends AbstractBootstrapConfig<Bootstrap, Channel> {
BootstrapConfig(Bootstrap bootstrap) {
super(bootstrap);
}
/**
* Returns the configured remote address or {@code null} if non is configured yet.
* 返回已配置的远程地址;如果尚未配置,则返回{@code null}。
*/
public SocketAddress remoteAddress() {
return bootstrap.remoteAddress();
}
/**
* Returns the configured {@link AddressResolverGroup} or the default if non is configured yet.
* 返回已配置的{@link AddressResolverGroup};如果尚未配置,则返回{@code null}。
*/
public AddressResolverGroup<?> resolver() {
return bootstrap.resolver();
}
io.netty.bootstrap.AbstractBootstrap#bind()
。绑定端口,启动服务端。
/**
* Create a new {@link Channel} and bind it.
* 创建一个新的{@link Channel},并将其绑定。
* 与{@link #localAddress(SocketAddress)}对应,有四个重载方法
* 该方法返回的{@link ChannelFuture}对象。也就是异步的绑定端口,启动服务端。
* 如果需要同步,则需要调用 {@link ChannelFuture#sync()} 方法。该方法会阻塞。
*/
public ChannelFuture bind() {
validate(); // 校验服务启动需要的所有配置
SocketAddress localAddress = this.localAddress;
if (localAddress == null) {
throw new IllegalStateException("localAddress not set");
}
// 绑定本地地址(包括端口)
return doBind(localAddress);
}
/**
* Create a new {@link Channel} and bind it.
*/
public ChannelFuture bind(int inetPort) {
return bind(new InetSocketAddress(inetPort));
}
/**
* Create a new {@link Channel} and bind it.
*/
public ChannelFuture bind(String inetHost, int inetPort) {
return bind(SocketUtils.socketAddress(inetHost, inetPort));
}
/**
* Create a new {@link Channel} and bind it.
*/
public ChannelFuture bind(InetAddress inetHost, int inetPort) {
return bind(new InetSocketAddress(inetHost, inetPort));
}
/**
* Create a new {@link Channel} and bind it.
*/
public ChannelFuture bind(SocketAddress localAddress) {
validate();
// 使用 自定义localAddress,如果不为空。
return doBind(ObjectUtil.checkNotNull(localAddress, "localAddress"));
}
ChannelFuture#sync()
方法。 io.netty.bootstrap.AbstractBootstrap#doBind
里涉及到大量的JDK Future
异步操作,所以我们先简单的看一下Netty是则呢封装JDK 并发包下的Future的。
Netty中与JDK Future 的类关系图
我们先看一下io.netty.util.concurrent.Future
,是如何继承java.util.concurrent.Future
的。
这里首先提出一个疑问,在JDK已经提供了Future的实现,用于执行异步操作并且提供了相应对结果操作的方法。那为什么Netty内部还要维护一个自己的Future类,并且继承了JDK的Future接口。
该接口表示的是异步计算的结果,提供若干方法来检测计算是否完成、等待计算完成、获取计算的结果。
这种形式出现的解决的痛点就是在多线程并发开发下,Runable 执行的程序并没有返回值,很多程序的运行状态开发人员并不能很清楚的知晓。所以在很多并发条件下,并不能更加丰富且真实的完成并发。
eg:
java.util.concurrent.Future
源码中提供的示例* A { @code Future} represents the result of an asynchronous * computation. Methods are provided to check if the computation is * complete, to wait for its completion, and to retrieve the result of * the computation. The result can only be retrieved using method * { @code get} when the computation has completed, blocking if * necessary until it is ready. Cancellation is performed by the * { @code cancel} method. Additional methods are provided to * determine if the task completed normally or was cancelled. Once a * computation has completed, the computation cannot be cancelled. * If you would like to use a { @code Future} for the sake * of cancellability but not provide a usable result, you can * declare types of the form { @code Future<?>} and * return { @code null} as a result of the underlying task. { @code Future}表示异步计算的结果。提供了一些方法来检查计算是否完成,等待其完成并检索计算结果。 计算完成后,只能使用方法 { @code get}来检索结果;如果需要,则阻塞直到准备好为止。 取消是通过 { @code cancel}方法执行的。 提供了其他方法来确定任务是正常完成还是被取消。 计算完成后,无法取消计算。 如果出于可取消性的原因而想使用{ @code Future},但不提供可用的结果,则可以声明{ @code Future <?>}形式的类型,并返回{ @code null}作为基础任务的结果。 * * <p> * <b>Sample Usage</b> (Note that the following classes are all * made-up.) * <pre> { @code 简单的示例 * interface ArchiveSearcher { String search(String target); } * class App { * ExecutorService executor = ... * ArchiveSearcher searcher = ... * void showSearch(final String target) * throws InterruptedException { * Future<String> future * = executor.submit(new Callable<String>() { * public String call() { * return searcher.search(target); * }}); * displayOtherThings(); // do other things while searching * try { * displayText(future.get()); // use future * } catch (ExecutionException ex) { cleanup(); return; } * } * }}</pre> * * The { @link FutureTask} class is an implementation of { @code Future} that * implements { @code Runnable}, and so may be executed by an { @code Executor}. * For example, the above construction with { @code submit} could be replaced by: * { @link FutureTask}类是{ @code Future}的实现,它实现了{ @code Runnable},因此可以由{ @code Executor}执行。 例如,上面的{ @code Submit}结构可以替换为 <pre> { @code * FutureTask<String> future = * new FutureTask<String>(new Callable<String>() { * public String call() { * return searcher.search(target); * }}); * executor.execute(future);}</pre> * * <p>Memory consistency effects: Actions taken by the asynchronous computation * <a href="package-summary.html#MemoryVisibility"> <i>happen-before</i></a> * actions following the corresponding { @code Future.get()} in another thread. 受内存一致性的影响。Future.get() 会阻塞线程。 *
java.util.concurrent.Future
可以作为一个有返回值的Runnable
接口。用于判断标记检查计算是否完成,等待其完成并检索计算结果。Netty中的Future
io.netty.util.concurrent.Future
继承JDK Future
public interface Future<V> extends java.util.concurrent.Future<V> {
netty基于JDK的Future更加强化了它的功能性。下面列举几个比较重要的例子
io.netty.util.concurrent.Future#cause
:表示I/O操作失败时,返回异常信息。
io.netty.util.concurrent.Future#cancel
:使用布尔值,用于对以及该开始执行的Future进行中断操作。
io.netty.util.concurrent.Future#isSuccess
:仅当I / O操作成功完成时,才返回{@code true}
对于上述jdk中Future申明的isDone方法,只能知道I/O是否结束,有可能是成功完成、被取消、异常中断。netty中Future的此isSuccess方法能够很好的判断出操作是否真正地成功完成
io.netty.util.concurrent.Future#sync
:等待这个future直到完成,如果这个future失败,则重新抛出失败原因。该方法会一直阻塞。(所以在Netty 服务端的示例中,在绑定端口号时,会调用该方法,使其处于等待阻塞状态,取消异步处理)
io.netty.util.concurrent.Future#addListener
:将指定的侦听器添加到当前future。当此future完成时{ linkplain #isDone()完成}(返回true时),将通知指定的侦听器。如果这个future已经完成,则立即通知指定的侦听器
这是Netty 对JDK Future扩展中最重要的一个方法,这里使用了观察者模式
/**
* Listens to the result of a {@link Future}. The result of the asynchronous operation is notified once this listener
* is added by calling {@link Future#addListener(GenericFutureListener)}.
* 监听{@link Future}的结果。一旦通过调用{@link Future#addListener(GenericFutureListener)}添加了此侦听器,
* 便会通知异步操作的结果。
*/
public interface GenericFutureListener<F extends Future<?>> extends EventListener {
/**
* Invoked when the operation associated with the {@link Future} has been completed.
*
* 与{@link Future}关联的操作完成时调用。
* @param future the source {@link Future} which called this callback 回调
*/
void operationComplete(F future) throws Exception;
}
为什么JDK Future中已经提供了
java.util.concurrent.Future#get()
方法来获取异步的结果,Netty还要提供观察者模式用于获取异步结果呢? 答:原因:
-
java.util.concurrent.Future#get()
获取操作会一直阻塞,直到Future执行完毕。违背了异步调用的原则。- 我们在使用
java.util.concurrent.Future#get()
会考虑到底什么时候使用,因为该方法会阻塞后续的逻辑代码,如果我们使用监听器,会更加优雅地决定在合理的时间来处理我们的逻辑代码(监听器会监听整个过程,完成后会立即通知指定地侦听器)
io.netty.util.concurrent.Future#getNow
:返回这个结果并且不会阻塞,如果当前future并没有完成则将会返回{@code null}.
这个方法专门针对 java.util.concurrent.Future#get()
会阻塞后续代码提出的。但是有一个问题如果当前future没有完成返回 null,但是有可能Future在完成后返回的结果就是null呢?所以这里有个点需要注意:
由于可能使用{
@code null}值将future标记为成功,(有可能当前的future 返回的结果就是null ,如果单一的凭借这种标志来决定future是否完成太过草率了)
* 因此您还需要检查future是否真的由{
@link #isDone()}完成而不依赖于返回的{
@code null}值。
检查 isDone()是否返回true;
ChannelFuture
netty中的ChannelFuture继承来netty中的自己的Future
addListener方法传入的监听器会实现以下接口,也就是被通知的时候operationComplete方法会被调用:
public interface ChannelFuture extends Future<Void>
ChannelFuture表示Channel中异步I/O操作的结果,在netty中所有的I/O操作都是异步的,I/O的调用会直接返回,可以通过ChannelFuture来获取I/O操作的结果状态。对于多种状态的表示如下:
在ChannelFuture
doc 文档中强调了几点:因为文档太长了,可以到我的git上去查看。
* | Completed successfully |
* +---------------------------+
* +----> isDone() = true |
* +--------------------------+ | | isSuccess() = true |
* | Uncompleted | | +===========================+
* +--------------------------+ | | Completed with failure |
* | isDone() = false | | +---------------------------+
* | isSuccess() = false |----+----> isDone() = true |
* | isCancelled() = false | | | cause() = non-null |
* | cause() = null | | +===========================+
* +--------------------------+ | | Completed by cancellation |
* | +---------------------------+
* +----> isDone() = true |
* | isCancelled() = true |
* +---------------------------+
文档中强调了一点,一个ChannelFuture无非只有两种状态,完成与未完成。但是在完成状态下包含了三种情况都视为已经完成:
- successfully 成功
- failure 失败
- cancellation 取消
以上三种情况都为 完成状态。
ChannelFuture中需要注意的是添加了channel方法来获取Channel:
/**
* Returns a channel where the I/O operation associated with this
* future takes place.
*/
Channel channel();
JDK所提供的Future只能通过手工方式检查执行结果,而这个操作是会阻塞的;Netty则对ChannelFuture进行来增强,通过ChannelFutureListener以回调的方式来获取执行结果,
去除来手工检查阻塞的操作。需要注意的是ChannelFutureListener的operationComplete方法是由I/O线程执行的,因此要注意的是不要在这里执行耗时操作,否则需要通过另外的线程或线程池来执行
ChannelPromise
ChannelPromise是一种可写的特殊ChannelFuture。
/**
* Special {@link ChannelFuture} which is writable.
* 一个特殊的{@link ChannelFuture}
* 它是可写的。
*/
public interface ChannelPromise extends ChannelFuture, Promise
接下来我们接着研究io.netty.bootstrap.AbstractBootstrap#doBind
,因为这里涉及到了ChannelFuture的异步操作所以带入了一点Future的东西。
private ChannelFuture doBind(final SocketAddress localAddress) {
// 初始化并注册一个 Channel 对象,因为注册是异步的过程,所以返回一个 ChannelFuture 对象。
final ChannelFuture regFuture = initAndRegister(); // 初始化并且注册
final Channel channel = regFuture.channel(); // ChannelFuture 会利用监听器回调返回一个Channel
if (regFuture.cause() != null) {
// 若发生异常,直接返回,该ChannelFuture对象会携带异常信息
return regFuture;
}
// 绑定Channel 端口,并且注册 Channel 到 SelectionKey 中。
if (regFuture.isDone()) {
// 如果此任务完成
// At this point we know that the registration was complete and successful.
ChannelPromise promise = channel.newPromise();
doBind0(regFuture, channel, localAddress, promise); // 绑定。
return promise;
} else {
// Registration future is almost always fulfilled already, but just in case it's not.
//未来的注册几乎总是已经实现,但以防万一还没有完成。
final PendingRegistrationPromise promise = new PendingRegistrationPromise(channel);
regFuture.addListener(new ChannelFutureListener() {
@Override
public void operationComplete(ChannelFuture future) throws Exception {
Throwable cause = future.cause();
if (cause != null) {
// Registration on the EventLoop failed so fail the ChannelPromise directly to not cause an
// IllegalStateException once we try to access the EventLoop of the Channel.
promise.setFailure(cause);
} else {
// Registration was successful, so set the correct executor to use.
// 注册成功,因此请设置要使用的正确执行器
// See https://github.com/netty/netty/issues/2586
promise.registered();
doBind0(regFuture, channel, localAddress, promise);
}
}
});
return promise;
}
}
第3行:final ChannelFuture regFuture = initAndRegister();
初始化并且注册Channel,返回一个异步的ChannelFuture 对象
从9-14再14-35分为两个部分,因为Future无非是两种情况,完成与未完成。
#isDone
:执行doBind0(regFuture, channel, localAddress, promise);
绑定 Channel 的端口,并注册 Channel 到 SelectionKey 中。
else
:final PendingRegistrationPromise promise = new PendingRegistrationPromise(channel);
创建一个可写的ChannelFuture,并且将它添加到当前ChannelFuture中。
然后判断该Future中是否失败。
elese:
//添加监听器,在注册完成后,进行回调执行 #doBind0(...) 方法的逻辑
promise.registered();
doBind0(regFuture, channel, localAddress, promise);
所以可以看出,bind的逻辑是再执行register的逻辑之后。
io.netty.bootstrap.AbstractBootstrap#initAndRegister
final ChannelFuture initAndRegister() {
Channel channel = null;
try {
channel = channelFactory.newChannel(); // 使用工厂类创建channel
init(channel); // 初始化Channel 配置,具体在子类中实现
} catch (Throwable t) {
// 发生异常
if (channel != null) {
// channel已经创建
// channel can be null if newChannel crashed (eg SocketException("too many open files"))
channel.unsafe().closeForcibly(); // 强制关闭channel
// as the Channel is not registered yet we need to force the usage of the GlobalEventExecutor
// 返回一个带异常的 DefaultChannelPromise
return new DefaultChannelPromise(channel, GlobalEventExecutor.INSTANCE).setFailure(t);
}
// as the Channel is not registered yet we need to force the usage of the GlobalEventExecutor
// 由于尚未注册频道,因此我们需要强制使用GlobalEventExecutor
// 因为创建Channel对象失败,所示使用new FailedChannel()
return new DefaultChannelPromise(new FailedChannel(), GlobalEventExecutor.INSTANCE).setFailure(t);
}
//首先获得 EventLoopGroup 对象,后调用 EventLoopGroup#register(Channel) 方法,
// 注册 Channel 到 EventLoopGroup 中。实际在方法内部,
// EventLoopGroup 会分配一个 EventLoop 对象,将 Channel 注册到其上,并通知ChannelFuture
ChannelFuture regFuture = config().group().register(channel);
if (regFuture.cause() != null) {
if (channel.isRegistered()) {
// Channel 注册成功,正常关闭。
channel.close();
} else {
channel.unsafe().closeForcibly();
}
}
// If we are here and the promise is not failed, it's one of the following cases:
// 1) If we attempted registration from the event loop, the registration has been completed at this point.
// i.e. It's safe to attempt bind() or connect() now because the channel has been registered.
// 2) If we attempted registration from the other thread, the registration request has been successfully
// added to the event loop's task queue for later execution.
// i.e. It's safe to attempt bind() or connect() now:
// because bind() or connect() will be executed *after* the scheduled registration task is executed
// because register(), bind(), and connect() are all bound to the same thread.
// 如果我们在这里并且promise没有失败,则是以下情况之一:
// 1)如果我们尝试从事件循环中进行注册,则注册已完成。
// 即立即尝试bind()或connect()是安全的,因为该通道已被注册。
// 2)如果我们尝试从另一个线程进行注册,则注册请求已成功
// 添加到事件循环的任务队列中,以便以后执行。
// 立即尝试bind()或connect()是安全的:
// 因为bind()或connect()将在*计划的注册任务执行后*执行,
// 因为register(),bind()和connect()都绑定到同一线程。
return regFuture;
}
23行:注册 Channel 到 EventLoopGroup 中,并且通知ChannelFuture注册完成。返回ChannleFuture,有可能注册成功但是出现错误,有可能注册都没有成功,
所以分了正常关闭channle channel.close();
和强制关闭channel.unsafe().closeForcibly();
。
我们看一下这两种关闭方式的区别
/**
* Close the {@link Channel} of the {@link ChannelPromise} and notify the {@link ChannelPromise} once the
* operation was complete.
*
* 操作完成后,关闭{@link ChannelPromise}的{@link Channel},并通知{@link ChannelPromise}。
*/
void close(ChannelPromise promise);
/**
* Closes the {@link Channel} immediately without firing any events. Probably only useful
* when registration attempt failed.
*/
void closeForcibly();
正常的close 关闭 会触发事件,通知ChannelPromise。
强制关闭,不会触发任何事件。
channel = channelFactory.newChannel();
上述代码中使用,反射机制实现Channel 的创建。
以NioServerSocketChannel
的创建过程为例。流程图如下:
官方给与NettyChannel 的定义
A nexus to a network socket or a component which is capable of I/O operations such as read, write, connect, and bind
下面我们来看一下NioServerSocketChannel
具体的实现源码
/**
* A {@link io.netty.channel.socket.ServerSocketChannel} implementation which uses
* NIO selector based implementation to accept new connections.
* 基于 NIO selector的实现来接受连接的一个{@link io.netty.channel.socket.ServerSocketChannel}的实现
*/
public class NioServerSocketChannel extends AbstractNioMessageChannel
implements io.netty.channel.socket.ServerSocketChannel {
private static final SelectorProvider DEFAULT_SELECTOR_PROVIDER = SelectorProvider.provider();
private static ServerSocketChannel newSocket(SelectorProvider provider) {
try {
/**
* Use the {@link SelectorProvider} to open {@link SocketChannel} and so remove condition in
* {@link SelectorProvider#provider()} which is called by each ServerSocketChannel.open() otherwise.
*
* See #2308.
*/
return provider.openServerSocketChannel(); // 多么熟悉的味道啊。是不是感觉特别亲切?
} catch (IOException e) {
throw new ChannelException(
"Failed to open a server socket.", e);
}
}
private final ServerSocketChannelConfig config;
/**
* Create a new instance
*/
public NioServerSocketChannel() {
this(newSocket(DEFAULT_SELECTOR_PROVIDER));
}
/**
* Create a new instance using the given {@link SelectorProvider}.
*/
public NioServerSocketChannel(SelectorProvider provider) {
this(newSocket(provider));
}
/**
* Create a new instance using the given {@link ServerSocketChannel}.
*/
public NioServerSocketChannel(ServerSocketChannel channel) {
super(null, channel, SelectionKey.OP_ACCEPT);
config = new NioServerSocketChannelConfig(this, javaChannel().socket());
}
private final ServerSocketChannelConfig config;
DEFAULT_SELECTOR_PROVIDER
默认的SelectorProvider
实现类。
config
,每个Channel对应的配置对象,每种Channel实现类都会又对应的ChannelConfig实现类。例如:NioServerSocketChannel
对应的就是ServerSocketChannelConfig
io.netty.channel.socket.nio.NioServerSocketChannel
中的构造都依赖于静态方法io.netty.channel.socket.nio.NioServerSocketChannel#newSocket
。
构造函数中还可以接受Channel 对象。研究一下这个独特的构造函数。
public NioServerSocketChannel(ServerSocketChannel channel) {
super(null, channel, SelectionKey.OP_ACCEPT);
config = new NioServerSocketChannelConfig(this, javaChannel().socket());
}
调用了父类构造器。
io.netty.channel.nio.AbstractNioMessageChannel
/**
* @see AbstractNioChannel#AbstractNioChannel(Channel, SelectableChannel, int)
*/
protected AbstractNioMessageChannel(Channel parent, SelectableChannel ch, int readInterestOp) {
super(parent, ch, readInterestOp);
}
直接调用了父类构造。
protected AbstractNioChannel(Channel parent, SelectableChannel ch, int readInterestOp) {
super(parent);
this.ch = ch;
this.readInterestOp = readInterestOp;
try {
ch.configureBlocking(false); // 通道设置为非阻塞状态,是不是熟悉的味道
} catch (IOException e) {
try {
ch.close();
} catch (IOException e2) {
logger.warn(
"Failed to close a partially initialized socket.", e2);
}
throw new ChannelException("Failed to enter non-blocking mode.", e);
}
}
ch
属性为:io.netty.channel.Channel
,是Neety Channel ,持有JDK Channelch.configureBlocking(false);
:多么熟悉亲切的代码 /**
* Creates a new instance.
*
* @param parent
* the parent of this channel. {@code null} if there's no parent.
*/
protected AbstractChannel(Channel parent) {
this.parent = parent;
id = newId();
unsafe = newUnsafe();
pipeline = newChannelPipeline();
}
parent
:为父Channel
对象,如果没有父类,则为NULL。ChannleId
等属性。id
属性,Channel 编号对象。在构造方法中,通过调用 #newId()
方法,进行创建。unsafe
属性,Unsafe 对象。在构造方法中,通过调用 #newUnsafe()
方法,进行创建。
sun.misc.Unsafe
,而是 io.netty.channel.Channel#Unsafe
。pipeline
属性,DefaultChannelPipeline 对象。在构造方法中,通过调用 #newChannelPipeline()
方法,进行创建。本文就先不分享,感兴趣的胖友自己看。 在整个创建Channel的过程中,对于一个Netty NIO Channel对象,它包含了如下几个核心组件:
init(channel)
是一个抽象方法,具体实现由对应的类。
io.netty.channel.EventLoopGroup#register(io.netty.channel.Channel)
方法,注册Channel到 EventLoopGroup 中。整体流程如下:
我们直接从第4步入手,这里涉及到的操作都是基于io.netty.channel.AbstractChannel.AbstractUnsafe
这个内部类。继承了io.netty.channel.Channel.Unsafe
;
@Override
public final void register(EventLoop eventLoop, final ChannelPromise promise) {
ObjectUtil.checkNotNull(eventLoop, "eventLoop"); // 校验非空
if (isRegistered()) {
// 校验未注册
promise.setFailure(new IllegalStateException("registered to an event loop already"));
return;
}
if (!isCompatible(eventLoop)) {
// 校验Channel和EventLoop匹配
promise.setFailure(
new IllegalStateException("incompatible event loop type: " + eventLoop.getClass().getName()));
return;
}
AbstractChannel.this.eventLoop = eventLoop;
if (eventLoop.inEventLoop()) {
// 判断是否为当前线程。要保证在eventLoop线程组中执行注册逻辑
register0(promise);
} else {
try {
// 使用eventLoop 线程组中的线程实现注册操作
eventLoop.execute(new Runnable() {
@Override
public void run() {
register0(promise);
}
});
} catch (Throwable t) {
logger.warn(
"Force-closing a channel whose registration task was not accepted by an event loop: {}",
AbstractChannel.this, t);
closeForcibly();
closeFuture.setClosed();
safeSetFailure(promise, t);
}
}
}
private void register0(ChannelPromise promise) {
try {
// check if the channel is still open as it could be closed in the mean time when the register
// call was outside of the eventLoop
// 检查通道是否仍处于打开状态,因为在注册调用在eventLoop外部的同时可能会关闭
if (!promise.setUncancellable() || !ensureOpen(promise)) {
return;
}
boolean firstRegistration = neverRegistered; // 记录是否为首次注册
doRegister(); // 执行注册逻辑
neverRegistered = false; // 修改标记,说明应该不是首次注册。
registered = true; // 维护Channel 已经注册
// Ensure we call handlerAdded(...) before we actually notify the promise. This is needed as the
// user may already fire events through the pipeline in the ChannelFutureListener.
// //确保我们在实际通知promise之前调用handlerAdded(...)。
// 这是必需的,因为用户可能已经通过ChannelFutureListener中的pipeline触发了事件
pipeline.invokeHandlerAddedIfNeeded();
// 回调通知已注册事件
safeSetSuccess(promise);
pipeline.fireChannelRegistered(); // 触发通道已注册事件
// Only fire a channelActive if the channel has never been registered. This prevents firing
// multiple channel actives if the channel is deregistered and re-registered.
// 仅当从未注册过频道时才触发channelActive。
// 如果取消注销并重新注册了通道,则可以防止激活多个通道。
if (isActive()) {
if (firstRegistration) {
pipeline.fireChannelActive();
} else if (config().isAutoRead()) {
// This channel was registered before and autoRead() is set. This means we need to begin read
// again so that we process inbound data.
//该通道已注册,并已设置autoRead()。这意味着我们需要再次开始read,以便处理入站数据
// See https://github.com/netty/netty/issues/4805
beginRead();
}
}
} catch (Throwable t) {
// Close the channel directly to avoid FD leak.
closeForcibly();
closeFuture.setClosed();
safeSetFailure(promise, t);
}
}
注册方式是多态的,它既可以被 NIOServerSocketChannel 用来监听客户端的连接接入,也可以注册 SocketChannel 用来监听网络读或者写操作。
通过
SelectionKey#interestOps(int ops)
方法可以方便地修改监听操作位。所以,此处注册需要获取 SelectionKey 并给 AbstractNIOChannel 的成员变量
selectionKey
赋值。
- 如果不理解,没关系,在下文中,我们会看到服务端对
SelectionKey.OP_ACCEPT
事件的关注。
Channel注册完之后,调用doBind0
#doBind0(...)
方法,执行 Channel 的端口绑定逻辑。代码如下:
1: private static void doBind0(
2: final ChannelFuture regFuture, final Channel channel,
3: final SocketAddress localAddress, final ChannelPromise promise) {
4:
5: // This method is invoked before channelRegistered() is triggered. Give user handlers a chance to set up
6: // the pipeline in its channelRegistered() implementation.
7: channel.eventLoop().execute(new Runnable() {
8: @Override
9: public void run() {
11: // 注册成功,绑定端口
12: if (regFuture.isSuccess()) {
13: channel.bind(localAddress, promise).addListener(ChannelFutureListener.CLOSE_ON_FAILURE);
14: // 注册失败,回调通知 promise 异常
15: } else {
16: promise.setFailure(regFuture.cause());
17: }
18: }
19: });
20: }
第 7 行:调用 EventLoop 执行 Channel 的端口绑定逻辑。但是,实际上当前线程已经是 EventLoop 所在的线程了,为何还要这样操作呢?答案在【第 5 至 6 行】的英语注释。感叹句,Netty 虽然代码量非常庞大且复杂,但是英文注释真的是非常齐全,包括 Github 的 issue 对代码提交的描述,也非常健全。
第 14 至 17 行:注册失败,回调通知 promise
异常。
第 11 至 13 行:注册成功,调用
Channel#bind(SocketAddress localAddress, ChannelPromise promise)
方法,执行 Channel 的端口绑定逻辑。后续的方法栈调用如下图:
Channel bind 流程
AbstractUnsafe#bind(final SocketAddress localAddress, final ChannelPromise promise)
方法,继续向下分享。AbstractUnsafe#bind(final SocketAddress localAddress, final ChannelPromise promise)
方法,Channel 的端口绑定逻辑。代码如下:
1: @Override
2: public final void bind(final SocketAddress localAddress, final ChannelPromise promise) {
3: // 判断是否在 EventLoop 的线程中。
4: assertEventLoop();
5:
6: if (!promise.setUncancellable() || !ensureOpen(promise)) {
7: return;
8: }
9:
10: // See: https://github.com/netty/netty/issues/576
11: if (Boolean.TRUE.equals(config().getOption(ChannelOption.SO_BROADCAST)) &&
12: localAddress instanceof InetSocketAddress &&
13: !((InetSocketAddress) localAddress).getAddress().isAnyLocalAddress() &&
14: !PlatformDependent.isWindows() && !PlatformDependent.maybeSuperUser()) {
15: // Warn a user about the fact that a non-root user can't receive a
16: // broadcast packet on *nix if the socket is bound on non-wildcard address.
17: logger.warn(
18: "A non-root user can't receive a broadcast packet if the socket " +
19: "is not bound to a wildcard address; binding to a non-wildcard " +
20: "address (" + localAddress + ") anyway as requested.");
21: }
22:
23: // 记录 Channel 是否激活
24: boolean wasActive = isActive();
25:
26: // 绑定 Channel 的端口
27: try {
28: doBind(localAddress);
29: } catch (Throwable t) {
30: safeSetFailure(promise, t);
31: closeIfClosed();
32: return;
33: }
34:
35: // 若 Channel 是新激活的,触发通知 Channel 已激活的事件。
36: if (!wasActive && isActive()) {
37: invokeLater(new Runnable() {
38: @Override
39: public void run() {
40: pipeline.fireChannelActive();
41: }
42: });
43: }
44:
45: // 回调通知 promise 执行成功
46: safeSetSuccess(promise);
47: }
第 4 行:调用 #assertEventLoop()
方法,判断是否在 EventLoop 的线程中。即该方法,只允许在 EventLoop 的线程中执行。代码如下:
// AbstractUnsafe.java
private void assertEventLoop() {
assert !registered || eventLoop.inEventLoop();
}
第 6 至 8 行:和 #register0(...)
方法的【第 5 至 8 行】的代码,是一致的。
第 10 至 21 行:https://github.com/netty/netty/issues/576
第 24 行:调用 #isActive()
方法,获得 Channel 是否激活( active )。NioServerSocketChannel 对该方法的实现代码如下:
// NioServerSocketChannel.java
@Override
public boolean isActive() {
return javaChannel().socket().isBound();
}
#isActive()
的方法实现,判断 ServerSocketChannel 是否绑定端口。此时,一般返回的是 false
。第 28 行:调用 #doBind(SocketAddress localAddress)
方法,绑定 Channel 的端口。代码如下:
// NioServerSocketChannel.java
@Override
protected void doBind(SocketAddress localAddress) throws Exception {
if (PlatformDependent.javaVersion() >= 7) {
javaChannel().bind(localAddress, config.getBacklog());
} else {
javaChannel().socket().bind(localAddress, config.getBacklog());
}
}
第 36 行:再次调用 #isActive()
方法,获得 Channel 是否激活。此时,一般返回的是 true
。因此,Channel 可以认为是新激活的,满足【第 36 至 43 行】代码的执行条件。
第 37 行:调用 #invokeLater(Runnable task)
方法,提交任务,让【第 40 行】的代码执行,异步化。代码如下:
// AbstractUnsafe.java
private void invokeLater(Runnable task) {
try {
// This method is used by outbound operation implementations to trigger an inbound event later.
// They do not trigger an inbound event immediately because an outbound operation might have been
// triggered by another inbound event handler method. If fired immediately, the call stack
// will look like this for example:
//
// handlerA.inboundBufferUpdated() - (1) an inbound handler method closes a connection.
// -> handlerA.ctx.close()
// -> channel.unsafe.close()
// -> handlerA.channelInactive() - (2) another inbound handler method called while in (1) yet
//
// which means the execution of two inbound handler methods of the same handler overlap undesirably.
eventLoop().execute(task);
} catch (RejectedExecutionException e) {
logger.warn("Can't invoke task later as EventLoop rejected it", e);
}
}
第 40 行:调用 DefaultChannelPipeline#fireChannelActive()
方法,触发 Channel 激活的事件。详细解析,见 「3.13.3 beginRead」 。
第 46 行:调用 #safeSetSuccess(ChannelPromise)
方法,回调通知 promise
执行成功。此处的通知,对应回调的是我们添加到 #bind(...)
方法返回的 ChannelFuture 的 ChannelFutureListener 的监听器。示例代码如下:
ChannelFuture f = b.bind(PORT).addListener(new ChannelFutureListener() {
// 回调的就是我!!!
@Override
public void operationComplete(ChannelFuture future) throws Exception {
System.out.println("测试下被触发");
}
}).sync();
在 #bind(final SocketAddress localAddress, final ChannelPromise promise)
方法的【第 40 行】代码,调用 Channel#bind(SocketAddress localAddress, ChannelPromise promise)
方法,触发 Channel 激活的事件。后续的方法栈调用如下图:触发 Channel 激活的事件
* 还是老样子,我们先省略掉 pipeline 的内部实现代码,从 `AbstractUnsafe#beginRead()` 方法,继续向下分享。
AbstractUnsafe#beginRead()
方法,开始读取操作。代码如下:
@Override
public final void beginRead() {
// 判断是否在 EventLoop 的线程中。
assertEventLoop();
// Channel 必须激活
if (!isActive()) {
return;
}
// 执行开始读取
try {
doBeginRead();
} catch (final Exception e) {
invokeLater(new Runnable() {
@Override
public void run() {
pipeline.fireExceptionCaught(e);
}
});
close(voidPromise());
}
}
调用 Channel#doBeginRead()
方法,执行开始读取。对于 NioServerSocketChannel 来说,该方法实现代码如下:
// AbstractNioMessageChannel.java
@Override
protected void doBeginRead() throws Exception {
if (inputShutdown) {
return;
}
super.doBeginRead();
}
// AbstractNioChannel.java
@Override
protected void doBeginRead() throws Exception {
// Channel.read() or ChannelHandlerContext.read() was called
final SelectionKey selectionKey = this.selectionKey;
if (!selectionKey.isValid()) {
return;
}
readPending = true;
final int interestOps = selectionKey.interestOps();
if ((interestOps & readInterestOp) == 0) {
selectionKey.interestOps(interestOps | readInterestOp);
}
}
SelectionKey#interestOps(ops)
方法,将我们创建 NioServerSocketChannel 时,设置的 readInterestOp = SelectionKey.OP_ACCEPT
添加为感兴趣的事件。也就说,服务端可以开始处理客户端的连接事件。 io.netty.bootstrap.ServerBootstrap
下面值是重点指出了重要的方法实现。其它源码与父类io.netty.bootstrap.AbstractBootstrap
大同小异。
// 子Channel的可选集合
private final Map<ChannelOption<?>, Object> childOptions = new ConcurrentHashMap<ChannelOption<?>, Object>();
// 子Channel的属性集合
private final Map<AttributeKey<?>, Object> childAttrs = new ConcurrentHashMap<AttributeKey<?>, Object>();
// 启动类 配置对象
private final ServerBootstrapConfig config = new ServerBootstrapConfig(this);
// 子Channel 的EventLoopGroup对象
private volatile EventLoopGroup childGroup;
// 子Channel 的处理器
private volatile ChannelHandler childHandler;
public ServerBootstrap() {
}
private ServerBootstrap(ServerBootstrap bootstrap) {
super(bootstrap);
childGroup = bootstrap.childGroup;
childHandler = bootstrap.childHandler;
childOptions.putAll(bootstrap.childOptions);
childAttrs.putAll(bootstrap.childAttrs);
}
@Override
void init(Channel channel) {
// 初始化Channel 的可选想集合
setChannelOptions(channel, options0().entrySet().toArray(EMPTY_OPTION_ARRAY), logger);
// 初始化 Channel的属性集合
setAttributes(channel, attrs0().entrySet().toArray(EMPTY_ATTRIBUTE_ARRAY));
ChannelPipeline p = channel.pipeline();
final EventLoopGroup currentChildGroup = childGroup;
final ChannelHandler currentChildHandler = childHandler;
final Entry<ChannelOption<?>, Object>[] currentChildOptions =
childOptions.entrySet().toArray(EMPTY_OPTION_ARRAY);
final Entry<AttributeKey<?>, Object>[] currentChildAttrs = childAttrs.entrySet().toArray(EMPTY_ATTRIBUTE_ARRAY);
// 添加 ChannelInitializer 对象到 pipeline 中,用于后续初始化 ChannelHandler 到 pipeline 中。
p.addLast(new ChannelInitializer<Channel>() {
@Override
public void initChannel(final Channel ch) {
final ChannelPipeline pipeline = ch.pipeline();
// 添加配置的 ChannelHandler 到 pipeline 中。
ChannelHandler handler = config.handler();
if (handler != null) {
pipeline.addLast(handler);
}
// 添加 ServerBootstrapAcceptor 到 pipeline 中。
// 使用 EventLoop 执行的原因,
// 参见 https://github.com/lightningMan/netty/commit/4638df20628a8987c8709f0f8e5f3679a914ce1a
ch.eventLoop().execute(new Runnable() {
@Override
public void run() {
pipeline.addLast(new ServerBootstrapAcceptor(
ch, currentChildGroup, currentChildHandler, currentChildOptions, currentChildAttrs));
}
});
}
});
}
创建 ServerBootstrapAcceptor 对象,添加到 pipeline 中。为什么使用 EventLoop 执行添加的过程?如果启动器配置的处理器,并且 ServerBootstrapAcceptor 不使用 EventLoop 添加,则会导致 ServerBootstrapAcceptor 添加到配置的处理器之前。示例代码如下:
ServerBootstrap b = new ServerBootstrap();
b.handler(new ChannelInitializer<Channel>() {
@Override
protected void initChannel(Channel ch) {
final ChannelPipeline pipeline = ch.pipeline();
ch.eventLoop().execute(new Runnable() {
@Override
public void run() {
pipeline.addLast(new LoggingHandler(LogLevel.INFO));
}
});
}
});
ServerBootstrapAcceptor 也是一个 ChannelHandler 实现类,用于接受客户端的连接请求。详细解析,见后续文章。
该 ChannelInitializer 的初始化的执行,在 AbstractChannel#register0(ChannelPromise promise)
方法中触发执行。
那么为什么要使用 ChannelInitializer 进行处理器的初始化呢?而不直接添加到 pipeline 中。例如修改为如下代码:
final Channel ch = channel;
final ChannelPipeline pipeline = ch.pipeline();
// 添加配置的 ChannelHandler 到 pipeline 中。
ChannelHandler handler = config.handler();
if (handler != null) {
pipeline.addLast(handler);
}
// 添加 ServerBootstrapAcceptor 到 pipeline 中。
// 使用 EventLoop 执行的原因,参见 https://github.com/lightningMan/netty/commit/4638df20628a8987c8709f0f8e5f3679a914ce1a
ch.eventLoop().execute(new Runnable() {
@Override
public void run() {
System.out.println(Thread.currentThread() + ": ServerBootstrapAcceptor");
pipeline.addLast(new ServerBootstrapAcceptor(
ch, currentChildGroup, currentChildHandler, currentChildOptions, currentChildAttrs));
}
});
EventLoop#execute(Runnable runnable)
方法,会抛出 Exception in thread "main" java.lang.IllegalStateException: channel not registered to an event loop
异常。更多源码信息参见:码云
引用:
一个例子让就能你彻底理解Java的Future模式,Future类的设计思想
netty源码分析之-Future、ChannelFuture与ChannelPromise详解
开源异步并行框架,完成任意的多线程编排、阻塞、等待、串并行结合、强弱依赖
本文仅供笔者本人学习,有错误的地方还望指出,一起进步!望海涵!
关于Netty核心组件简介:Netty核心组件简介
转载请注明出处!
欢迎关注我的公共号,无广告,不打扰。不定时更新Java后端知识,我们一起超神。
——努力努力再努力xLg
加油!