netty高性能网络框架原理&源码解析
2021-06-28 13:43:03   0  举报             
     
         
 netty源码解析,启动流程解析,netty架构,netty原理
    作者其他创作
 大纲/内容
 Channel自己的一些属性
  NIO里面设置非阻塞
    实例
  client
  BIO阻塞IO排队打饭
  HeadContext
  得到
  netty简易原理
  .childHandler给worker循环组使用的Handler
  channelFuture.channel().closeFuture().sync();关闭也是会在完全关闭才会返回
  pipeline.fireChannelRegistered();
  代理服务器:两个Channel共用同一个eventLoop
  新建Selector
  自己实现的处理器
  涉及数据的复制
  主要作用:channelRead实现将Channel转移到workerGroup
  io.netty.util.concurrent.GlobalEventExecutor#execute
  netty提供的接口netty没有这个,是直接调用具体处理器的
  bossGroup
  int selectedKeys = selector.select(timeoutMillis);
  ChannelInboundHandlerAdapter不是泛型类,需要强制类型转换
  JDK提供的Future只能手动检查执行结果,而且是阻塞的。(get或者isDone)netty则对Future进行了增强,使用了观察者模式,一个Future上注册了很多感兴趣的Listener,会调用每一个每一个Listener的Complete方法,会传递自己,自己可以获取到Channel,拿到Channel就可以任意操作promise继承Future,只能写一次
  GenericEventExecutorChooser
  pipeline
  如果设置的时候使用了handler(),也就是给bossGroup设置了Handler,就加到管道里面。ChannelInitializer的initChannel在Channel注册到eventGroupLoop上面的时候会被调用,而且当前ChannelInitializer(并不是Handler,但是可以重写他的initChannel这个方法添加多个Handler,这就是他的使命,一次性添加多个Handler。实际也是这样做的)会被删除,因为没用了最后往管道添加一个接收器ServerBootstrapAcceptor,可以看到事件循环组其实就是一个线程池,execute方法里面是添加ServerBootstrapAcceptor接收器,对应的就是reactor里面的Acceptor
  写数据请求解除阻塞SelectionKey.OP_READ
  SelectionKey.OP_READ | SelectionKey.OP_ACCEPT
  非阻塞
  等待结束,一定是执行结束了,Future有结果了
  异步执行
  ServerBootstrap
  reactor模式优点:
  自定义编码器
  Channel的pipeline
  doRegister();
  设置Handler
  策略模式
  startThread();
  message
  不管是serverSocketChannel还是SocketChannel,一开始都是简单的初始化器。注册结束之后会被替换为真正的Hhandler
  EventExecutorChooserFactory.EventExecutorChooser chooser;轮询还是随机next
  AIO(1.7)异步IO包厢模式
  1 连接请求
  for (;;) {
  taskQueue.offer(task);
  一般是一对一如果添加到pipeline多次,就是多对一
  每一个Handler的channelRegistered,注册Handler
  NIO(1.4)非阻塞点单被叫
  AbstractConstant
  消息不会被释放
  reactor模式缺点:
  一致接口
  通用模式的 NIO 实现多路复用器是怎么跨平台的
  Channel
  TailContext
  ch.configureBlocking(false);
  4 读取Channel数据交给pipeline
  attr()方法作用域(4.1改进):AttributeMap是共用的同一个Channel上的所有的HandlerContext是共享attr的源码的Context最后还是调用了Channel的attr,Channel的又是调用自己的父类Map的attr方法
  异步
  constant配置信息以及业务信息
  删除的逻辑就是会先调用自己写的全部注册进去之后,再调用原本的内部的一个方法将自己从pipeline里面删除handlerAdded--》initChannel--》remove(ctx);
  实现
  serverSocketChannelsocketChannel最后都是调用这个下面的流程是两种Channel共享的
   ReplayingDecoder 、ByteToMessageDecoder 主要区别:decode() and decodeLast() 可以被实现就像所有的字节已经接收到了,而不用检测到底够不够填充当前的类型。自定义协议常用的(头部标记长度字段)   public class IntegerHeaderFrameDecoder extends font color=\"#ff0000\
  DefaultChannelOption
  selectCnt ++;空轮询bug解决
  io.netty.bootstrap.ServerBootstrap#init(channel)
  菜好了谁来端的问题
  事件循环组(线程池)
  1
  io.netty.bootstrap.ServerBootstrap.ServerBootstrapAcceptor#channelRead在服务器初始化之后注册的Handler,也就是reactor中的Acceptor
  输入源
  从buffer里面获取需要处理的Channel
  重点:去注册了,和服务端共用一套逻辑
  select(wakenUp.getAndSet(false));
  自己定义的Handler
  规避多线程并发问题是当前线程就直接执行,不是当前线程就会作为一个任务加到当前事件循环,等待被当前事件循环的IO线程执行,所以就是把多线程串行 了,避免了并发的问题netty的任务队列保证先进来的队列先执行,综上,netty中Channel的实现是线程安全的。所以可以保存一个Channel引用,想要向远程断点发数据时候,可以用这个引用调用Channel的方法,很多线程再使用也不会有多线程问题而且一定会按照顺序发出去。
  value
  accept
  io.netty.util.concurrent.SingleThreadEventExecutor#doStartThread
  pipeline流转需要的AttributeKey以及value注意作用域,下面有写
  Channel注册到一个线程(事件循环组里面)
  连接建立
  自定义协议实现粘包拆包解决
  消息短多个拼接在一起,消息长拆分为多个,自己实现比较麻烦,netty会提供编解码器的方式,(基于分隔符或者头部标记长度或者定长)。
  netty的NioSocketChannel
  read2个实现
  处理连接请求SelectionKey.OP_ACCEPT
  消息计数-1,引用数少一个,表示消息被消费。会自动释放消息资源使用了模板方法设计模式,推迟到子类执行channelRead0
  不用BIO:连接多的时候会阻塞,耗资源效率低
  切换的时候没有管socketChannel而是只切换了ServerSocketChannel是因为SocketChannel是ServerSocketChannel在内部使用SocketUtils.accept(javaChannel());创建出来的
  添加
  为什么不使用AtomicInteger?而是使用了updater的方式?buf用的很多,static的全局变量,一个更新器谁都可以使用。AtomicIntegerFieldUpdater,只有一份,每一个都用一个AtomicInteger就会有性能损耗
  客户端服务器启动类
  监听端口
  Executor executor = new ThreadPerTaskExecutor(newDefaultThreadFactory());每一个任务的线程执行器
  ChannelOptionTCP 相关的系统设置项
  super(parent);
  ChannelFutureListener会被事件循环的IO线程调用,如果是耗时的处理方法是一样的
  this(newSocket(DEFAULT_SELECTOR_PROVIDER)new...会创建返回一个serverSocketChannel
  select
  Selector
  处理数据
  一些属性的设置
  DEFAULT_SELECTOR_PROVIDER = SelectorProvider.provider();这是NIO获取Selector的方式,spi的范式创建里面是同步的,可能会大量连接的时候阻塞,所以直接把这个provider存起来
  2
  随机读写   ByteBuf buffer = ...;   for (int i = 0; i < buffer.capacity(); i ++) {       byte b = buffer.getByte(i);       System.out.println((char) b);   }
  next得到一个事件循环(一个线程):(EventLoop) EventExecutor
  callHandlerAdded0(newCtx);见名知意
  通用的NIO在Linux下也是epoll啊,为什么另外写了?暴露更多的参数,边缘触发和水平触发可以切换,netty实现更少的垃圾回收,性能更好
  客户端服务端初始化器
  事件循环(线程)死循环监听处理事件
  io.netty.channel.socket.nio public class NioServerSocketChannelextends AbstractNioMessageChannelimplements io.netty.channel.socket.ServerSocketChannel启动器里面指定的是什么channel,创建的时候就会使用反射的工厂去找到指定的class创建什么。而且创建的会创建pipeline。init 的时候会设置 Channel  pipeline
  启动结束
  Acceptor
  AttributeMap一个Channel多个Handler数据的共享也是在初始化ServerBootStrap的时候可以设置,key以及对应的 T  value
  Reactor 角色构成与优劣势
  io.netty.channel public interface Channel所有的IO操作都是异步的Channel  pipeline 是其一个属性,包含了Handler的链条。支持父子关系,SocketChannel是ServerSocketChannel产生的,就有一个parent Channel。他们可以共享一个Socket连接,使用Channel之后要释放句柄。
  pipeline.invokeHandlerAddedIfNeeded();调用已经加进来的Handler,就是最开始的ChannelInitializer
  socketChannel完成注册的时候会替换为真正的Handler
  io.netty.channel.nio.AbstractNioMessageChannel.NioMessageUnsafe#read
  为什么说 Netty 的 main reactor 大多并不能用到一个线程组,只能线程组里面的一个?只是bind的时候用一下
  io.netty.channel.SingleThreadEventLoop#register(io.netty.channel.Channel)
  read
  MultithreadEventLoopGroup
  要不要死等
  IO切换的原理:不同的IO之间的切换,就是直接用到NIO的地方换Oio.channel() 使用反射的方式构建,泛型加反射加工厂实现IO模式的切换工厂模式+泛型+反射实现
  场景:代码简单连接有限并发度低不会扩展
  TCP 粘包拆包常见解决思路:不同的编解码器实现
  ChannelConfig:Channel持有的自己的属性里面可以设置ChannelOption为key的值是什么
    register0(promise)
  等待超时和连接超时 * // BAD - NEVER DO THIS * {@link Bootstrap} b = ...; * {@link ChannelFuture} f = b.connect(...);font color=\"#ff0000\
  调用所有的Handler处理客户端的数据
  进入else可能Future还没有完成
  反射的方式创建Channel,class在前面已经设置到里面了,不带参数的,直接无参构造器的newInstance
  连接超时不应该使用等待超时来配置直接使用连接超时实现
  final ChannelFuture regFuture = initAndRegister();异步的初始化和注册
  新建ServerChannel
  2注册读事件
  5个主要角色:1. Handle 句柄或者是描述符,本质是一种资源,用于表示一个个事件的集合,是操作系统提供的,事件既可以来自外部(客户端连接请求以及数据请求)也可以来自内部(定时器),本质是一个文件描述符,是事件产生的发源地2. Synchronous Event Demultiplexer 同步事件分离器:本身是一个OS系统调用,用于等待事件的发生,调用方调用这个的时候会阻塞,直到同步分离器上有事件产生位置返回,对于Linux而言就是常用的IO多路复用(select Poll epoll),对于NIO就是Selector,选择器底层还是会调用OS的多路复用,阻塞的方法就是select()3. 事件处理器:本身是有多个回调方法构成的,这些回调方法构成了事件的反馈机制,NIO没有实现要自己实现,netty有事件对应的回调,我们可以改写。4. 事件处理器的实现:实现了事件处理器提供的各个回调方法,实现了特定于业务的逻辑,本质就是一个个处理器的实现5. Initiation Dispatcher初始分发器:就是reactor(事件循环,线程池),事件处理器的注册删除,控制事件的调度方式,事件发生之后会分离出每一个事件,然后调用事件处理器,最后调用相关的回调方法处理相关的操作
  protobuf (编解码) netty(传输)-----------  thrift(编解码 + 基于netty的rpc框架)   grpc(基于netty的rpc框架)
  0 构造器创建
  socketChannel绑到childGroup
  3 读写请求
  Channel注册到事件循环组
  将Channel和端口绑定,注册关闭事件
  数据容器:ByteBuf   io.netty.buffer;  与  jdk  的 ByteBuffer
  初始化Channel,添加了ChannelInitializer,后续Channel注册时候使用
  io.netty.channel.socket.nio.NioServerSocketChannel#doReadMessages(readBuf)
  io.netty.channel.nio.NioEventLoop#run
  注意socketChannel注册到worker循环组和serveSocketChannel注册到boss的逻辑是一样的,最后也会调用invokeHandlerAddedIfNeeded将ChannelInitializer替换为我们自己编写的一个个Handler
  eventLoop.execute其实就是Executor的execute方法
  interface ArchiveSearcher { String search(String target); } * class App { *   ExecutorService executor = ... *   ArchiveSearcher searcher = ... *   void showSearch(final String target) *       throws InterruptedException { *     Future<String> future *       = executor.submit(new Callable<String>() { *         public String call() { *             return searcher.search(target); *         }}); *     displayOtherThings(); // do other things while searching *     try { *       displayText(future.get()); // use future *     } catch (ExecutionException ex) { cleanup(); return; } *   } * }
  init的时候会调用代码写的设置,没有设置的话就是空的
  获取这里初始化的只有首尾的pipeline
  基于头部长度标记的编解码器LengthFieldBasedFrameDecoder经常在自定义协议里面使用
  前面的都是在设置属性值,最后的bind才是真正的开始
  pipeline.fireChannelRead(byteBuf);
  socket Channel 的 pipeline
  连接请求解除阻塞SelectionKey.OP_ACCEPT
  io.netty.channel.AbstractChannel.AbstractUnsafe#register
  EventLoop
  Reactor
  看注释补齐
  反射创建的时候就会使用
  同步
  比较;netty 的是两个指针实现的,不需要flipjdk只是使用了一个指针,最后读取的时候还需要flip反转
  Channel注册了之后才会被调用
  为什么我们写的Handler不需要并发控制?因为netty实现保证了一个事件循环在整个生命周期只会和一个Thread绑定为什么不能在channelRead0写耗时操作?左边第五点,因为会有成百上千Channel属于同一个事件循环,又是单线程,这不是堵死了吗,性能直线下降。这个时候就要用业务线程池EventExecutor,两种实现:实现一:在channelHandler回调方法使用自己定义的JDK线程池:ExecutorService executorService = Executors.newCachedThreadPool();      executorService.submit(() -> {      });font color=\"#ff0000\
  Channel 注入Selector
  客户端
   反射创建
  MultithreadEventExecutorGroup
  按照协议长度,读取数据
  基于分隔符的编解码器DelimiterBasedFrameDecoder
  p.addLast(new ChannelInitializer<Channel>() {ChannelInitializer 本质是一个InBoundHandler
  ChannelInitializer有什么用?不是Handler,但是他的方法可以一次性添加多个Handler,添加完了没用了就可以删除了
  io.netty.channel public interface ChannelPipelineChannel创建的时候这个也会创建是一个ChannelHandler的列表(容器),就是event Handler,里面的一个个事件处理器就是Concurrent event Handler,具体的事件是容器元素处理的,不是容器处理的传统的拦截器入和出都要经过,netty是细化的特殊的拦截器,不是双向的,入站的不会管出去的入站处理器会处理IO线程生成的入站数据,从远端读取的,如果没有处理器就走到结尾丢掉出站事件到达最后就会被IO线程写到远端。   ChannelPipeline p = ...;   p.addLast(\"1\
  pipeline = newChannelPipeline();
  AttributeKey维护业务数据流程中后续可以获取
  给新连接建立NioSocketChannel之后加到buffer里面
  ServerBootstrapAcceptor
  主要流程:1. 应用向  Initiation Dispatcher 注册具体的事件处理器,应用会指定自己关注什么事件,事件和Handle相关联。(事件是Handle的子集,事件是读写,Handle是socket的文件描述符)2.  Initiation Dispatcher 要求每个事件处理器传递内部的Handle,Handle向操作系统标识了事件处理器3. 所有事件处理器注册之后,启动事件循环,使用同步事件分离器等待这些事件的发生。4. 当与某个事件源对应的Handle变为读状态,同步事件分离器会通知 Initiation Dispatcher5.  Initiation Dispatcher 会触发事件处理器的回调方法(自己写的),响应处于ready状态的handle,事件发生时, Initiation Dispatcher 会将事件源激活的Handle作为key寻找分发恰当的事件处理器回调方法6.  Initiation Dispatcher 会回调事件处理器的handle_event(一个Handle是可以产生多个事件的,socket的读写连接),处理特定的类型
  socketChannel注册到事件循环组的Selector
  addLast0(newCtx);完成链表的改变,也就是链表中间插入元素
  AbstractChannelHandlerContext.handler().handlerAdded(ctx);这个方法是我们自己可以重写的,标记当前的Handler的包装Context已经加入到pipeline里面了
  三种IO
  Reactor 单线程
  ByteBuf
  应用:spark Hadoop、rocketMQ、检索ES、grpc/dubbo/spring5 (和MVC相对的webflux使用,web响应式编程)、zk、async-http-client
  SimpleChannelInboundHandler泛型指定只处理特定类型的消息
  ChannelHandlerContext连接ChannelHandler以及pipeline的纽带通知pipeline里面下一个Handler,ChannelHandler handler();可以获取对应的Handler,Channel channel();  获取Channel对象的引用ChannelPipeline pipeline();获取pipeline,也可以修改pipeLine所属的对象
  bind(9000).sync();非阻塞方法调用
  runAllTasks();
  什么是reactor?注册感兴趣的事件 -> 扫描是否有感兴趣的事件发生 -> 事件发生后做出相应的处理。
  初始化NioServerSocketChannel
  netty 4.0 之前是JBoss的4.0之后是Netty自己独立出来的5.0 是复杂,增加了ForkJoinPool没有显著性能优势
  Channel感兴趣的事件accept
  新建事件循环
  连接建立的socketChannel注册到workGroup上的一个线程的Selector上
  AbstractBootstrap
  ChannelInitializer
      private volatile int refCnt = 1;-1操作;为0 的时候就不能使用了,会调用   deallocate(),堆buf的话数组置为null就好,垃圾回收机制会负责接下来的回收,而直接内存的buf的话io.netty.buffer.AbstractReferenceCountedByteBuf#release0    private boolean release0(int decrement) {font color=\"#ff0000\
  netty处理器分为2类:入站处理器(顶层是ChannelInBoundHandler),出站处理器(顶层是ChannelOutBoundHandler)。有回调方法,时间产生的时候就会调度编解码器本质就是处理器,最后在网络里面流动时候都是字节流的形式,编解码统一叫做 codec出站操作--编码--从程序流入网络--ChannelOutBoundHandler入站操作--解码--从网络流入程序--ChannelInBoundHandlernetty里面通常是XXXEncoder命名,解码器通常是XXXDecoder
  和Channel互相引用
  processSelectedKeys()
  addLast方法的详解:往pipeline的链表最后加一个HandlerContext。源码启动流程中每一个Channel一开始都会使用此方法添加ChannelInitializer,我们自己写的addLast也是这个方法
  抽象类实现类分类:堆上的直接内存的池化的和非池化的所以组合一共4种:非池化的堆上和直接内存的ByteBuf,释放的时候堆上直接使用null等待GC,直接内存则是使用Cleaner。池化的:引用计数的领域和jdkGC访问可达分析算法不一样。引用计数值release()到达0的时候就会放回池子里面,访问引用计数值为0的对象就会触发异常。增加引用计数可以使用retain()方法。引用计数:https://netty.io/wiki/reference-counted-objects.html衍生buffer和原本的buffer是使用同一个的。引用计数不会变化,所以产生衍生buffer的时候要retain,加1 ,Mark and reset作用是一致的
  childGroup.register(child)
  按照协议编码其实直接协议对象写入
  socketChannel
  SingleThreadEventExecutor.this.run();
  需要Channel的时候直接调用openServerSocketChannel而不是SocketChannel的open方法避免性能问题issues/2308ServerSocketChannel newSocket(SelectorProvider provider) {            return provider.openServerSocketChannel();
  ByteBuf的引用计数  : 原子更新   --- CAS + 自旋
  io.netty.channel.nio.AbstractNioChannel#doRegister
  EventLoopGroupworkerGroup
  易混5点:1. 一个事件循环组会有多个事件循环2. 一个事件循环在整个生命周期只会有唯一一个Thread与之绑定3. 所有事件循环所处理的IO事件都将在所关联的Thread上处理4. 一个Channel在他的生命周期只会注册到一个事件循环上5. 一个事件循环在运行过程中,会被分配给一个或多个Channel(也就是会有多个Channel注册上来)
  ChannelPromise可写入的ChannelFuture,父子关系
  触发执行task.run
  内部有自己定义的ByteBufReplayingDecoder传递一个特殊的ByteBuf实现,当缓冲区中没有足够的数据时抛出特定类型的错误。在上面的IntegerHeaderFrameDecoder中,当调用buf.readInt()时,您只是假设缓冲区中有4个或更多字节。如果缓冲区中真的有4个字节,它将像您期望的那样返回整数头。否则,将引发Error,控件将返回到ReplayingDecoder。如果ReplayingDecoder捕获了Error,那么它将把缓冲区的readerIndex倒带回到“初始”位置(即缓冲区的开始),并在缓冲区接收到更多数据时再次调用decode(..)方法。数据不够,解码器可能会重复解码相同的部分。而不像上面的不够就返回,为了解码一个单独的消息会解码好多次。泛型是状态类型,不使用直接Void,使用是用在检查点的时候,用来提高性能,区分不同类型的数据段,自己读取到哪里了
  Netty 对 Reactor 模式支持
  接口 netty Future为什么不直接使用jdk??对listener的操作,观察者模式。特定操作结束就会调用,不需要手动调用,jdk的需要手动调用监听者GenericFutureListener实现的接口的方法operationComplete会被自动调用
  删除已经做好的AIO?window实现成熟,但是不是服务器。LinuxAIO不成熟,对于NIO的性能提升不明显
  0 注册accept事件
  ChannelInitializer入站处理器
  注册Channel到SelectorNIO调用
  pipeline.fireChannelRead(readBuf.get(i));
  ConstantPool创建并且管理一些常量ConcurrentMap
  设置EventLoopGroupworkerGroup(定义在ServerBootStrap)
  io.netty.channel.nio.AbstractNioByteChannel.NioByteUnsafe#read
  io.netty.channel.nio.NioEventLoop#processSelectedKeysOptimized
  java.util.concurrent.Future代表着异步计算的结果,提供了计算是否完成的 check,等待完成,取回等多种方法。isDone主动查询,阻塞;get,会一直阻塞到结果计算完成。cancel,一旦计算完成,就无法被取消了
  OioServerSocketChannel基于流的
  sync确保初始化注册都结束了,操作完毕 ,返回的channelFuture异步操作结束虽然是异步的,但是也要有同步操作
  使用等待超时实现连接超时
  本质:网络应用程序框架实现:异步、基于事件驱动用途:开发服务器和客户端特性:高性能可维护和快速开发为什么不直接使用NIO?做的更多:netty支持常用应用层协议,解决传输问题粘包和半包线性;支持流量控制;完善的断连和空闲异常处理做的更好:        JDKNIO空轮询epoll bug(选择器无限唤醒,Linux2.4),检测问题的发生,(也就是select的次数大于阈值),之后重新建立一个Selector        IP包使用时候抛出异常,IP_TOS 参数,检测到NIOChannelOption是IP_TOS 的时候,直接不设置(work around,搞不定就不支持);        JDK的NIO的API不友好,功能薄弱,例如ByteBuffer(flip不能扩容)和ByteBuf(可以自动扩容)之间。以及功能的增强例如ThreadLocal和FastThreadLocal(高性能)自己实现的难度:JDK的bug,自己实现的bug
  MessageToMessageEncoder是一个编解码器
  服务端
  没有数据的复制,推荐使用
  基于行的编解码器LineBasedFrameDecoder
  处理连接的建立
  返回值 接口ChannelFuture针对Channel的Future接口Channel IO操作的结果,netty里面所有的IO都是异步的,因此会立即返回,得到一个ChannelFuture的实例,可以通过其获取IO操作的状态。可以 addListener() ,对Channel监控,完成时候会通知 ChannelFutureListener 不会阻塞,而不是使用await方法,因为会阻塞直到结果返回会有死锁发生。
  NioEventLoopGroup
  NioEventLoop事件循环组其实就是一个死循环
  ChannelFuture regFuture = config().group().register(channel);config返回服务器启动类的config对象,group获取到事件循环组
  serversocketChannel注册到事件循环组的Selector
  MINA:(虽然是后来者)netty是对mina的重构,更加简单,Apache管理SUN的Grizzly:用的少,文档少,社区不活跃Tomcat、Jetty:实现没有独立出来是为了支持servletTomcat不用是因为创建的时候netty还没有
  channel.eventLoop().execute(new Runnable() {
  两个指针分别读写,不需要flip()
  ChannelOptionTCP 相关的设置项,只维护key的信息,值在其余地方可以用来配置ChannelConfig初始化ServerBootStrap的时候可以设置,key以及对应的 T  value
  MessageToMessageDecoder#decode
  而且isSuccess是isDone的细化,后者可能是失败null结束的
  服务端真正的业务逻辑Handler注册我们自己写的,在客户端建立连接之后得到SocketChannel之后才会注册
  传统编程模式问题:1. 一个socket一个线程2. 上下文切换开销大3. 没有数据传递,线程保持,浪费资源
  适配器模式+模板方法:入出站处理器
  Reactor 多线程模式
  读取数据   ByteBuf buffer = ...;   while (buffer.isReadable()) {       System.out.println(buffer.readByte());   }
  Bootstrap
  ChannelPipeline p = channel.pipeline();一个个的Handler在这里
  next().register(channel);
  AbstractChannelHandlerContext
  Nio....
  io.netty.channel.DefaultChannelPipeline#font color=\"#ff0000\
  ReplayingDecoder#decode
  hashmap的方式
  实现了任务和线程创建解耦
  非阻塞监听IO事件
  workerGroup
  acceptor作用是将Channel注册到workerGroup,他是位于bossGroup的pipeline里面的最后一个处理器acceptor是netty绑定端口的时候自动创建的,加到了pipeline,也就是一个处理器,这个处理器会将生成出来的socketChannel绑定到workerGroup(IO线程组,里面有一个线程池)
  next.invokeChannelRead(m);
  初始化pipeline
   FutureTask<String> future = new FutureTask<String>(new Callable<String>() {     public String call() {       return searcher.search(target);     } });executor.execute(future);
  自定义协议长度内容
   ChannelHandler包含了入站和出站两种,处理不同的数据
  SingleThreadEventLoop
  基于固定长度的编解码器FixedLengthFrameDecoder
  channel = channelFactory.newChannel();实际是ReflectiveChannelFactory反射新建Channel
  客户端服务端处理器
  ServerBootStrap
  观察者放到一个集合,来的时候遍历集合
  主从 Reactor 多线程模式
  启动流程解析
  netty对三种IO的支持
  写数据   ByteBuf buffer = ...;   // 整数大于4字节,可以放下   while (buffer.maxWritableBytes() >= 4) {       buffer.writeInt(random.nextInt());   }
  自定义解码器
  EventLoopGroupbossGroup
  处理连接请求SelectionKey.OP_READ
  NioServerSocketChannel
  https://www.jianshu.com/p/36b58ef2ff23
  .channel反射的方式创建,但是此时不创建,只是NioServerSocketChannel.class给了
  感兴趣的事件
  设置EventLoopGroupbossGroup用于接收客户端连接(定义在AbstractBootStrap)
  SocketChannel ch = SocketUtils.accept(javaChannel());使用java原生的方式获取Channel
  Netty 如何支持主从 Reactor 模式的?serverSocketChannel注册到bossGroup(parentGroup)ChannelFuture regFuture = config().group().register(channel);font color=\"#ff0000\
  addTask(task);
  开始真正启动
  for循环处理selectedKeys所有的key
  ByteToMessageDecoder#decode
  doBind(localAddress);
  抽象类ChannelHandlerAdapter
  Channel.pipeline().addLast(childHandler);
  阻塞
  register0(promise) 任务放到队列异步执行
  互相引用
  serverbootstrap
  ChannelHandler
  中转
  Netty 给 socketChannel 分配 NIO event loop 的规则是什么?线程池里面选一个线程(事件循环)来注册socketChannel,选择的index计算有两种方式,根据初始的线程的个数是否是2的幂决定使用哪一个chooser,决定了index的计算方式不一样childGroup.register(child)--》next().register(channel); --》chooser.next();
  readBuf里面填充需要处理的socketChannel
  netty主要的基本组件   Channel  ChannelHandler  ChannelHandlerContext  ChannelPipeline
  处理器
  管道实际存放的是  new DefaultChannelHandlerContext
   
 
 
 
 
  0 条评论
 下一页
  
   
   
   
   
  
  
  
  
  
  
  
 