(精华)Netty由浅入深_JAVA_流程图_架构图_系统图_数据库
2024-11-03 14:21:04 50 举报
从TCP->IO->NIO->Reactor->Netty,一步一步由浅入深来讲解Netty
作者其他创作
大纲/内容
EPollSelectorImpl:调用implRegister将channel添加到数组pollWrapper中(pollWrapper.add(fd))
复制数据到用户缓冲区
JAVA应用层
Reactor
红黑树
step3:runAllTasks
阻塞
accept
buffer
Boss Group
完成通知
linux内核空间
应用A
pollWrapper = new EPollArrayWrapper();
client
1、应用进程向内核发起read读取数据2、等待内核缓冲区数据(阻塞)3、内核缓冲区数据准备好后,复制数据到用户缓冲区(阻塞)4、复制完成,返回成功- 用户空间和内核空间都是在内存中的- read系统调用是将数据从内核空间复制到用户空间;write系统调用是将数据从用户空间复制到内核空间;底层都是由内核来完成的
TCP发送缓冲区(内核空间)
Selector
TCP/IP层
NioEventLoop
dispatch
Worker线程
Reactor主线程
implRegister()
select
业务处理
1、Reactor主线程MainReactor对象通过select监听客户端连接请求事件,收到事件后,通过Acceptor处理连接事件2、Acceptor处理完连接事件后,MainReactor将连接分发到SubReactor3、SubReactor将连接加入到队列进行监听,并创建Handler对各种事件处理4、Handler只负责响应read、send事件,不做具体业务处理。通过read读取数据后,会分发给后面的Worker线程池中的某个线程处理业务。5、Worker线程池中的线程处理完业务,会将结果返回给Handler。6、Handler收到响应后,通过send将结果返回给clientMainReactor只负责处理连接事件SubReactor负责处理read、send事件Worker负责处理具体业务Nginx、Netty都是基于这种线程模型
服务端ServerSocketChannel是在服务启动时注册的,只关注连接事件客户端SocketChannel是在事件(连接,读,写)发生后注册的,只关注读或者写事件
注册
read调用
Sub Reactor
监控
ChannelHandlerContext
read调用1
用户空间
IO模型-同步非阻塞NIO
SocketChannel1
read
接收数据
Selector.open()
send
复制完成
IO模型-IO多路复用
TaskQueue
应用层
step2:processSelectedKeys
处理请求
span style=\
ChannelHandler
具体任务交给handler来处理
注意:span style=\
建立连接
ChannelPipeLine
Acceptor
1、应用进程向内核发起read读取数据2、数据没有准备好,直接返回EWOULDBLOCK错误(非阻塞)3、应用进程不断发起read读取数据4、内核缓冲区数据准备好后,将数据复制到用户缓冲区(阻塞)5、复制完成,返回成功- 内核空间数据准备好前,线程都不阻塞,- 线程会轮询,确认内核数据是否准备好,准备好后,就会进行数据复制- BIO是面向流的,是单向通道;NIO是面向缓冲区的,是双向通道- 这个NIO模型不是JAVA中的NIO,这个循环会循环所有socket连接,性能太差
epoll_ctl方法
Netty线程模型
第1步
TCP接收缓冲区(内核空间)
Handler
1、发送数据
等待数据
TCP发送数据流程
客户端连接
epoll_create方法
调用返回
Worker线程池
...
......
JAVA NIO
注册绑定
client2
head
EPollSelectorImpl:调用poll开始注册绑定(第1步)及监控(第2步)注册绑定:将channel添加到红黑树,并且与对应事件绑定起来(连接,读,写事件)监控:监控rdlist列表有没有数据
等待内核缓冲区数据
select调用
Worker Group
rdlist
发送数据
epoll_create,创建了一个epoll实例(epoll包含了一个红黑树和一个rdlist双向链表)
SocketChannel2
1、Selector.open():在JAVA层面创建了一个存储所有channel的数组,底层(C语言)调用了epoll_create,创建了一个epoll实例(epoll包含了一个红黑树和一个rdlist双向链表span style=\
Reactor子线程N
注册绑定:将channel添加到红黑树,并且与对应事件绑定起来(连接,读,写事件)
就绪事件列表rdlist
单Reactor单线程
client3
主从Reactor多线程
复制数据
非阻塞
client1
可读连接
step1:select
2、发送数据
SocketChannel3
tail
ServerSocketChannel
socketChannel.register()
JAVA底层
epoll_wait采用阻塞的方式来监控rdlist有没有数据,有数据就将该数据拷贝到用户空间,程序继续往下运行
1、Reactor对象通过select监听客户端请求事件,再通过dispatch进行分发2、如果是连接请求,会分发到Acceptor处理,然后创建一个Handler来处理完成连接后的各种事件3、如果不是连接请求,会分发到对应的Handler来处理4、Handler只负责响应read、send事件,不做具体业务处理。通过read读取数据后,会分发给后面的Worker线程池中的某个线程处理业务。5、Worker线程池中的线程处理完业务,会将结果返回给Handler。6、Handler收到响应后,通过send将结果返回给client将业务放入专门的线程池中处理,可以充分利用CPU多核性能;读写操作还是由Reactor单线程来完成,高并发下容易出现瓶颈
单Reactor多线程
IO模型-异步IO
1、NIO SERVER启动时,会调用Selector.open()方法,创建一个Selector对象,并且调用register方法,将ServerSocketChannel注册到Selector上面(此时Selector上面只有一个ServerSocketChannel,ServerSocketChannel只负责连接事件)2、调用selector.select()方法,采用阻塞方式监听所有channel的事件(连接,读,写),一旦有事件产生,就将这些事件以集合形式返回,由程序来处理3、如3个客户端发起了连接,此时,ServerSocketChannel会有3个连接事件返回,程序会拿到这3个SocketChannel并注册到Selector上(此时Selector上面有1个ServerSocketChannel和3个SocketChannel,SocketChannel负责读写事件)4、client1和client2有数据过来,Selector会感知到,将这2个SocketChannel以集合形式返回,由程序来处理
应用B
3、接收数据
read调用2
TCP发送流程简单分为三步:1、应用A将数据发送到TCP发送缓冲区2、TCP发送缓冲区将数据发送出去,经过网络传输,会到达应用B的TCP接收缓冲区3、应用B再从TCP接收缓冲区内读取数据,读到用户缓冲区注:1、IO模型中所说的阻塞是指在建立连接后,读取数据时出现的情况2、应用B向TCP接收缓冲区读取数据时,数据还没有准备好,这时线程会阻塞3、TCP接收缓冲区数据准备好后,在复制数据到用户缓冲区时,也会发生阻塞
channel与selectionkey绑定
PipeLinee具体结构
第2步
updateRegistrations();
客户端
1、Reactor对象通过select监听客户端请求事件,再通过dispatch进行分发2、如果是连接请求,会分发到Acceptor处理,然后创建一个Handler来处理连接完成后的后续业务处理3、如果不是连接请求,会分发到对应的Handler来处理4、Handler会完成read -> 业务处理 -> send完整的业务流程服务端用一个线程通过多路复用来完成连接,读,写,业务处理操作当读,写,业务处理耗时,容易出现性能问题
pollWrapper.poll(timeout)
epfd = epollCreate();
read调用N
Reactor子线程1
PipeLine
这里的socketChannel代表SocketChannel和ServerSocketChannel。
Channel
当集合里的channel有事件发生时,操作系统会将其复制到rdlist中
SelectionKey
注册NioSocketChannel到Selector
当红黑树的中channel有对应的事件发生时,会将该channel复制到rdlist。这一步是由操作系统来完成的
EPollArrayWrapper:EPollArrayWrapper()在创建channel数组时,构造方法里面还会调用epollCreate(),来触发C层面创建epoll实例。
8080端口
Main Reactor
监听
存放所有channel的集合
read/write
Netty抽象出两组线程池:BossGroup和WorkerGroupBossGroup:专门接收客户端的连接WorkerGroup:专门负责网络的读写BossGroup和WorkerGroup的类型都是NioEventLoopGroup(事件循环组),每个事件循环都是NioEventLoop注:1、一个Channel包含一个ChannelPipeLine;2、ChannelPipeLine维护了一个由ChannelHandlerContext组成的双向链表,每个ChannelHandlerContext又关联了一个ChannelHandler3、一个Handler要么属于入站(InBound)或者属于出站(OutBound)4、read事件(入站事件)会从链表head向后传递,直到最后一个入站的Handler5、write事件(出站事件)会从链表tail向前传递,直到最前一个出站的Handler6、两种类型的handler相互不干扰
selector.select()
EPollSelectorImpl:JAVA底层创建channel数组
Thread
服务端
epoll_wait方法
IO模型-同步阻塞
Selector多路复用器
C层面
非阻塞做其它事情
1、进程受阻于select调用,等待多个socket中的任意一个变为可读(阻塞)2、当socket变成可读时(说明内核缓冲区数据已经准备好),用户进程发起read读取数据,将数据复制到用户缓冲区(阻塞)3、复制完成,返回成功- JAVA中的NIO就是IO多路复用,Linux环境下底层使用的是epoll- 该模型适用于连接数多,连接时间短的场景- select阻塞,等待所有socket中的任意一个变成可读
0 条评论
下一页
为你推荐
查看更多