Netty 源码解析: Netty 的 ChannelPipeline
ChannelPipeline和Inbound、Outbound

比如客户端在发起请求的时候,需要 1️⃣connect 到服务器,然后 2️⃣write 数据传到服务器,再然后 3️⃣read 服务器返回的数据,前面的 connect 和 write 就是 out 事件,后面的 read 就是 in 事件。
. pipeline.addLast(new StringDecoder()); . pipeline.addLast(new StringEncoder()); . pipeline.addLast(new BizHandler());
. pipeline.addLast(new StringDecoder());
. pipeline.addLast(new StringEncoder());
. pipeline.addLast(new BizHandler());
客户端连接进来的时候,读取(read)客户端请求数据的操作是 Inbound 的,e 操作是 Outbound 的,此时使用的是 2。
处理完数据后,返回给客户端数据的 write 操作是 Outbound 的,此时使用的是 2。
如果我们在上面的基础上,加上下面的第四行,这是一个 OutboundHandler:. pipeline.addLast(new OutboundHandlerA());那么执行顺序是不是就是 1 -> 3 -> 2 -> 4 呢?答案是:不是的。对于 Inbound 操作,按照添加顺序执行每个 Inbound 类型的 handler;而对于 Outbound 操作,是反着来的,从后往前,顺次执行 Outbound 类型的 handler。所以,上面的顺序应该是先 1 后 3,它们是 Inbound 的,然后是 4,最后才是 2,它们两个是 Outbound 的。说实话,这种组织方式对新手应该很是头疼。那我们在开发的时候怎么写呢?其实也很简单,从最外层开始写,一步步写到业务处理层,把 Inbound 和 Outbound 混写在一起。比如 encode 和 decode 是属于最外层的处理逻辑,先写它们。假设 decode 以后是字符串,那再进来一层应该可以写进来和出去的日志。再进来一层可以写 字符串 <=> 对象 的相互转换。然后就应该写业务层了。

protected AbstractChannel(Channel parent) {
this.parent = parent; // 给每个 channel 分配一个唯一 id
id = newId(); // 每个 channel 内部需要一个 Unsafe 的实例
unsafe = newUnsafe(); // 每个 channel 内部都会创建一个 pipeline
pipeline = newChannelPipeline();
}
Unsafe 类的构造方法是 private 的,但是它提供了 getUnsafe() 这个静态方法:Unsafe unsafe = Unsafe.getUnsafe();大家可以试一下,上面这行代码编译没有问题,但是执行的时候会抛java.lang.SecurityException异常,因为它就不是给我们的代码用的。但是如果你就是想获取 Unsafe 的实例,可以通过下面这个代码获取到:Field f = Unsafe.class.getDeclaredField("theUnsafe");f.setAccessible(true);Unsafe unsafe = (Unsafe) f.get(null);
不过,对于我们源码分析来说,我们还是会有很多时候需要分析 Unsafe 中的源码的
protected DefaultChannelPipeline newChannelPipeline() { return new DefaultChannelPipeline(this);}
protected DefaultChannelPipeline(Channel channel) {
this.channel = ObjectUtil.checkNotNull(channel, "channel");
succeededFuture = new SucceededChannelFuture(channel, null);
voidPromise = new VoidChannelPromise(channel, true);
tail = new TailContext(this);
head = new HeadContext(this);
head.next = tail;
tail.prev = head;
}

注意,在不同的版本中,源码也略有差异,head 不一定是 in + out,大家知道这点就好了。从上面的 head 和 tail 我们也可以看到,其实 pipeline 中的每个元素ChannelHandlerContext 的实例,而不是 ChannelHandler 的实例,context 包装了一下 handler,但是,后面我们都会用 handler 来描述一个 pipeline 上的节点,而不是使用 context,希望读者知道这一点。

我们说过 childHandler 中指定的 handler 不是给 NioServerSocketChannel 使用的,是给 NioSocketChannel 使用的,所以这里我们不看它。
final ChannelFuture initAndRegister() {
Channel channel = null;
try { // 1. 构造 channel 实例,同时会构造
pipeline 实例, // 现在 pipeline 中有 head 和 tail 两个 handler 了
channel = channelFactory.newChannel(); // 2. 看这里
init(channel);
}
catch (Throwable t) { ......}
}
@Override void init(Channel channel) throws Exception {
......
// 拿到刚刚创建的 channel 内部的 pipeline 实例
ChannelPipeline p = channel.pipeline();
...
// 开始往 pipeline 中添加一个 handler,这个 handler 是 ChannelInitializer 的实例
p.addLast(new ChannelInitializer<Channel>() {
// 我们以后会看到,下面这个 initChannel 方法何时会被调用
@Override
public void initChannel(final Channel ch) throws Exception {
final ChannelPipeline pipeline = ch.pipeline();
// 这个方法返回我们最开始指定的 LoggingHandler 实例
ChannelHandler handler = config.handler();
if (handler != null) {
// 添加 LoggingHandler
pipeline.addLast(handler);
}
// 先不用管这里的 eventLoop
ch.eventLoop().execute(new Runnable() {
@Override
public void run() {
// 添加一个 handler 到 pipeline 中:
ServerBootstrapAcceptor
// 从名字可以看到,这个 handler 的目的是用于接收客户端请求
pipeline.addLast(new ServerBootstrapAcceptor(
ch, currentChildGroup,
currentChildHandler,
currentChildOptions,
currentChildAttrs));
}
});
}
});
}

void init(Channel channel) throws Exception {
ChannelPipeline p = channel.pipeline();
p.addLast(config.handler());
...
}



援引原文链接:https://juejin.im/post/5eacc88f6fb9a0437f73a713
Netty 源码解析: Netty 的 ChannelPipeline的更多相关文章
- Netty 源码解析(四): Netty 的 ChannelPipeline
今天是猿灯塔“365篇原创计划”第四篇. 接下来的时间灯塔君持续更新Netty系列一共九篇 Netty 源码解析(一): 开始 Netty 源码解析(二): Netty 的 Channel Netty ...
- Netty源码解析—客户端启动
Netty源码解析-客户端启动 Bootstrap示例 public final class EchoClient { static final boolean SSL = System.getPro ...
- Netty源码解析---服务端启动
Netty源码解析---服务端启动 一个简单的服务端代码: public class SimpleServer { public static void main(String[] args) { N ...
- Netty 源码解析(三): Netty 的 Future 和 Promise
今天是猿灯塔“365篇原创计划”第三篇. 接下来的时间灯塔君持续更新Netty系列一共九篇 Netty 源码解析(一): 开始 Netty 源码解析(二): Netty 的 Channel 当前:Ne ...
- Netty 源码解析(九): connect 过程和 bind 过程分析
原创申明:本文由公众号[猿灯塔]原创,转载请说明出处标注 今天是猿灯塔“365篇原创计划”第九篇. 接下来的时间灯塔君持续更新Netty系列一共九篇 Netty 源码解析(一): 开始 Netty 源 ...
- Netty 源码解析(八): 回到 Channel 的 register 操作
原创申明:本文由公众号[猿灯塔]原创,转载请说明出处标注 今天是猿灯塔“365篇原创计划”第八篇. 接下来的时间灯塔君持续更新Netty系列一共九篇 Netty 源码解析(一): 开始 Netty 源 ...
- Netty 源码解析(七): NioEventLoop 工作流程
原创申明:本文由公众号[猿灯塔]原创,转载请说明出处标注 今天是猿灯塔“365篇原创计划”第七篇. 接下来的时间灯塔君持续更新Netty系列一共九篇 Netty 源码解析(一): 开始 Netty 源 ...
- Netty 源码解析(六): Channel 的 register 操作
原创申明:本文由公众号[猿灯塔]原创,转载请说明出处标注 今天是猿灯塔“365篇原创计划”第六篇. 接下来的时间灯塔君持续更新Netty系列一共九篇 Netty 源码解析(一 ):开始 Netty ...
- Netty 源码解析(五): Netty 的线程池分析
今天是猿灯塔“365篇原创计划”第五篇. 接下来的时间灯塔君持续更新Netty系列一共九篇 Netty 源码解析(一): 开始 Netty 源码解析(二): Netty 的 Channel Netty ...
随机推荐
- JS 把数字转换成字母
JS 把数字转换成字母 2013-03-12 22:28:11 分类: JavaScript String.fromCharCode(addcount+65) 位运算alert(1<<0 ...
- Poj1328 用雷达覆盖所有的岛屿
(此配图来自http://blog.csdn.net/zhengnanlee/article/details/9613161) 图中ABCD为海岛的位置.题目中会给出几个海岛的坐标位置,雷达覆盖半径d ...
- Angular SPA基于Ocelot API网关与IdentityServer4的身份认证与授权(四)
在上一讲中,我们已经完成了一个完整的案例,在这个案例中,我们可以通过Angular单页面应用(SPA)进行登录,然后通过后端的Ocelot API网关整合IdentityServer4完成身份认证.在 ...
- GO 使用Webhook 实现github 自动化部署
通常大家开发大部分是本地git push 提交,服务器上git pull 手动更新.git 可以使用webhook实现自动部署.webhook是仓库平台的一个钩子事件,通过hook 钩子监听代码,回调 ...
- web项目——javax.servlet.ServletException: Circular view path [registerForm]
报错: 控制台输出: 三月 21, 2019 10:12:32 上午 org.springframework.web.servlet.PageNotFound noHandlerFound 警告: N ...
- PHP常量的定义和用法
我们通常把不经常变的值定义成常量,常量一般用全部大写来表示,前面不加美元符号,也可减少团队开发的出错.那么define和const有什么区别呢? 1.const是一个语言结构:而define是一个函数 ...
- [Python基础]010.os模块(2)
os模块(2) 介绍 os 常量 路径 判断路径属性 路径变换 文件属性 相同文件 介绍 - os.path模块,主要处理路径操作,包含了各种处理文件和文件名的方法. os.path 常量 os.pa ...
- 创建多线程的方式&Thread类的常用方法
创建多线程的第一种方式:继承java.lang.Thread类 注意:1.一个线程只能执行一次start() 2.不能通过Thread实现类对象的 run()去启动一个线程 3.增加加一个线程,需要新 ...
- FHQ-Treap学习笔记
平衡树与FHQ-Treap 平衡树(即平衡二叉搜索树),是通过一系列玄学操作让二叉搜索树(BST)处于较平衡的状态,防止在某些数据下退化(BST在插入值单调时,树形不平衡,单次会退化成 \(\math ...
- java的Interger自动包装带来的问题
1 首先看一下以下代码: Integer b=7; Integer c=7; Integer r=234; Integer d=234; System.out.println(b==c); Syste ...