channel介绍:

  netty中channel分为NioServerScoketChannel和NioSocketChannel,分别对应java nio中的ServerScoketChannel和SocketChannel

channel、pipeline、context、handler关系

  ScoketChannel都会注册到EventLoop上的selector中,每个channel内部都会有一个pipeline,pipeline管道 里面由多个handler连接成一个双向链表结构,而handler又由ChannelHandlerContext包裹着,context相当于一个handler上下文,做到了承上启下的作用,

从context可以得到handler,自然也能得到channel和pipeline,context内部有两个指针分别指向前 后context,handler在pipeline向前或向后进行传递,当然顺序只能由一个方向传递

head和tail表示头尾,每一次有io事件进入,netty称为入站 它始终从head入站像后进行传递,反之io事件出去称为出站,也是从head出去,图中入站出站看似形成了一个完整的双向链表,但实际可能还没到tail就结束了,

context分为inbound和outbound,入站和出站会判断当前context是否符合 也是判断context中handler是inboundhandler还是outboundhandler,入站只执行入站的context,出站也是如此,在初始化pipeline会默认创建head、tail,

它们分别表示头 尾 位置固定不许修改,head和tail同时为inbound、outbound

先来看看handler是如何先加入pipeline中的,handler添加顺序无论怎么添加,头都是head 尾都是tail pipeline初始化就固定了

  1. bossGroup.childHandler(new ChannelInitializer<SocketChannel>() {
  2. @Override
  3. public void initChannel(SocketChannel ch) throws Exception {
  4. ChannelPipeline p = ch.pipeline();
         // 像pipeline添加一个handler
  5. p.addLast(new EchoServerHandler());
  6. }
  7. });

    // 将context添加到pipeline的"最后"
    private void addLast0(AbstractChannelHandlerContext newCtx) {
  1. AbstractChannelHandlerContext prev = tail.prev;
    newCtx.prev = prev;
    newCtx.next = tail;
    prev.next = newCtx;
      // 重新指向 依然保持着 tail》context》tail
    tail.prev = newCtx;
    }
  1.  

在创建ServerBootGroup时会给workerGroup分配一个handler,后面每一个NioSocketChannel都会添加一个该handler, ChannelInitializer可以理解为帮助我们创建channel,它是一个inboundhandler,在第一次注册时会调用initChannel来执行我们自定义的实现

  1. public final void channelRegistered(ChannelHandlerContext ctx) throws Exception {// 这里会调用我们实现的initChannel方法,并把ctx中的channel传过去
  2. if (initChannel(ctx)) {
  3. // 初始化成功,这里就是我们入站的入口了,它是从pipeline发起的
  4. ctx.pipeline().fireChannelRegistered();
  5. } else {
  6. // 如果已经初始化过了,说明已经进入pipeline了,直接向后传递
  7. ctx.fireChannelRegistered();
  8. }
  9. }

下面分别列举ChannelInboundHandler和ChannelOutboundHandler的方法让大家知道哪些是入站执行的,哪些是出站执行的

  1. public interface ChannelInboundHandler extends ChannelHandler {
  2.  
  3. // 注册
  4. void channelRegistered(ChannelHandlerContext ctx) throws Exception;
  5.  
  6. // 取消注册
  7. void channelUnregistered(ChannelHandlerContext ctx) throws Exception;
  8.  
  9. // 处于活动状态
  10. void channelActive(ChannelHandlerContext ctx) throws Exception;
  11.  
  12. // 处于非活动状态
  13. void channelInactive(ChannelHandlerContext ctx) throws Exception;
  14.  
  15. // 读取数据
  16. void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception;
  17.  
  18. // 读取数据完毕
  19. void channelReadComplete(ChannelHandlerContext ctx) throws Exception;
  20.  
  21. // 用户自定义事件触发,如发生心跳检测时,会调用该方法把当前心跳事件传播进来
  22. void userEventTriggered(ChannelHandlerContext ctx, Object evt) throws Exception;
  23.  
  24. // channel可写状态改变
  25. void channelWritabilityChanged(ChannelHandlerContext ctx) throws Exception;
  26.  
  27. // 发生异常
  28. void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception;
  29. }
  30.  
  31. public interface ChannelOutboundHandler extends ChannelHandler {
  32.  
  33. // 绑定
  34. void bind(ChannelHandlerContext ctx, SocketAddress localAddress, ChannelPromise promise) throws Exception;
  35.  
  36. // 连接
  37. void connect(ChannelHandlerContext ctx, SocketAddress remoteAddress, SocketAddress localAddress, ChannelPromise promise) throws Exception;
  38.  
  39. // 断开连接
  40. void disconnect(ChannelHandlerContext ctx, ChannelPromise promise) throws Exception;
  41.  
  42. // 关闭
  43. void close(ChannelHandlerContext ctx, ChannelPromise promise) throws Exception;
  44.  
  45. // 注销
  46. void deregister(ChannelHandlerContext ctx, ChannelPromise promise) throws Exception;
  47.  
  48. // 暂时不知道这一步干啥的,按理说read操作是入站操作
  49. void read(ChannelHandlerContext ctx) throws Exception;
  50.  
  51. // 写
  52. void write(ChannelHandlerContext ctx, Object msg, ChannelPromise promise) throws Exception;
  53.  
  54. // 刷新刚执行的操作
  55. void flush(ChannelHandlerContext ctx) throws Exception;
  56. }

还有一个特殊的handler ChannelDuplexHandler,它同时继承ChannelInboundHandler和ChannelOutboundHandler,但不推荐用它,容易混淆,建议我们自己写的时候把入站和出站的handler分开

  1. public class ChannelDuplexHandler extends ChannelInboundHandlerAdapter implements ChannelOutboundHandler

ChannelHandlerContext传播行为

  前面我们讲入站是像后进行事件传递,出站是向前进行事件传递,那么事件入口是如何进来的、怎么出去的,怎么保证执行的顺序

如读事件,发生read事件时,会交给NioUnsafe方法,Unsafe随后会有介绍 它是netty中用来操作jdk中的nio,因为事件操作还是都交给jdk中的nio来,读取数据时会调用pipeline.fireChannelRead(readBuf.get(i)),这样就进入pipeline了 来开始事件传播

  1. private final class NioMessageUnsafe extends AbstractNioUnsafe {
  2. @Override
  3. public void read() {
  4. ~~~~~~~~~~~~~
  5. int size = readBuf.size();
  6. for (int i = 0; i < size; i ++) {
  7. readPending = false;
  8. // >>>>>>>>>进入入站读操作
  9. pipeline.fireChannelRead(readBuf.get(i));
  10. }
  11. readBuf.clear();
  12. allocHandle.readComplete();
  13. // 完成操作
  14. pipeline.fireChannelReadComplete();
  15. ~~~~~~~~
  16. }
  17. }

pipeline开启事件传播

  1. // 调用pipeline该方法开始事件传播
  2. public final ChannelPipeline fireChannelRead(Object msg) {
  3. AbstractChannelHandlerContext.invokeChannelRead(head, msg);
  4. return this;
  5. }
  6.  
  7. // >>>AbstractChannelHandlerContext.invokeChannelRead(head, msg) 由pipeline调用 然后进入context事件传递
  8. static void invokeChannelRead(final AbstractChannelHandlerContext next, Object msg) {
  9. final Object m = next.pipeline.touch(ObjectUtil.checkNotNull(msg, "msg"), next);
  10. EventExecutor executor = next.executor();
  11. // 判断是否在当前线程,如果在当前线程直接调用,否则当成任务交给executor执行
  12. if (executor.inEventLoop()) {
  13. next.invokeChannelRead(m);
  14. } else {
  15. executor.execute(new Runnable() {
  16. @Override
  17. public void run() {
  18. next.invokeChannelRead(m);
  19. }
  20. });
  21. }
  22. }
  23.  
  24. private void invokeChannelRead(Object msg) {
  25. // 判断handler是否已经被调用
  26. if (invokeHandler()) {
  27. try {
  28. /**
  29. * 调用handler的读操作,用户通过实现该方法完成自定义读事件逻辑,如果读取完后需要向后传递,需要在channelRead自定义方法中继续调用context.fireChannelRead(msg)
  30. *
  31. **/
  32. ((ChannelInboundHandler) handler()).channelRead(this, msg);
  33. } catch (Throwable t) {
  34. notifyHandlerException(t);
  35. }
  36. } else {
  37. // 已经被调用 调过当前handler向后传递
  38. fireChannelRead(msg);
  39. }
  40. }
  41.  
  42. public ChannelHandlerContext fireChannelRead(final Object msg) {
  43. // 找出下一个入站handler继续向后传递
  44. invokeChannelRead(findContextInbound(), msg);
  45. return this;
  46. }
  47.  
  48. private AbstractChannelHandlerContext findContextInbound() {
  49. AbstractChannelHandlerContext ctx = this;
  50. // 不停地获取下一个handler直到是inboundhandler返回
  51. do {
  52. ctx = ctx.next;
  53. } while (!ctx.inbound);
  54. return ctx;
  55. }

这就是读数据入站的大致传播流程,从head入站直至tail或中途停止事件传播, 出站流程类似 我就不贴了

在讲解服务端和客户端启动流程前我们还需要再熟悉几个重要类

ChannelFuture、Promise、Unsafe

我们先来channelfuture和promise的继承结构

  • ChannelFuture:nio既然是异步执行的,那么必定有异步执行结果,跟线程池一样,netty也对应有一个future
  • Promise:Promise也继承于future,听起来是不是和channelfuture功能重复了是不是,它两主要区别是,channelfuture可以得到对应的channel,promise可以主动设置异步执行状态,实际使用的ChannelPromise的实现类DefaultChannelPromise,channelPromise又同时继承channelfuture和实现promise,我个人不太理解为啥有channelfuture还多设计一个promise,直接在channelfuture加一个设置异步状态的接口不就好了,我猜想可能是promise可以当做一个脱离channel普通的异步实现,优秀的框架内部“果然都是可高度自定义重写的”
  • Unsafe:unsafe读过jdk源码的人应该很熟悉了,它是一个可直接进行原子化操作的工具,netty中的unsafe是用来操作jdk中的nio操作,我们前面说过netty就是一个在jdk原生nio上进行封装优化,所以内部网络通信肯定还是依靠jdk的nio实现的
  1. public interface Future<V> extends java.util.concurrent.Future<V> {
  2.  
  3. // 是否成功
  4. boolean isSuccess();
  5.  
  6. // 是否取消
  7. boolean isCancellable();
  8.  
  9. // 执行时候发生的异常
  10. Throwable cause();
  11.  
  12. // 添加监听器
  13. Future<V> addListener(GenericFutureListener<? extends Future<? super V>> listener);
  14.  
  15. // 批量添加监听器
  16. Future<V> addListeners(GenericFutureListener<? extends Future<? super V>>... listeners);
  17.  
  18. // 移除监听器
  19. Future<V> removeListener(GenericFutureListener<? extends Future<? super V>> listener);
  20.  
  21. // 移除多个监听器
  22. Future<V> removeListeners(GenericFutureListener<? extends Future<? super V>>... listeners);
  23.  
  24. // 同步阻塞,内部先执行await() 如果执行任务有发生异常会重新抛出
  25. Future<V> sync() throws InterruptedException;
  26.  
  27. // 与sync区别是,如果等待过程中发生中断,会将当前线程也一起中断,不响应中断
  28. Future<V> syncUninterruptibly();
  29.  
  30. // 同步阻塞,它与sync区别是,任务执行失败不会抛出异常
  31. Future<V> await() throws InterruptedException;
  32.  
  33. // 同理
  34. Future<V> awaitUninterruptibly();
  35.  
  36. boolean await(long timeout, TimeUnit unit) throws InterruptedException;
  37.  
  38. boolean await(long timeoutMillis) throws InterruptedException;
  39.  
  40. boolean awaitUninterruptibly(long timeout, TimeUnit unit);
  41.  
  42. boolean awaitUninterruptibly(long timeoutMillis);
  43.  
  44. // 获得执行结果,但不阻塞
  45. V getNow();
  46.  
  47. // 取消任务,并调用通知唤醒阻塞,然后调用监听器,mayInterruptIfRunning值好像没啥作用
  48. boolean cancel(boolean mayInterruptIfRunning);
  49. }
  50.  
  51. public interface Promise<V> extends Future<V> {
  52.  
  53. // 设置任务结果为成功 如果任务已经完成则抛出异常
  54. Promise<V> setSuccess(V result);
  55.  
  56. // 设置任务结果为成功,返回设置结果 不抛出异常
  57. boolean trySuccess(V result);
  58.  
  59. // 设置任务结果为失败 如果任务已经完成则抛出异常
  60. Promise<V> setFailure(Throwable cause);
  61.  
  62. // 设置任务结果为失败,返回设置结果 不抛出异常
  63. boolean tryFailure(Throwable cause);
  64. }
  65.  
  66. public interface ChannelFuture extends Future<Void> {
  67.  
  68. // 返回当前channel
  69. Channel channel();
  70. }

我们这节先大致介绍channel、handler、ChannelHandlerContext、pipeline 作用和方法,具体如何创建并使用,它们之前是怎么相互配合工作,我们下节通过对服务端和客户端启动过程进行分析过就会清晰许多

netty核心组件之channel、handler、ChannelHandlerContext、pipeline的更多相关文章

  1. Netty 核心组件 Pipeline 源码分析(二)一个请求的 pipeline 之旅

    目录大纲: 前言 针对 Netty 例子源码做了哪些修改? 看 pipeline 是如何将数据送到自定义 handler 的 看 pipeline 是如何将数据从自定义 handler 送出的 总结 ...

  2. 【转】Netty那点事(三)Channel中的Pipeline

    [原文]https://github.com/code4craft/netty-learning/blob/master/posts/ch3-pipeline.md Channel是理解和使用Nett ...

  3. Netty 核心组件 Pipeline 源码分析(一)之剖析 pipeline 三巨头

    目录大纲: 前言 ChannelPipeline | ChannelHandler | ChannelHandlerContext 三巨头介绍 三巨头编织过程(创建过程) ChannelPipelin ...

  4. netty系列之:Event、Handler和Pipeline

    目录 简介 ChannelPipeline ChannelHandler ChannelHandlerContext ChannelHandler中的状态变量 异步Handler 总结 简介 上一节我 ...

  5. 5. 彤哥说netty系列之Java NIO核心组件之Channel

    你好,我是彤哥,本篇是netty系列的第五篇. 简介 上一章我们一起学习了如何使用Java原生NIO实现群聊系统,这章我们一起来看看Java NIO的核心组件之一--Channel. 思维转变 首先, ...

  6. Netty核心组件介绍及手写简易版Tomcat

    Netty是什么: 异步事件驱动框架,用于快速开发高i性能服务端和客户端 封装了JDK底层BIO和NIO模型,提供高度可用的API 自带编码解码器解决拆包粘包问题,用户只用关心业务逻辑 精心设计的Re ...

  7. netty中的Channel、ChannelPipeline

    一.Channel与ChannelPipeline关系 每一个新创建的 Channel 都将会被分配一个新的 ChannelPipeline.这项关联是永久性 的:Channel 既不能附加另外一个 ...

  8. Netty服务端Channel的创建与初始化

    Netty创建服务端Channel时,从服务端 ServerBootstrap 类的 bind 方法进入,下图是创建服务端Channel的函数调用链.在后续代码中通过反射的方式创建服务端Channel ...

  9. 一文聊透 Netty IO 事件的编排利器 pipeline | 详解所有 IO 事件的触发时机以及传播路径

    欢迎关注公众号:bin的技术小屋,本文图片加载不出来的话可查看公众号原文 本系列Netty源码解析文章基于 4.1.56.Final版本 1. 前文回顾 在前边的系列文章中,笔者为大家详细剖析了 Re ...

随机推荐

  1. 团队作业part5--测试与发布(Alpha版本)

    测试报告 1.测试与解决bug 1)测试人员测试出的bug 游戏失败后方块还能下落 分数设计不太合理 存在行数不可消除的情况 2)开发人员解决bug 2.场景测试 适用群体:打发时间的学生.工作压力大 ...

  2. java中==和equals的不同使用方法

    System.out.println("input a charact a      "); Scanner input2 = new Scanner(System.in); St ...

  3. POJ3565

    题目大意: 给定\(n\)个蚂蚁和\(n\)颗苹果树的坐标,要求每个蚂蚁爬到一颗苹果树旁,使得每个蚂蚁路线不相交且路线总长度最小,求每个蚂蚁爬到哪个苹果树旁? 首先假设有两只蚂蚁路径相交,那么这两个蚂 ...

  4. 【题解】「UVA681」Convex Hull Finding

    更改了一下程序的错误. Translation 找出凸包,然后逆时针输出每个点,测试数据中没有相邻的边是共线的.多测. Solution 首先推销一下作者的笔记 由此进入>>> ( ...

  5. 转:什么是Shingling算法

    shingling算法用于计算两个文档的相似度,例如,用于网页去重.维基百科对w-shingling的定义如下: In natural language processing a w-shinglin ...

  6. MarkDown的练习_Java开发学习路径

    MarkDown的练习 语言学习 C/C++语言 Java语言 基础四大件 数据结构与算法 操作系统 计算机网络 设计模式 数据库/SQL 私人令牌:42bb654f53941d5692e98b35f ...

  7. fabric智能合约模板

    以创建用户为例,我觉得基本都是这个框架,用特殊功能直接再往上加,欢迎留言交流 func createUser(stub shim.ChaincodeStubInterface, args []stri ...

  8. java.lang.UnsupportedOperationException: Unable to create instance of org.fisco.bcos.web3j.abi.datatypes.generated.Int256

    Contract Address : 0x967f92adc229b77dda64b42af21ea1ff1b0702eb Unable to create instance of org.fisco ...

  9. 一、java多线程编程核心技术之(笔记)——多线程的实现

    概念:可以理解为在进程中独立运行的子任务.比如,QQ.exe在运行时就有很多子任务在运行,好友视屏线程,下载文件线程,传输数据线程等等. 优点:1.提升CPU资源利用率,提升系统性能. 一.多线程的实 ...

  10. Springboot mini - Solon详解(四)- Solon的事务传播机制

    Springboot min -Solon 详解系列文章: Springboot mini - Solon详解(一)- 快速入门 Springboot mini - Solon详解(二)- Solon ...