/**
* 绑定的主流程,各种重载的bind方法底层走到该方法
* 主要包括:initAndRegister和doBind0,
* initAndRegister是创建Channel和绑定用户的Handler,以及将Channel注册到Reactor
* doBind0是绑定端口
*/private ChannelFuture doBind(final SocketAddress localAddress){//1.初始化并注册一个 Channel 对象,注册是异步的过程,因此返回一个ChannelFuture对象。final ChannelFuture regFuture =initAndRegister();final Channel channel = regFuture.channel();//2.发生异常,直接返回if(regFuture.cause()!= null){return regFuture;}if(regFuture.isDone()){// 未//3.到此说明创建Channel和绑定完成且是成功的,因此绑定Channel的端口,并注册 Channel 到 SelectionKey 中。// 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 =newPendingRegistrationPromise(channel);
regFuture.addListener(newChannelFutureListener(){@OverridepublicvoidoperationComplete(ChannelFuture future)throws Exception {
System.out.println(Thread.currentThread()+": PendingRegistrationPromise");
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;}}
doBind0是绑定的具体实现
privatestaticvoiddoBind0(final ChannelFuture regFuture,final Channel channel,final SocketAddress localAddress,final ChannelPromise promise){// This method is invoked before channelRegistered() is triggered. Give user handlers a chance to set up// the pipeline in its channelRegistered() implementation.
channel.eventLoop().execute(newRunnable(){@Overridepublicvoidrun(){
System.out.println(Thread.currentThread()+": bind");// 注册成功,绑定端口if(regFuture.isSuccess()){
channel.bind(localAddress, promise).addListener(ChannelFutureListener.CLOSE_ON_FAILURE);// 注册失败,回调通知 promise 异常}else{
promise.setFailure(regFuture.cause());}}});}
1.2 initAndRegister
initAndRegister是初始化和注册Channel的主流程,内部会调用init方法
/**
* 初始化并注册一个 Channel 对象,注册是异步的过程,返回一个ChannelFuture对象。
* initAndRegister主要包括:创建Channel和绑定用户的Handler,以及将Channel注册到Reactor
*/final ChannelFuture initAndRegister(){
Channel channel = null;try{//1.创建Channel对象,通过ChannelFactory创建
channel = channelFactory.newChannel();//2.初始化Channel配置init(channel);}catch(Throwable t){//3.异常处理,当实例化或者初始化抛出异常的情况下则关闭通道并且返回一个失败状态的ChannelFuture对象。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 GlobalEventExecutorreturnnewDefaultChannelPromise(channel, GlobalEventExecutor.INSTANCE).setFailure(t);}// as the Channel is not registered yet we need to force the usage of the GlobalEventExecutorreturnnewDefaultChannelPromise(newFailedChannel(), GlobalEventExecutor.INSTANCE).setFailure(t);}//4.注册 Channel 到 EventLoopGroup 中 ,返回一个ChannelFuture对象,如果注册失败则关闭连接通道。
ChannelFuture regFuture =config().group().register(channel);if(regFuture.cause()!= null){if(channel.isRegistered()){
channel.close();}else{
channel.unsafe().closeForcibly();// 强制关闭 Channel}}// 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.//5.返回regFutre对象,表示未来注册的结果。如果成功了则表示已经注册成功,则可以安全地调用bind()或者connect方法进行下一步处理了。return regFuture;}
/**
* @see #connect()
* 解析地址并连接的实现主体
*/private ChannelFuture doResolveAndConnect(final SocketAddress remoteAddress,final SocketAddress localAddress){//1.初始化并注册Channel对象,异步注册返回一个ChannelFuture对象final ChannelFuture regFuture =initAndRegister();//得到初始化和注册好的Channel对象final Channel channel = regFuture.channel();if(regFuture.isDone()){//2.若执行失败,直接返回if(!regFuture.isSuccess()){return regFuture;}//3.如果执行成功,就解析地址并连接returndoResolveAndConnect0(channel, remoteAddress, localAddress, channel.newPromise());}else{//4.如果未完成,则给ChannelFuture添加一个监听器,当事件到达的时候执行响应处理,// Registration future is almost always fulfilled already, but just in case it's not.final PendingRegistrationPromise promise =newPendingRegistrationPromise(channel);//5.添加一个监听器用来监听操作完成事件operationComplete。
regFuture.addListener(newChannelFutureListener(){@OverridepublicvoidoperationComplete(ChannelFuture future)throws Exception {// Directly obtain the cause and do a null check so we only need one volatile read in case of a// failure.
Throwable cause = future.cause();if(cause != null){//6.判断若操作完成的时候有异常,则设置PendingRegistrationPromise为失败。// 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{//7.若执行成功,则设置PendingRegistrationPromise为注册成功,最后也会调用doResolveAndConnect0方法,这与第3步是一样的。// Registration was successful, doResolveAndConnect0so set the correct executor to use.// See https://github.com/netty/netty/issues/2586
promise.registered();// 解析地址并连接doResolveAndConnect0(channel, remoteAddress, localAddress, promise);}}});return promise;}}
doResolveAndConnect0
/**
* @see #connect()
* 解析地址并连接的实现
*/private ChannelFuture doResolveAndConnect0(final Channel channel, SocketAddress remoteAddress,final SocketAddress localAddress,final ChannelPromise promise){try{//1.得到当前Channel绑定的一个EventLoop对象和与当前EventLoop对象绑定的一个地址解析器对象final EventLoop eventLoop = channel.eventLoop();final AddressResolver<SocketAddress> resolver =this.resolver.getResolver(eventLoop);//2.如果地址解析器不支持或者已经解析过,则直接使用该远程地址进行连接if(!resolver.isSupported(remoteAddress)|| resolver.isResolved(remoteAddress)){// Resolver has no idea about what to do with the specified remote address or it's resolved already.doConnect(remoteAddress, localAddress, promise);return promise;}//3.解析地址final Future<SocketAddress> resolveFuture = resolver.resolve(remoteAddress);//4.如果解析器正好解析完成,则判断解析结果;如果解析抛出异常则关闭连接,设置状态是失败;如果解析成功则进行连接操作。if(resolveFuture.isDone()){// 解析远程地址失败,关闭 Channel ,并回调通知 promise 异常final Throwable resolveFailureCause = resolveFuture.cause();if(resolveFailureCause != null){// Failed to resolve immediately
channel.close();
promise.setFailure(resolveFailureCause);}else{// Succeeded to resolve immediately; cached? (or did a blocking lookup)// 连接远程地址doConnect(resolveFuture.getNow(), localAddress, promise);}return promise;}// Wait until the name resolution is finished.//5.如果一段时间后才解析完成,则添加一个监听器,监听未来监听的事件。处理逻辑与4相同
resolveFuture.addListener(newFutureListener<SocketAddress>(){@OverridepublicvoidoperationComplete(Future<SocketAddress> future)throws Exception {// 解析远程地址失败,关闭 Channel ,并回调通知 promise 异常if(future.cause()!= null){
channel.close();
promise.setFailure(future.cause());// 解析远程地址成功,连接远程地址}else{doConnect(future.getNow(), localAddress, promise);}}});}catch(Throwable cause){// 发生异常,并回调通知 promise 异常
promise.tryFailure(cause);}return promise;}
/**
* connect连接的主体
*/privatestaticvoiddoConnect(final SocketAddress remoteAddress,final SocketAddress localAddress,final ChannelPromise connectPromise){// This method is invoked before channelRegistered() is triggered. Give user handlers a chance to set up// the pipeline in its channelRegistered() implementation.final Channel channel = connectPromise.channel();
channel.eventLoop().execute(newRunnable(){@Overridepublicvoidrun(){if(localAddress == null){
channel.connect(remoteAddress, connectPromise);}else{
channel.connect(remoteAddress, localAddress, connectPromise);}
connectPromise.addListener(ChannelFutureListener.CLOSE_ON_FAILURE);}});}
/**
* 绑定的主流程,各种重载的bind方法底层走到该方法
* 主要包括:initAndRegister和doBind0,
* initAndRegister是创建Channel和绑定用户的Handler,以及将Channel注册到Reactor
* doBind0是绑定端口
*/private ChannelFuture doBind(final SocketAddress localAddress){//1.初始化并注册一个 Channel 对象,注册是异步的过程,因此返回一个ChannelFuture对象。final ChannelFuture regFuture =initAndRegister();final Channel channel = regFuture.channel();//2.发生异常,直接返回if(regFuture.cause()!= null){return regFuture;}if(regFuture.isDone()){//3.到此说明创建Channel和绑定完成且是成功的,因此绑定Channel的端口,并注册 Channel 到 SelectionKey 中。// 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 =newPendingRegistrationPromise(channel);
regFuture.addListener(newChannelFutureListener(){@OverridepublicvoidoperationComplete(ChannelFuture future)throws Exception {
System.out.println(Thread.currentThread()+": PendingRegistrationPromise");
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;}}
@OverridepublicvoidexceptionCaught(ChannelHandlerContext ctx, Throwable cause)throws Exception {final ChannelConfig config = ctx.channel().config();if(config.isAutoRead()){//1.关闭接受新的客户端连接// stop accept new connections for 1 second to allow the channel to recover// See https://github.com/netty/netty/issues/1328
config.setAutoRead(false);//2.发起 1 秒的延迟任务,恢复重启开启接受新的客户端连接
ctx.channel().eventLoop().schedule(enableAutoReadTask,1, TimeUnit.SECONDS);}//2.继续传播 exceptionCaught 给下一个节点// still let the exceptionCaught event flow through the pipeline to give the user// a chance to do something with it
ctx.fireExceptionCaught(cause);}