在前两篇文章中,介绍了netty的整体启动流程,以及请求处理、数据读写的流程。但是对于netty来说,串联整个请求处理的核心组件是通过pipeline,所以本篇我们梳理下相关的概念。
各个组件的关系如下:
channel 维护一个channelPipeline,而channelPipeline通过内部持有的双向链表头和尾部节点。而每个channelHandler又封装在channelHandlerContext中。
既然了解了基本组件的关系,接下来我们从pipeLine的初始化、添加节点、删除节点进行分析,以及介绍是如何通过channelPipeline进行数据读写的过程的。
pipeline 初始化
在上一篇文章中,NioServerSocketChannel
通过反射的方式来实例化,进而会调用父类初始化,所以就会创建一个pipeLine属性。
protected AbstractChannel(Channel parent) {
this.parent = parent;
id = newId();
// 用于负责底层的connect register read write操作等
unsafe = newUnsafe();
// 底层实际是 DefaultChannelPipeline
// 每个Channel都有自己的pipeline,当有请求事件发生时,pipeline负责调用相应的hander进行处理
pipeline = newChannelPipeline();
}
// 底层实际是new了一个 DefaultChannelPipeline
protected DefaultChannelPipeline newChannelPipeline() {
return new DefaultChannelPipeline(this);
}
可以看到构造方法中,构建了头和尾部节点。然后首尾相连接。
protected DefaultChannelPipeline(Channel channel) {
// 异步处理结果
succeededFuture = new SucceededChannelFuture(channel, null);
voidPromise = new VoidChannelPromise(channel, true);
// 构建了一个头节点和尾部节点
tail = new TailContext(this);
head = new HeadContext(this);
// 头节点的next 指向 尾节点
head.next = tail;
// 尾节点前置节点 指向 头节点
tail.prev = head;
}
class TailContext extends AbstractChannelHandlerContext implements ChannelInboundHandler
class HeadContext extends AbstractChannelHandlerContext implements ChannelOutboundHandler,ChannelInboundHandler
可以看到头节点即是一个InboundHandler
也是一个 OutboundHandler
,而尾部节点只是一个 InboundHandler
pipeline 添加节点
.childHandler(new ChannelInitializer<SocketChannel>() {
@Override
protected void initChannel(SocketChannel ch) throws Exception {
ch.pipeline().addLast(new LoggingHandler());
}
})
前置处理
通过遍历多个handler进行添加,如果是多个的话
public final ChannelPipeline addLast(EventExecutorGroup executor, ChannelHandler... handlers) {
ObjectUtil.checkNotNull(handlers, "handlers");
for (ChannelHandler h: handlers) {
if (h == null) {
break;
}
addLast(executor, null, h);
}
return this;
}
添加节点
该方法是核心方法,1.判断是否重复 2.生成节点 3.添加到新的节点中。
// group 可以不使用IOeventLoop
// name 是否指定名字,默认的名字策略
// handler 添加的handler
@Override
public final ChannelPipeline addLast(EventExecutorGroup group, String name, ChannelHandler handler) {
final AbstractChannelHandlerContext newCtx;
synchronized (this) {
// 目前handler是不是已经存在pipeline
checkMultiplicity(handler);
// 创建channelcontext
newCtx = newContext(group, filterName(name, handler), handler);
// 添加到双向链表中
addLast0(newCtx);
// If the registered is false it means that the channel was not registered on an eventLoop yet.
// In this case we add the context to the pipeline and add a task that will call
// ChannelHandler.handlerAdded(...) once the channel is registered.
if (!registered) {
newCtx.setAddPending();
callHandlerCallbackLater(newCtx, true);
return this;
}
EventExecutor executor = newCtx.executor();
if (!executor.inEventLoop()) {
callHandlerAddedInEventLoop(newCtx, executor);
return this;
}
}
// 触发add方法
callHandlerAdded0(newCtx);
return this;
}
检查是否重复
检查是否重复,其实就是判断当前节点的added属性是否为true,以及判断包含注解@Sharable,如果不包含共享注解 并且添加过,就抛出异常
private static void checkMultiplicity(ChannelHandler handler) {
if (handler instanceof ChannelHandlerAdapter) {
ChannelHandlerAdapter h = (ChannelHandlerAdapter) handler;
// added 如果添加过 true 否则就是false
// 是否有 @Sharable
// 没有贡献注解并且 已经添加过 抛出异常
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;
}
}
是否可见的
其实就是获取当前Class对象,然后判断是否包含注解
public boolean isSharable() {
Class<?> clazz = getClass();
Map<Class<?>, Boolean> cache = InternalThreadLocalMap.get().handlerSharableCache();
Boolean sharable = cache.get(clazz);
if (sharable == null) {
sharable = clazz.isAnnotationPresent(Sharable.class);
cache.put(clazz, sharable);
}
return sharable;
}
生成名字
private String filterName(String name, ChannelHandler handler) {
// 如果没有默认名字 自动生成
if (name == null) {
return generateName(handler);
}
// 查看是否有冲突
checkDuplicateName(name);
return name;
}
private String generateName(ChannelHandler handler) {
// handler 的名字,通过fastThreadLocal 把名字存储在线程中
Map<Class<?>, String> cache = nameCaches.get();
Class<?> handlerType = handler.getClass();
String name = cache.get(handlerType);
// 如果没有名字
if (name == null) {
// 生成一个名字
name = generateName0(handlerType);
// 存储在线程中
cache.put(handlerType, name);
}
// It's not very likely for a user to put more than one handler of the same type, but make sure to avoid
// any name conflicts. Note that we don't cache the names generated here.
if (context0(name) != null) {
String baseName = name.substring(0, name.length() - 1); // Strip the trailing '0'.
for (int i = 1;; i ++) {
String newName = baseName + i;
if (context0(newName) == null) {
name = newName;
break;
}
}
}
return name;
}
private static String generateName0(Class<?> handlerType) {
// 类民#0
return StringUtil.simpleClassName(handlerType) + "#0";
}
判断后续节点是否有名字冲突。其实就是链表遍历
private AbstractChannelHandlerContext context0(String name) {
AbstractChannelHandlerContext context = head.next;
while (context != tail) {
if (context.name().equals(name)) {
return context;
}
context = context.next;
}
return null;
}
生成节点
private AbstractChannelHandlerContext newContext(EventExecutorGroup group, String name, ChannelHandler handler) {
return new DefaultChannelHandlerContext(this, childExecutor(group), name, handler);
}
添加到双向链表中
private void addLast0(AbstractChannelHandlerContext newCtx) {
AbstractChannelHandlerContext prev = tail.prev;
newCtx.prev = prev;
newCtx.next = tail;
prev.next = newCtx;
tail.prev = newCtx;
}
如何执行具体的流程
上面通过源码的分析可以发现,构成一个双向链表。那么对于客户端发起的请求,是如何将请求发送给channelPipeLine进行执行的。
以read分析
NioByteUnsafe.read
当客户端发起一个写事件时候,服务端对应的开启读操作,然后将数据写入到byteBuf中,通过如下方法调用整个pipeline事件
pipeline.fireChannelRead(byteBuf);
public final ChannelPipeline fireChannelRead(Object msg) {
AbstractChannelHandlerContext.invokeChannelRead(head, msg);
return this;
}
static void invokeChannelRead(final AbstractChannelHandlerContext next, Object msg) {
final Object m = next.pipeline.touch(ObjectUtil.checkNotNull(msg, "msg"), next);
EventExecutor executor = next.executor();
if (executor.inEventLoop()) {
next.invokeChannelRead(m);
} else {
executor.execute(new Runnable() {
@Override
public void run() {
next.invokeChannelRead(m);
}
});
}
}
private void invokeChannelRead(Object msg) {
if (invokeHandler()) {
try {
final ChannelHandler handler = handler();
final DefaultChannelPipeline.HeadContext headContext = pipeline.head;
// 可能是head节点
if (handler == headContext) {
headContext.channelRead(this, msg);
} else if (handler instanceof ChannelDuplexHandler) {
// 这里是自定义的读操作
((ChannelDuplexHandler) handler).channelRead(this, msg);
} else {
((ChannelInboundHandler) handler).channelRead(this, msg);
}
} catch (Throwable t) {
invokeExceptionCaught(t);
}
} else {
fireChannelRead(msg);
}
}
public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
// 将消息传递给下一个channelHandler
ctx.fireChannelRead(msg);
}
public ChannelHandlerContext fireChannelRead(final Object msg) {
invokeChannelRead(findContextInbound(MASK_CHANNEL_READ), msg);
return this;
}
这里其实就是查找下一个channelInHandlerContext。
private AbstractChannelHandlerContext findContextInbound(int mask) {
AbstractChannelHandlerContext ctx = this;
EventExecutor currentExecutor = executor();
do {
ctx = ctx.next;
} while (skipContext(ctx, currentExecutor, mask, MASK_ONLY_INBOUND));
return ctx;
}
对于outBoundHandler来说,是从相反的尾部节点开始操作。
public final ChannelFuture bind(SocketAddress localAddress) {
return tail.bind(localAddress);
}
而至此,也通过3篇文章详细介绍了 启动流程、线程处理流程读写操作,以及pipeline是如何处理事件的。