Netty源码解析 -- ChannelPipeline机制与读写过程

{"type":"doc","content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"本文继续阅读Netty源码,解析ChannelPipeline事件传播原理,以及Netty读写过程。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"strong"}],"text":"源码分析基于Netty 4.1"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"heading","attrs":{"align":null,"level":4},"content":[{"type":"text","text":"ChannelPipeline"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"Netty中的ChannelPipeline可以理解为拦截器链,维护了一个ChannelHandler链表,ChannelHandler即具体拦截器,可以在读写过程中,对数据进行处理。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"ChannelHandler也可以分为两类。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"strong"}],"text":"ChannelInboundHandler"},{"type":"text","text":",监控Channel状态变化,如channelActive,channelRegistered,通常通过重写ChannelOutboundHandler#channelRead方法处理读取到的数据,如HttpObjectDecoder将读取到的数据解析为(netty)HttpRequest。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"strong"}],"text":"ChannelOutboundHandler"},{"type":"text","text":",拦截IO事件,如bind,connect,read,write,通常通过重写ChannelInboundHandler#write方法处理将写入Channel的数据。如HttpResponseEncoder,将待写入的数据转换为Http格式。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"ChannelPipeline的默认实现类为DefaultChannelPipeline,它在ChannelHandler链表首尾维护了两个特殊的ChannelHandler -- HeadContext,TailContext。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"HeadContext负责将IO事件转发给对应的UnSafe处理,例如前面文章中说到的register,bind,read等操作。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"TailContext主要是一些兜底处理,如channelRead方法释放ByteBuf的引用等。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"heading","attrs":{"align":null,"level":5},"content":[{"type":"text","text":"事件传播"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"strong"}],"text":"ChannelOutboundInvoker"},{"type":"text","text":"负责触发ChannelOutboundHandler的方法,他们方法名相同,只是ChannelOutboundInvoker方法中少了ChannelHandlerContext参数。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"同样,"},{"type":"text","marks":[{"type":"strong"}],"text":"ChannelInboundInvoker"},{"type":"text","text":"负责触发ChannelInboundHandler的方法,但ChannelInboundInvoker的方法名多了fire,如ChannelInboundInvoker#fireChannelRead方法,触发ChannelInboundHandler#channelRead。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"strong"}],"text":"ChannelPipeline"},{"type":"text","text":"和*"},{"type":"text","marks":[{"type":"italic"}],"text":"ChannelHandlerContext"},{"type":"text","text":"*都继承了这两个接口。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"但他们作用不同,ChannelPipeline是拦截器链,实际请求委托给ChannelHandlerContext处理。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"ChannelHandlerContext接口(即ChannelHandler上下文)维护了链表的上下节点,它作为ChannelHandler方法参数, 负责与ChannelPipeline及其他 ChannelHandler互动。通过它可以动态修改Channel的属性,给EventLoop提交任务,也可以向下一个(上一个)ChannelHandler传播事件。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"例如,在ChannelInboundHandler#channelRead处理完数据后,可以通过ChannelHandlerContext#write将数据写到Channel。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"ChannelInboundHandler#handler方法返回真正的ChannelHandler,并使用该ChannelHandler执行实际操作。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"通过DefaultChannelPipeline#addFirst等方法添加ChannelHandler时,Netty会为ChannelHandler构造一个DefaultChannelHandlerContext,handler方法返回对应的ChannelHandler。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"HeadContext,TailContext也实现了AbstractChannelHandlerContext,handler方法返回自身this。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"我们也可以通过ChannelHandlerContext给EventLoop提交异步任务"}]},{"type":"codeblock","attrs":{"lang":""},"content":[{"type":"text","text":"ctx.channel().eventLoop().execute(new Runnable() {\n\tpublic void run() {\n\t\t...\n\t}\n});"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"对于阻塞时间较长的操作,使用异步任务完成是不错的选择。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"下面以DefaultChannelPipeline#fireChannelRead为例,看一下他们的事件传播过程。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"DefaultChannelPipeline"}]},{"type":"codeblock","attrs":{"lang":""},"content":[{"type":"text","text":"public final ChannelPipeline fireChannelRead(Object msg) {\n\tAbstractChannelHandlerContext.invokeChannelRead(head, msg);\n\treturn this;\n}"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"使用HeadContext作为开始节点,调用AbstractChannelHandlerContext#invokeChannelRead方法开始调用拦截器链表。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"AbstractChannelHandlerContext"}]},{"type":"codeblock","attrs":{"lang":""},"content":[{"type":"text","text":"static void invokeChannelRead(final AbstractChannelHandlerContext next, Object msg) {\n\tfinal Object m = next.pipeline.touch(ObjectUtil.checkNotNull(msg, \"msg\"), next);\n\tEventExecutor executor = next.executor();\n\tif (executor.inEventLoop()) {\n\t\tnext.invokeChannelRead(m);\n\t} else {\n\t\t...\n\t}\n}\n\nprivate void invokeChannelRead(Object msg) {\n\tif (invokeHandler()) {\n\t\ttry {\n\t\t\t// #1\n\t\t\t((ChannelInboundHandler) handler()).channelRead(this, msg);\n\t\t} catch (Throwable t) {\n\t\t\tnotifyHandlerException(t);\n\t\t}\n\t} else {\n\t\tfireChannelRead(msg);\n\t}\n}"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"codeinline","content":[{"type":"text","text":"#1"}]}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"handler方法获取AbstractChannelHandlerContext真正的Handler,再触发其ChannelPipeline#channelRead方法"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"由于invokeChannelRead方法在HeadContext中执行,"},{"type":"codeinline","content":[{"type":"text","text":"handler()"}]},{"type":"text","text":"这里返回HeadContext,这时会触发HeadContext#channelRead"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"HeadContext#channelRead"}]},{"type":"codeblock","attrs":{"lang":""},"content":[{"type":"text","text":"public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {\n\tctx.fireChannelRead(msg);\n}"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"HeadContext方法调用"},{"type":"codeinline","content":[{"type":"text","text":"ctx.fireChannelRead(msg)"}]},{"type":"text","text":",就是向下一个ChannelInboundHandler传播事件。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"AbstractChannelHandlerContext#fireChannelRead"}]},{"type":"codeblock","attrs":{"lang":""},"content":[{"type":"text","text":"public ChannelHandlerContext fireChannelRead(final Object msg) {\n\tinvokeChannelRead(findContextInbound(MASK_CHANNEL_READ), msg);\n\treturn this;\n}"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"codeinline","content":[{"type":"text","text":"AbstractChannelHandlerContext#fireChannelRead(final Object msg)"}]},{"type":"text","text":"方法主要负责找到下一个ChannelInboundHandler,并触发其channelRead方法。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"从DefaultChannelPipeline#fireChannelRead方法可以看到一个完整的调用链路:"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"codeinline","content":[{"type":"text","text":"#1"}]},{"type":"text","text":" DefaultChannelPipeline通过HeadContext开始调用"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"codeinline","content":[{"type":"text","text":"#2"}]},{"type":"text","text":" ChannelInboundHandler处理完当前逻辑后,调用"},{"type":"codeinline","content":[{"type":"text","text":"ctx.fireChannelRead(msg)"}]},{"type":"text","text":"向后传播事件"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"codeinline","content":[{"type":"text","text":"#3"}]},{"type":"text","text":" AbstractChannelHandlerContext找到下一个ChannelInboundHandler,并触发其channelRead,从而保证拦截器链继续执行。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"注意:对于ChannelOutboundHandler中的方法,DefaultChannelPipeline从TailContext开始调用,并向前传播事件,与ChannelInboundHandler方向相反。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"大家在阅读Netty源码时,对于DefaultChannelPipeline的方法,要注意该方法底层调用是ChannelInboundHandler还是ChannelOutboundHandler的方法,以及他们的传播方向。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"如果我们定义一个Http回声程序,示意代码如下"}]},{"type":"codeblock","attrs":{"lang":""},"content":[{"type":"text","text":"new ServerBootstrap().group(parentGroup, childGroup)\n .channel(NioServerSocketChannel.class)\n .childHandler(new ChannelInitializer() {\n public void initChannel(SocketChannel ch) throws Exception {\n ChannelPipeline p = ch.pipeline();\n p.addLast(new HttpRequestDecoder());\n\t\t\t\t p.addLast(new HttpResponseEncoder());\n\t\t\t\t p.addLast(new LoggingHandler(LogLevel.INFO));\n\t\t\t\t p.addLast(new HttpEchoHandler());\n }\n });"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"其中HttpEchoHandler实现了ChannelInboundHandler,并在channelRead方法中调用ChannelHandlerContext#write方法回传数据。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"那么,数据流转如下所示"}]},{"type":"codeblock","attrs":{"lang":""},"content":[{"type":"text","text":"Socket.read() -> head#channelRead -> HttpRequestDecoder#channelRead -> LoggingHandler#channelRead -> HttpEchoHandler#channelRead\n |\n \\|/\nSocket.write() HeadContext#write -> AbstractUnsafe#write"}]},{"type":"codeblock","attrs":{"lang":""},"content":[{"type":"text","text":"public final void write(Object msg, ChannelPromise promise) {\n\tassertEventLoop();\n\t// #1\n\tChannelOutboundBuffer outboundBuffer = this.outboundBuffer;\n\t...\n\n\tint size;\n\ttry {\n\t\t// #2\n\t\tmsg = filterOutboundMessage(msg);\n\t\t// #3\n\t\tsize = pipeline.estimatorHandle().size(msg);\n\t\tif (size < 0) {\n\t\t\tsize = 0;\n\t\t}\n\t} catch (Throwable t) {\n\t\tsafeSetFailure(promise, t);\n\t\tReferenceCountUtil.release(msg);\n\t\treturn;\n\t}\n\t// #4\n\toutboundBuffer.addMessage(msg, size, promise);\n}"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"codeinline","content":[{"type":"text","text":"#1"}]},{"type":"text","text":" 获取AbstractUnsafe中维护的ChannelOutboundBuffer,该类负责缓存write的数据,等到flush再实际写数据。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"codeinline","content":[{"type":"text","text":"#2"}]},{"type":"text","text":" AbstractChannel提供给子类的扩展方法,可以做一些ByteBuf检查,转化等操作。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"codeinline","content":[{"type":"text","text":"#3"}]},{"type":"text","text":" 检查待写入数据量"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"codeinline","content":[{"type":"text","text":"#4"}]},{"type":"text","text":" 将数据添加到ChannelOutboundBuffer缓存中。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"可以看到,write并没有真正的写数据,而是将数据放到了一个缓冲对象ChannelOutboundBuffer。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"ChannelOutboundBuffer中的数据要等到ChannelHandlerContext#flush时再写出。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"ByteBuf是Netty中负责与Channel交互的内存缓冲区,而ByteBufAllocator,RecvByteBufAllocator主要负责分配内存给ByteBuf,后面有文章解析它们。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"ChannelOutboundBuffer主要是缓存write数据,等到flush时再一并写入Channel。后面有文章解析它。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"如果您觉得本文不错,欢迎关注我的微信公众号,您的关注是我坚持的动力!"}]},{"type":"image","attrs":{"src":"https://static001.geekbang.org/infoq/da/da6c5c8363dc2a6fc148bc2eab39d883.jpeg","alt":null,"title":"","style":[{"key":"width","value":"50%"},{"key":"bordertype","value":"none"}],"href":"","fromPaste":false,"pastePass":false}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}}]}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章