导读
原创文章,转载请注明出处。
本文源码地址:netty-source-code-analysis
本文所使用的netty版本4.1.6.Final:带注释的netty源码
在"Pipeline的构造"这一节中我们已经讲过了,Pipeline
中利用了责任链模式,而发挥责任链功能的数据结构就是由多个HandlerContext
构成的的双向链表,而每一个HandlerContext
又对应一个ChannelHandler
(组合模式或者继承)。由于每一个HandlerContext
对应一个ChannelHandler
,所以本文行文中有时候会把HandlerContext
和ChannelHandler
等同对待,例如文章中
从当前
HandlerContext
(tail
)开始向head
方向遍历寻找下一个ChannelOutboundHandler
实际上准确来说应该是
从当前
HandlerContext
(tail
)开始向head
方向遍历寻找下一个HandlerContext
,并且该HandlerContext
包含或者实现了ChannelOutboundHandler
。
为了行文不那么拗口,我们就把HandlerContext
和ChannelHandler
等同对待了。
本文我们以客户端建立连接、收发数据为例来学习一下Pipeline
的工作原理。
1 ChannelHandler方法概览
在ChannelPipeline
的构造这篇文章中我们提到过ChannelHandler
,Pipeline
中双向链表由HandlerContext
组成,而每一个HandlerContext
又包含一个ChannleHandler
。我们看一下其中的主要方法,这是所有ChannelHandler
的公共接口,公共接口中主要有两个方法handlerAdded
和handlerRemoved
,这是在ChannelHandler
添加和删除完成之后的回调方法。上一篇文章中我们已经分析过Pipeline
中ChannelHandler
的添加和删除了,这里不再赘述。
public interface ChannelHandler {
void handlerAdded(ChannelHandlerContext ctx) throws Exception;
void handlerRemoved(ChannelHandlerContext ctx) throws Exception;
@Deprecated
void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception
}
我们重点来看ChannelHandler
的两个子接口,ChannelInboundHandler
和ChannelOutboundHandler
。
首先来看ChannelInboundHandler
,ChannelInboundHandler
类上的注释如下
which adds callbacks for state changes. This allows the user
to hook in to state changes easily.
翻译过来就是“Channel状态改变时的回调方法”。其中的方法名多为Channel
+ 动词过去分词的形式。
public interface ChannelInboundHandler extends ChannelHandler {
void channelRegistered(ChannelHandlerContext ctx) throws Exception;
void channelUnregistered(ChannelHandlerContext ctx) throws Exception;
void channelActive(ChannelHandlerContext ctx) throws Exception;
void channelInactive(ChannelHandlerContext ctx) throws Exception;
void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception;
void channelReadComplete(ChannelHandlerContext ctx) throws Exception;
void userEventTriggered(ChannelHandlerContext ctx, Object evt) throws Exception;
void channelWritabilityChanged(ChannelHandlerContext ctx) throws Exception;
void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception;
}
再来看ChannelOutboundHandler
,ChannelOutboundHandler
上的注释如下
which will get notified for IO-outbound-operations.
翻译过来就是“当发生IO出站操作时的回调方法”,其中的方法名多为动词原型的形式。
public interface ChannelOutboundHandler extends ChannelHandler {
void bind(ChannelHandlerContext ctx, SocketAddress localAddress, ChannelPromise promise) throws Exception;
void connect(
ChannelHandlerContext ctx, SocketAddress remoteAddress,
SocketAddress localAddress, ChannelPromise promise) throws Exception;
void disconnect(ChannelHandlerContext ctx, ChannelPromise promise) throws Exception;
void close(ChannelHandlerContext ctx, ChannelPromise promise) throws Exception;
void deregister(ChannelHandlerContext ctx, ChannelPromise promise) throws Exception;
void read(ChannelHandlerContext ctx) throws Exception;
void write(ChannelHandlerContext ctx, Object msg, ChannelPromise promise) throws Exception;
void flush(ChannelHandlerContext ctx) throws Exception;
}
2 ChannelInboundInvoker和ChannelOutboundInvoker
和本文相关的组件中,ChannelPipeline
和ChannelHandlerContext
都实现了ChannelInboundInvoker
和ChannelOutboundInvoker
接口。
ChannelInboundInvoker
内的方法的特点是大部分都是以fire
开头,并且是fire
+ 动词的过去分词的形式。我们知道过去分词是表被动的意思,所以这里呢,一般是Channel
里发生了某些“被动事件”之后调用的。
public interface ChannelInboundInvoker {
ChannelInboundInvoker fireChannelRegistered();
ChannelInboundInvoker fireChannelUnregistered();
ChannelInboundInvoker fireChannelActive();
ChannelInboundInvoker fireChannelInactive();
ChannelInboundInvoker fireExceptionCaught(Throwable cause);
ChannelInboundInvoker fireUserEventTriggered(Object event);
ChannelInboundInvoker fireChannelRead(Object msg);
ChannelInboundInvoker fireChannelReadComplete();
ChannelInboundInvoker fireChannelWritabilityChanged();
}
ChannelOutboundInvoker
内的方法的特点是除了后边几个以new
开头的方法之外,都是以单个或者两个动词原型的形式作为方法名称,比如read
、write
和writeAndFlush
。这里直接用动词原型就是表示主动的意思,所以这里一般是向Channel
发送主动命令
使用的。
public interface ChannelOutboundInvoker {
ChannelFuture bind(SocketAddress localAddress);
ChannelFuture connect(SocketAddress remoteAddress);
ChannelFuture connect(SocketAddress remoteAddress, SocketAddress localAddress);
ChannelFuture disconnect();
ChannelFuture close();
ChannelFuture deregister();
ChannelFuture bind(SocketAddress localAddress, ChannelPromise promise);
ChannelFuture connect(SocketAddress remoteAddress, ChannelPromise promise);
ChannelFuture connect(SocketAddress remoteAddress, SocketAddress localAddress, ChannelPromise promise);
ChannelFuture disconnect(ChannelPromise promise);
ChannelFuture close(ChannelPromise promise);
ChannelFuture deregister(ChannelPromise promise);
ChannelOutboundInvoker read();
ChannelFuture write(Object msg);
ChannelFuture write(Object msg, ChannelPromise promise);
ChannelOutboundInvoker flush();
ChannelFuture writeAndFlush(Object msg, ChannelPromise promise);
ChannelFuture writeAndFlush(Object msg);
ChannelPromise newPromise();
ChannelProgressivePromise newProgressivePromise();
ChannelFuture newSucceededFuture();
ChannelFuture newFailedFuture(Throwable cause);
ChannelPromise voidPromise();
我们来仔细分析一下上文中提到的ChannelInboundHandler
、ChannelInboundInvoker
、ChannelOutboundHandler
和ChannelOutboundInvoker
。
我们看到ChannelInboundHandler
和ChannelInboundInvoker
中的方法非常相似,ChannelInboundHandler
中的方法名多以Channel
加动词过去分词的形式组成,而ChannelInboundInvoker
中的方法就是ChannelInboundHandler
中的方法加上fire
构成。
ChannelOutboundHandler
和ChannelOutboundInvoker
中的方法名几乎一模一样,都是以动词原形作为方法名。
我们来仔细琢磨一下这些方法的命名,Channel
加动词过去分词表示该Channel
发生了某种事件,而fireChannel
加动词过去分词表示在该Channel
上触发这种事件。例如ChannelRead
表示该Channel
上读到了数据事件,fireChannelRead
表示在该Channel
上触发ChannelRead
事件。
直接用动词原形作为方法名,表示命令该Channel
进行某种操作,例如read
表示命令该Channel
进行数据的读取。为什么ChannelOutboundInvoker
中的方法名没有以fire
开头呢,因为ChannelOutbound
传播的不是事件,而是一种主动的操作。
还有同学记得我在“Netty整体架构”这篇文章中提到的“事件”和“命令”吗,在某些地方翻译成“入站事件”和“出站事件”,我个人认为翻译成“事件”和“命令”更容易理解,因为我们感知到“事件”的发生是被动的,而发出“命令”是主动的,都翻译成“事件”不容易理解。
3 以客户端为例分析Pipeline工作原理
本文的示例代码在package com.zhongdaima.netty.analysis.pipeline
中。
服务端示例代码如下,这个服务端的功能就是将所有客户端发过来的数据原封不动地返回。
/**
* 欢迎关注公众号“种代码“,获取博主微信深入交流
*
* @author wangjianxin
*/
public class ServerBoot {
public static void main(String[] args) throws InterruptedException {
EventLoopGroup bossGroup = new NioEventLoopGroup(1);
EventLoopGroup workerGroup = new NioEventLoopGroup(1);
try {
ServerBootstrap b = new ServerBootstrap();
b.group(bossGroup, workerGroup)
.channel(NioServerSocketChannel.class)
.option(ChannelOption.TCP_NODELAY, true)
.childHandler(new ChannelInitializer<NioSocketChannel>() {
@Override
protected void initChannel(NioSocketChannel ch) throws Exception {
ch.pipeline().addLast(new ChannelInboundHandlerAdapter() {
@Override
public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
//连接发过来的数据原样返回
if (msg instanceof ByteBuf) {
ctx.write(msg);
ctx.flush();
}
}
});
}
});
ChannelFuture f = b.bind(8000).sync(