Netty中对于ChannelPipeline责任链的个人理解总结,以及对于客户端和服务端的Channel的理解

Netty中对于ChannelPipeline责任链的个人理解总结,以及对于客户端和服务端的Channel的理解

Channel客户端和服务器端都有

在Netty中,Channel表示是对端的连接。比如说如果是在服务器端,那么每一个客户端来连接自己,服务器就会为这个客户端创建一个Channel,所以对于服务器来说一个Channel就表示一个客户端;
如果是在客户端,那么客户端可能需要连接多台服务器,客户端每连接一个服务器,就会为这个服务器创建一个对应的Channel,所以对于客户端来说也可以把一个Channel理解成一个服务器。

因此在Netty中你会发现,不管是Client客户端,还是Server服务器端,它都会有一个Channel,这个Channel会与唯一一个ChannelPipeline通道关联,通道里面是一个由多个ChannelHandler组成的处理器链。Netty的这种思想内部使用的是责任链设计模式。

Netty中的入站和出站指的是什么

我刚开始理解的是,凡是客户端到服务器的请求都是入站,凡是服务器到客户端的请求都是出站,这种理解非常的错误;

入站和出站其实与客户端服务器没有太大的关系,入站和出站是相对于网络和应用程序的,如果是网络中的数据流入到应用程序中,那么就是入站;如果是应用程序中的数据流出到网络中,那么就是出站。如下图:
在这里插入图片描述
从上图中可以发现,对于客户端来说,它的编码处理器就是一个出站处理器,为什么呢?因为客户端在给服务器端发送请求的时候,需要先把数据流入到网络中,而从应用程序流入网络,这就是出站;
而对于客户端来说它的解码处理器就是一个入站处理器,为什么呢?因为服务端在接收客户端的数据的时候,是从网络中接收的,而从网络流入应用程序,这就是出站。
因此是入站还是出站和客户端服务器端没有太大的关系,主要是网络与应用程序有关系,如果你的数据是从应用程序流向网络的,那么就是出站;如果你的数据是从网路流向应用程序的,那么就是入站。

Netty中的ChannelHandler构成的责任链是什么样的?

首先看下官方说明:

  • 官方说每个Channel都会唯一关联一个ChannelPipeline通道。
  • 官方说一个ChannelPipeline通道里面会有多个ChannelHandler组成一个链条。
  • 官方说内部是使用的ChannelHandlerContext组成的双向链表。
  • 官方说Netty里面的ChannelHandlerContext上下文对象不是全局唯一的,和其他地方的上下文对象设计可能略有不同。
  • 官方说双向链表的每个节点对应的ChannelHandlerContext中都有一个唯一对应的ChannelHandler处理器。

大致的图像模型如下图:
在这里插入图片描述

但是只看官方说明我还是没有理解内部的运行过程,因此我进行了一个自己的转化,可能底层思想会有些许的偏差,但是对待每一应用场景的时候确实可以用我这一套理论解释,因此我就先这样理解,以后随着自己了解逐渐加深再更正。
现在以客户端的角度,看一下它的入站和出站是怎么经理ChannelHandler责任链上的每个处理器的
看下我们自定义的责任链中每个ChannelHandler的顺序,如下图:
在这里插入图片描述
所以我们转换之后,责任链上的ChannelHandler处理器的顺序就如下图:
在这里插入图片描述
假设我们远程已经有个服务器启动成功在等待我们客户端连接了,那么这个时候我去启动客户端,启动完之后,当客户端连接服务器成功的时候会自动的执行clienthandlerInbound中的连接服务器成功的方法,这个过程不牵涉到入站出站,方法如下图:
在这里插入图片描述
其实也就是当客户端连接成功之后会去执行channelActive方法,然后会往网络中冲刷数据,这个时候就牵涉到了出站操作了。那么出站的时候会调用哪些ChannelHandler处理器呢?首先我们需要有个往网络中冲刷数据的ChannelHandler,出站的时候就是从这个ChannelHandler为起始点,因为是出站,所以就是从这个ChannelHandler起始点往链表的头部head方向,也就是链表的左边以此调用责任链上的每个ChannelHandler处理器,因为我们是从应用程序往网络冲刷数据,因此是出站操作,因此我们往左调用责任链的ChannelHandler处理对象的时候只会调用outBoundHandler出站处理器,因此对于我们上面的情况,责任链中只有一个出站处理器就是encodeOutbound,因此出站的时候只会执行这一个ChannelHandler处理器,我本地打断点测试也确实是如此。

怎样验证客户端出站操作的时候是以clientHandlerInbound这个处理器为起始点呢?我们可以再写一个出站处理器ChannelHandler,然后把这个处理器加入到责任链的最尾端,也就是加入到clientHandlerInbound这个起始点处理器的后面,只要我们尾端的新加的这个出站处理器不被执行,那么就证明了客户端出站操作的时候是以clientHandlerInbound这个处理器为起始点的。
代码添加新的出站处理器如下图:
在这里插入图片描述
看一下此时的责任链的结构 如下图:
在这里插入图片描述
因为我们出站操作的时候,是往链表的头部执行,也就是从当前起始点clientHandlerInbound往左执行,因此是不能执行到newOutbound的,结果也确实是没有执行到,本地调试的测试结果。出站的时候只执行了encoderOutbound这个处理器。
但是我如果把newOutbound放在clientHandlerInbound起始点ChannelHandler处理器的前面的话,出站的话就会先执行newOutbound出站处理器,然后再执行encoderOutbound出站处理器了。把addAfter换成addBefore就可以了,代码如下:
在这里插入图片描述

上面说的是客户端出站,从客户端应用程序往网络中冲刷数据,那如果是客户端入站呢?也就是从网络中往客户端应用程序流入数据,这个时候还可以用上面的思想理解吗?这个时候clienthandlerInbound处理器就不是起点了,而是终点。
具体的思想逻辑就是,客户端业务处理的那个ChannelHandler,就是接收服务端传送的实际的数据的那个ChannelHandler不是起点就是终点。那么起点是谁呢?对于客户端应用程序往网络中冲刷数据,起点就是clientHandlerInbound,然后从责任链中的这个处理器往左以此调用执行其他的出站处理器;那么现在问题来了,对于入站的顺序呢?对于入站的起点处理器是谁?对于入站的顺序是从责任链的头部处理器,也就是从责任链的左边的第一个处理器开始,以此向右执行所有的入站处理器,并不一定走到责任链的最右边,也就是责任链的尾端,因为有个终点处理器,就是我们责任链最重要的思想还是处理逻辑,所以我们的最终处理器就是业务逻辑处理器,也就是最终接收到客户端实际数据的那个处理器,这里是clientHandlerInbound如下图:
在这里插入图片描述
就算没有执行到最终处理器,入站的时候,可能到不了终点业务处理器也会执行完毕,因为可能执行到责任链中间的某个处理器的时候,这个处理器不让往下继续执行了,就是不会把请求处理传送给下一个处理器了,如下图:
在这里插入图片描述

总结一下其实就是,看一下我们之前的责任链结构 如下图:
在这里插入图片描述
现在我们想要加一个新的入站处理器newInbound,假如我们加在了clienthandlerInbound这个最终的入站处理器的右边,那么新加的入站处理器newInbound后面就不会被执行到了;但是如果我们把newInbound放到clientHandlerInbound终点入站处理器的前面的话,它就可以被执行到了。
执行入站操作的起点是责任链头部及往右的第一个入站处理器,这里因为第一个入站处理器是decoderInbound,因此执行入站操作的时候,执行的第一个入站处理器就是decoderInbound。

详细代码参考码云:https://gitee.com/xuanyuanzy/netty.git

  • 30
    点赞
  • 26
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
Netty 客户端服务端发送数据需要经过以下步骤: 1. 创建一个 Bootstrap 实例,用于启动客户端。 2. 设置客户端的 EventLoopGroup,用于处理网络事件。 3. 配置客户端Channel,包括 Channel 的类型、ChannelPipeline 的配置等。 4. 连接到服务端,使用 connect() 方法连接到服务端。 5. 在连接成功后,向服务端发送数据,可以通过 ChannelHandlerContext 的 writeAndFlush() 方法实现。 以下是一个简单的示例代码: ```java EventLoopGroup group = new NioEventLoopGroup(); Bootstrap bootstrap = new Bootstrap(); bootstrap.group(group) .channel(NioSocketChannel.class) .handler(new ChannelInitializer<SocketChannel>() { @Override protected void initChannel(SocketChannel ch) throws Exception { ChannelPipeline pipeline = ch.pipeline(); pipeline.addLast(new StringEncoder()); pipeline.addLast(new StringDecoder()); pipeline.addLast(new ClientHandler()); } }); ChannelFuture future = bootstrap.connect("localhost", 8888).sync(); future.channel().writeAndFlush("Hello, server!"); future.channel().closeFuture().sync(); group.shutdownGracefully(); ``` 在上面的示例,我们创建了一个 Bootstrap 实例,并设置了客户端的 EventLoopGroup 和 Channel。然后使用 connect() 方法连接到服务端,并在连接成功后向服务端发送了一条消息。注意,在这里我们使用了 ChannelHandlerContext 的 writeAndFlush() 方法来发送数据。最后,我们等待连接关闭并关闭 EventLoopGroup。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Mr-X~

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值