netty高性能网络框架原理&源码解析
2021-06-28 13:43:03 0 举报netty源码解析,启动流程解析,netty架构,netty原理
netty原理
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
收藏
立即使用
收藏
立即使用
收藏
立即使用
收藏
立即使用
Collect
Get Started
Collect
Get Started
Collect
Get Started
Collect
Get Started
评论
0 条评论
下一页