netty新版本源码阅读(4.0.9.final)
来源:互联网 发布:农村淘宝网点查询系统 编辑:程序博客网 时间:2024/05/08 01:50
好久没有看netty了,今天无意中一个东西用到了netty,发现原来新版本的final都已经出来了。。
但是我以前看的都是4.0的rc版本。。。瞬间觉得好郁闷。。。
不过起码以前看过了netty的代码,它的主要的脉络还是一样的。。
在这里还要赞一下final版本,它对数据的处理变得更简单了,,看起来更清晰。。。。
这里就拿数据的读取来举例子吧:
(1)还是先从nioeventloop中获取有数据可以读的channel
(2)利用channel的unsafe对象来将数据读取出来
(3)调用当前channel的pipeline上的fireChannelRead方法,用于从pipeline上由前向后的调用handler,并用合适的handler来处理刚刚读取的数据,这里所谓的合适就是inboundhandler,最新版本不再分byte类型和message类型了,这个其实思路比老版本更清晰。。。
好了,那么接下来还是进入老话题,开始看进入源代码,不过要记住这里看的已经是最新版本的源代码了,那么就从数据读取的入口开始,也就是processSelectedKey函数:
private static void processSelectedKey(SelectionKey k, AbstractNioChannel ch) { final NioUnsafe unsafe = ch.unsafe(); if (!k.isValid()) { // close the channel if the key is not valid anymore unsafe.close(unsafe.voidPromise()); return; } try { int readyOps = k.readyOps(); if ((readyOps & (SelectionKey.OP_READ | SelectionKey.OP_ACCEPT)) != 0 || readyOps == 0) { unsafe.read(); //调用当前channel的unsafe对象来读取数据 if (!ch.isOpen()) { // Connection already closed - no need to handle write. return; } } if ((readyOps & SelectionKey.OP_WRITE) != 0) { // Call forceFlush which will also take care of clear the OP_WRITE once there is nothing left to write ch.unsafe().forceFlush(); } if ((readyOps & SelectionKey.OP_CONNECT) != 0) { // remove OP_CONNECT as otherwise Selector.select(..) will always return without blocking // See https://github.com/netty/netty/issues/924 int ops = k.interestOps(); ops &= ~SelectionKey.OP_CONNECT; k.interestOps(ops); unsafe.finishConnect(); } } catch (CancelledKeyException e) { unsafe.close(unsafe.voidPromise()); } }其实这部分代码没有太大的变化,直接就是获取当前的channel,然后调用该channel的unsafe对象的read函数来进行下一步的处理就是了。。。。
好吧,那么接下来我们来看看unsafe对象的read方法做了什么工作吧:
private final class NioByteUnsafe extends AbstractNioUnsafe { private RecvByteBufAllocator.Handle allocHandle; @Override public void read() { assert eventLoop().inEventLoop(); final SelectionKey key = selectionKey(); final ChannelConfig config = config(); if (!config.isAutoRead()) { int interestOps = key.interestOps(); if ((interestOps & readInterestOp) != 0) { // only remove readInterestOp if needed key.interestOps(interestOps & ~readInterestOp); } } //当前channel的pipeline final ChannelPipeline pipeline = pipeline(); RecvByteBufAllocator.Handle allocHandle = this.allocHandle; if (allocHandle == null) { this.allocHandle = allocHandle = config.getRecvByteBufAllocator().newHandle(); } final ByteBufAllocator allocator = config.getAllocator(); //buffer的allocater final int maxMessagesPerRead = config.getMaxMessagesPerRead(); boolean closed = false; Throwable exception = null; ByteBuf byteBuf = null; int messages = 0; try { for (;;) { byteBuf = allocHandle.allocate(allocator); int localReadAmount = doReadBytes(byteBuf); //将数据读进来 if (localReadAmount == 0) { byteBuf.release(); byteBuf = null; break; } if (localReadAmount < 0) { closed = true; byteBuf.release(); byteBuf = null; break; } pipeline.fireChannelRead(byteBuf); //调用pipeline上面的handler来处理数据 allocHandle.record(localReadAmount); //记录已经读取的数据量 byteBuf = null; if (++ messages == maxMessagesPerRead) { break; } } } catch (Throwable t) { exception = t; } finally { if (byteBuf != null) { if (byteBuf.isReadable()) { pipeline.fireChannelRead(byteBuf); } else { byteBuf.release(); } } pipeline.fireChannelReadComplete(); //表示读取已经完成了。。。 if (exception != null) { if (exception instanceof IOException) { closed = true; } pipeline().fireExceptionCaught(exception); } if (closed) { //有异常了,那就需要关掉 setInputShutdown(); if (isOpen()) { if (Boolean.TRUE.equals(config().getOption(ChannelOption.ALLOW_HALF_CLOSURE))) { key.interestOps(key.interestOps() & ~readInterestOp); pipeline.fireUserEventTriggered(ChannelInputShutdownEvent.INSTANCE); } else { close(voidPromise()); } } } } } }这部分就出现了与老版本不太一样的地方吧,读取了数据之后,在unsafe对象里面调用pipeline的fireChannelRead来处理读取进来的数据,记得在老版本里面实在外面调用这个方法的吧,。。。
不管了,都是小事情,那么下面来看看pipeline上上面有什么更新么。。
//表示当前channel有数据读了进来,保存在msg里面,一般情况下它就是一个bytebuf //从头开始向后找inboundhandler来处理数据 public ChannelPipeline fireChannelRead(Object msg) { head.fireChannelRead(msg); return this; }到这里并没有什么好多说的,直接来看fireChannelRead方法吧:
public ChannelHandlerContext fireChannelRead(final Object msg) { if (msg == null) { throw new NullPointerException("msg"); } //找到第一个inboundhandler就可以了,这里不再分byte和message类型什的 final DefaultChannelHandlerContext next = findContextInbound(); EventExecutor executor = next.executor(); if (executor.inEventLoop()) { next.invokeChannelRead(msg); //调用方法来处理数据 } else { executor.execute(new Runnable() { @Override public void run() { next.invokeChannelRead(msg); } }); } return this; }这里findcontextinbound的时候,就不会再区分什么byte类型或者message类型的handler了,统一就一种类型,那就是object,其实这样一来反而更简单了,没有以前那么繁琐,开发人员难道不知道自己要处理的数据类型是什么么。。以前真的有点画蛇添足的感觉。。。而且也没有了什么buff用于存储这些数据,从开始就只有这一个数据,你爱怎么处理怎么处理
private void invokeChannelRead(Object msg) { try { ((ChannelInboundHandler) handler).channelRead(this, msg); } catch (Throwable t) { notifyHandlerException(t); } }另外这里也有跟老版本方法有不同的地方,这里是channelRead方法,直接来处理这个数据,而以前是inboundbufferupdated方法,听名字就觉得现在的方法更清晰。。。
也就是说我们现在的handler里面也将会使用channelRead方法了。。。。
到这里整个数据的读取流程就差不多了。。。
总的来说,主要的核心部分与老版本还是保持一致的,不过新版本的netty变的更简洁了。。赞一下。。
- netty新版本源码阅读(4.0.9.final)
- Netty源码阅读(一) ServerBootstrap启动
- Netty源码阅读(一) ServerBootstrap启动
- Netty源码阅读
- Netty源码阅读之一:综述
- Netty源码阅读笔记2: 线程模型
- Netty 源码阅读之初始环境搭建
- Netty 4.0 源码分析(四):ByteBuf
- Netty 4.1.2.Final 和 4.0.38.Final 更新内容
- Netty源码阅读笔记1:ChannelPipeline责任链模式
- netty源码探索(二)
- Netty 源码分析(一)
- Netty 源码分析(二)
- Netty 源码分析(三)
- Netty API 中文翻译(4.1.11.Final) —— 缘由
- Netty 之 netty源码学习之netty server端源码初读(上)
- Netty之 netty源码学习之netty server端源码初读(下)
- Netty源码解读(二)Netty中的buffer
- sigsuspend的用法
- linux下 ls -l 命令显示结果每一列代表什么意思
- HDU1068-Girls and Boys
- 3款移动应用数据统计分析平台对比
- 5 Open操作和Close操作
- netty新版本源码阅读(4.0.9.final)
- xargs 命令
- 计算机经典书籍汇总
- 【读书笔记】Objective-C编程之道:iOS设计设计模式解析(1)-原型模式
- javamail收发邮件比较全面
- HDU 4715题解
- MySQL查询优化:LIMIT 1避免全表扫描
- 别再让C++头文件中出现“using namespace xxx;”
- 关于窗口前端显示和置顶