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 ...
随机推荐
- $releasever 不正确解析
[nginx] gpgcheck=0 baseurl=http://nginx.org/packages/centos/$releasever/$basearch/ name=nginx repo 这 ...
- Redux:store
Store是一个对象.他有如下职责: 1.存放state 2.对外提供访问state的接口: getState() 3.允许state更新:dispatch(action) 4.注册监听器: subs ...
- Unity2.5D Sprite层级显示遮挡问题处理
代码源自游戏<A Place for the Unwilling> 开发<A Place for the Unwilling>游戏第一部要解决的问题就是让精灵可以围绕其它精灵前 ...
- 8.2 Go 锁
8.2 Go 锁 案例(坑):多个goroutine操作同一个map. go提供了一种叫map的数据结构,可以翻译成映射,对应于其他语言的字典.哈希表.借助map,可以定义一个键和值,然后可以从map ...
- Django视图函数之request请求与response响应对象
官方文档: https://docs.djangoproject.com/en/1.11/ref/request-response/ 视图中的request请求对象: 当请求页面时,Django创建一 ...
- BitArray虽好,但请不要滥用,又一次线上内存暴增排查
一:背景 1. 讲故事 前天写了一篇大内存排查在园子里挺火,这是做自媒体最开心的事拉,干脆再来一篇满足大家胃口,上个月我写了一篇博客提到过使用bitmap对原来的List<CustomerID& ...
- 王玉兰201771010128实验二 Java基本程序设计
第一部分:理论知识学习部分: (1)标识符:标识符由字母.下划线.美元符号和数字组成,且第一个符号不能为数字.Hello.$1234.程序名.www_123都是合法标识符.标识符可用作类名.变量名. ...
- 26-13 order by排序
表中数据是集合,集合是没有顺序的.order by返回的数据是有顺序的,故此我们把order by以后返回的数据集合叫“游标”. --------------------------通过order b ...
- Centos 安装 docker 和 docker-compose
一.docker安装 1.卸载旧版本 sudo yum remove docker \ docker-client \ docker-client-latest \ docker-common \ d ...
- java导入web项目httpservlet报错
于是开始了,调错之路. 解决方法:鼠标右击项目工程——>Build Path——>点击comfigure Build Path进入----->选择java Bulid Path--- ...