前边文章的代码中,我们经常往pipeline中添加ChannelHandler来进行前后顺序控制处理实际业务。是不是类似Servlet和Filter过滤器,利用职责链模式的思想,通过一个一个的Handler进行业务传递处理。下边这个图感觉挺合适:
好,这篇文章主要对ChannelPipeline进行总结学习,首先看下总结思维导图:
一,功能说明:
1,ChannelPipeline和ChannelHandler的事件处理模型:
如图,a,一个消息被socketChannel read()方法读取ByteBuf出发ChannelRead事件,由I/O线程NioEventLoop调用ChannelPipeline的fireChannelRead方法,将消息传输到ChannelPipeline中;b,消息一次被ChannelHandler处理,这个过程中任何ChannelHandler都可以中断当前的流程,结束消息的传递;c,处理完请求后,进行响应,调用ChannelHandlerContext的write方法发送消息,然后消息倒着在ChannelHandler中进行依次处理,直到最后哦socket进行write。
Netty中事件分为inbound事件(通常由I/O线程触发,例如TCP链路建立事件、链路关闭事件、读事件、异常通知时间等,对应上图的左半部分)和outbound事件(通常由用户主动发起的网络I/O操作,例如用户发起的连接操作、绑定操作、消息发送等操作,对应上图的右半部分)
事件方法 | 说明 |
---|---|
ChannelHandlerContext fireChannelRegistered(); | Channel注册事件 |
ChannelHandlerContext fireChannelActive(); | Tcp链路建立成功,Channel激活事件 |
ChannelHandlerContext fireChannelRead(Object msg); | 读事件 |
ChannelHandlerContext fireChannelReadComplete(); | 读操作完成通知事件 |
ChannelHandlerContext fireExceptionCaught(Throwable cause); | 异常通知事件 |
ChannelHandlerContext fireUserEventTriggered(Object event); | 用户自定义事件 |
ChannelHandlerContext fireChannelWritabilityChanged(); | Channel的可写状态变化通知事件 |
ChannelHandlerContext fireChannelInactive(); | Tcp连接关闭,链路不可用通知事件 |
事件方法 | 说明 |
---|---|
ChannelFuture bind(SocketAddress localAddress, ChannelPromise promise); | 绑定本地地址事件 |
ChannelFuture connect(SocketAddress remoteAddress, ChannelPromise promise); | 连接服务端事件 |
ChannelFuture write(Object msg, ChannelPromise promise); | 发送事件 |
ChannelHandlerContext flush(); | 刷新事件 |
ChannelHandlerContext read(); | 读事件 |
ChannelFuture disconnect(ChannelPromise promise); | 断开连接事件 |
ChannelFuture close(ChannelPromise promise); | 关闭当前Channel事件 |
2,自定义拦截器:也就是前边我们经常写的各种业务处理handler,例如:处理编解码的,处理半包问题的,处理加解密的,处理分隔符的…… 通常我们只需要继承ChannelHandlerAdapter类覆盖自己关心的方法即可。前边的例子写了非常多,大家可以回去看下,这里简单举个:
/**
* 拦截active时间,进行日志打印
*/
public class MyInboundHandler extends ChannelHandlerAdapter {
@Override
public void channelActive(ChannelHandlerContext ctx) throws Exception {
System.out.println("tcp connection");
ctx.fireChannelActive();
}
}
3,构建pipeline:其实回顾前边的例子,大家应该可以总结出来,其实不需要我们进行创建。Netty中使用ServerBootstrap或者bootstrap启动服务端或者客户端时,会为每个Channel链接创建一个独立的pipeline,我们只需要将自定义的Handler加入到pipeline中即可。(前边我们都是通过addLast()进行添加保证一定的顺序)。
4,主要特性:ChannelPipeline支持动态的添加或者删除ChannelHandler。一些业务场景会非常使用的。ChannelPipeline是线程安全,即可以运行多个业务线程并发操作ChannelPipeline,不存在多线程并发问题。但是ChannelHandler却不是线程安全,需要我们自己进行编码控制。
二,源码阶段:
1,类关系图:非常简单的,一个接口,一个默认实现类。
2,对ChannelHandler的管理:ChannelPipeline是ChannelHandler的管理容器,负责ChannelHandler的增删改查。类似Map的容器实现,看下新增方法吧,其它的也可以这样跟踪理解一下:
/**
* 1,添加方法
**/
@Override
public ChannelPipeline addBefore(String baseName, String name, ChannelHandler handler) {
return addBefore((ChannelHandlerInvoker) null, baseName, name, handler);
}
/**
* 1.1,调用添加方法
**/
@Override
public ChannelPipeline addBefore(
ChannelHandlerInvoker invoker, String baseName, String name, ChannelHandler handler) {
//注意synchronized关键字,来保证并发安全,保证同步块内所有操作的原子性。
synchronized (this) {
//第一步
AbstractChannelHandlerContext ctx = getContextOrDie(baseName);
//第二步
name = filterName(name, handler);
//第三步
addBefore0(name, ctx, new DefaultChannelHandlerContext(this, invoker, name, handler));
}
return this;
}
//第一步,根据baseName获取它对应的DefaultChannelHandlerContext.
private AbstractChannelHandlerContext getContextOrDie(String name) {
AbstractChannelHandlerContext ctx = (AbstractChannelHandlerContext) context(name);
if (ctx == null) {
throw new NoSuchElementException(name);
} else {
return ctx;
}
}
@Override
public ChannelHandlerContext context(String name) {
if (name == null) {
throw new NullPointerException("name");
}
synchronized (this) {
return name2ctx.get(name);
}
}
//第二步:对新增的handler名进行重复性校验
private String filterName(String name, ChannelHandler handler) {
if (name == null) {
return generateName(handler);
}
if (!name2ctx.containsKey(name)) {
return name;
}
throw new IllegalArgumentException("Duplicate handler name: " + name);
}
//第三部:进行添加
private void addBefore0(
final String name, AbstractChannelHandlerContext ctx, AbstractChannelHandlerContext newCtx) {
//校验
checkMultiplicity(newCtx);
//相当于向一个链表中添加了项应该好理解
newCtx.prev = ctx.prev;
newCtx.next = ctx;
ctx.prev.next = newCtx;
ctx.prev = newCtx;
name2ctx.put(name, newCtx);
callHandlerAdded(newCtx);
}
//进行ctx校验:!h.isSharable() && h.added
private static void checkMultiplicity(ChannelHandlerContext ctx) {
ChannelHandler handler = ctx.handler();
if (handler instanceof ChannelHandlerAdapter) {
ChannelHandlerAdapter h = (ChannelHandlerAdapter) handler;
if (!h.isSharable() && h.added) {
throw new ChannelPipelineException(
h.getClass().getName() +
" is not a @Sharable handler, so can't be added or removed multiple times.");
}
h.added = true;
}
}
3,inbound事件:上边已经介绍了功能,这边以fireChannelActive为例,看下:
/**
* 调用head.fireChannelActive()后,判断当前channel是否自动读取,如果是,调用read方法
**/
@Override
public ChannelPipeline fireChannelActive() {
head.fireChannelActive();
if (channel.config().isAutoRead()) {
channel.read();
}
return this;
}
4,outbound事件:上边介绍了其功能,我们来用connect举例看下:
//Pipeline本身不直接进行I/O操作的,前边看Channel和Unsafe的时候,都是有它俩进行真正IO操作的。通过方法追踪,最终是可以验证到unsafe身上的。
@Override
public ChannelFuture connect(SocketAddress remoteAddress, SocketAddress localAddress) {
return tail.connect(remoteAddress, localAddress);
}
@Override
public void connect(
ChannelHandlerContext ctx,
SocketAddress remoteAddress, SocketAddress localAddress,
ChannelPromise promise) throws Exception {
unsafe.connect(remoteAddress, localAddress, promise);
}
ChannelPipelie其实还是比较简单的,主要做了Handler的管理,还有就是各种事件的触发响应。有没有感觉就像“管事的”,管着工作怎么流转,到什么地方需要处理什么工序,有什么事情通知给他,他根据不同事情,找不同的人进行处理。嗯,大概就是这样。大家怎么看。。。