网站建设售后回访话术,秦皇岛建设银行网点分布,织梦怎么制作手机网站源码,wordpress添加数据库Redis中的线程和IO模型
什么是Reactor模式 #xff1f;
“反应”器名字中”反应“的由来#xff1a;
“反应”即“倒置”#xff0c;“控制逆转”,具体事件处理程序不调用反应器#xff0c;而向反应器注册一个事件处理器#xff0c;表示自己对某些事件感兴趣#xff0…Redis中的线程和IO模型
什么是Reactor模式
“反应”器名字中”反应“的由来
“反应”即“倒置”“控制逆转”,具体事件处理程序不调用反应器而向反应器注册一个事件处理器表示自己对某些事件感兴趣有时间来了具体事件处理程序通过事件处理器对某个指定的事件发生做出反应这种控制逆转又称为“好莱坞法则”不要调用我让我来调用你
例如路人甲去做男士SPA前台的接待小姐接待了路人甲路人甲现在只对10000技师感兴趣但是路人甲去的比较早就告诉接待小姐等10000技师上班了或者是空闲了通知我。等路人甲接到接待小姐通知做出了反应把10000技师占住了。
然后路人甲想起上一次的那个10000号房间不错设备舒适灯光暧昧又告诉前台的接待小姐我对10000号房间很感兴趣房间空出来了就告诉我我现在先和10000这个小姐聊下人生10000号房间空出来了路人甲再次接到接待小姐通知路人甲再次做出了反应。
路人甲就是具体事件处理程序前台的接待小姐就是所谓的反应器“10000技师上班了”和“10000号房间空闲了”就是事件路人甲只对这两个事件感兴趣其他比如10001号技师或者10002号房间空闲了也是事件但是路人甲不感兴趣。
前台的接待小姐不仅仅服务路人甲1人他还可以同时服务路人乙、丙……..每个人所感兴趣的事件是不一样的前台的接待小姐会根据每个人感兴趣的事件通知对应的每个人。
单线程Reactor模式流程
服务器端的Reactor是一个线程对象该线程会启动事件循环并使用Acceptor事件处理器关注ACCEPT事件这样Reactor会监听客户端向服务器端发起的连接请求事件(ACCEPT事件)。
客户端向服务器端发起一个连接请求Reactor监听到了该ACCEPT事件的发生并将该ACCEPT事件派发给相应的Acceptor处理器来进行处理。建立连接后关注的READ事件这样一来Reactor就会监听该连接的READ事件了。
当Reactor监听到有读READ事件发生时将相关的事件派发给对应的处理器进行处理。比如读处理器会通过读取数据此时read()操作可以直接读取到数据而不会堵塞与等待可读的数据到来。
在目前的单线程Reactor模式中不仅I/O操作在该Reactor线程上连非I/O的业务操作也在该线程上进行处理了这可能会大大延迟I/O请求的响应。所以我们应该将非I/O的业务逻辑操作从Reactor线程上卸载以此来加速Reactor线程对I/O请求的响应。 单线程Reactor工作者线程池
与单线程Reactor模式不同的是添加了一个工作者线程池并将非I/O操作从Reactor线程中移出转交给工作者线程池来执行。这样能够提高Reactor线程的I/O响应不至于因为一些耗时的业务逻辑而延迟对后面I/O请求的处理。
但是对于一些小容量应用场景可以使用单线程模型对于高负载、大并发或大数据量的应用场景却不合适主要原因如下
① 一个NIO线程同时处理成百上千的链路性能上无法支撑即便NIO线程的CPU负荷达到100%也无法满足海量消息的读取和发送
② 当NIO线程负载过重之后处理速度将变慢这会导致大量客户端连接超时超时之后往往会进行重发这更加重了NIO线程的负载最终会导致大量消息积压和处理超时成为系统的性能瓶颈 多Reactor线程模式
Reactor线程池中的每一Reactor线程都会有自己的Selector、线程和分发的事件循环逻辑。
mainReactor可以只有一个但subReactor一般会有多个。mainReactor线程主要负责接收客户端的连接请求然后将接收到的SocketChannel传递给subReactor由subReactor来完成和客户端的通信。
多Reactor线程模式将“接受客户端的连接请求”和“与该客户端的通信”分在了两个Reactor线程来完成。mainReactor完成接收客户端连接请求的操作它不负责与客户端的通信而是将建立好的连接转交给subReactor线程来完成与客户端的通信这样一来就不会因为read()数据量太大而导致后面的客户端连接请求得不到即时处理的情况。并且多Reactor线程模式在海量的客户端并发请求的情况下还可以通过实现subReactor线程池来将海量的连接分发给多个subReactor线程在多核的操作系统中这能大大提升应用的负载和吞吐量。 Redis中的线程和IO概述
Redis 基于 Reactor 模式开发了自己的网络事件处理器 - 文件事件处理器file event handler后文简称为 FEH而该处理器又是单线程的所以redis设计为单线程模型。
采用I/O多路复用同时监听多个socket根据socket当前执行的事件来为 socket 选择对应的事件处理器。
当被监听的socket准备好执行accept、read、write、close等操作时和操作对应的文件事件就会产生这时FEH就会调用socket之前关联好的事件处理器来处理对应事件。
所以虽然FEH是单线程运行但通过I/O多路复用监听多个socket不仅实现高性能的网络通信模型又能和 Redis 服务器中其它同样单线程运行的模块交互保证了Redis内部单线程模型的简洁设计。
下面来看文件事件处理器的几个组成部分。 socket
文件事件就是对socket操作的抽象 每当一个 socket 准备好执行连接accept、read、write、close等操作时 就会产生一个文件事件。一个服务器通常会连接多个socket 多个socket可能并发产生不同操作每个操作对应不同文件事件。
I/O多路复用程序
I/O 多路复用程序会负责监听多个socket。
尽管文件事件可能并发出现 但 I/O 多路复用程序会将所有产生事件的socket放入队列 通过该队列以有序、同步且每次一个socket的方式向文件事件分派器传送socket。
当上一个socket产生的事件被对应事件处理器执行完后 I/O 多路复用程序才会向文件事件分派器传送下个socket 如下 I/O多路复用程序的实现
Redis 的 I/O 多路复用程序的所有功能都是通过包装常见的 select、epoll、 evport 和 kqueue 这些 I/O 多路复用函数库实现的。
每个 I/O 多路复用函数库在 Redis 源码中都对应一个单独的文件
因为 Redis 为每个 I/O 多路复用函数库都实现了相同的 API 所以 I/O 多路复用程序的底层实现是可以互换的。Redis 在 I/O 多路复用程序的实现源码ae.c文件中宏定义了相应规则使得程序在编译时自动选择系统中性能最高的 I/O 多路复用函数库作为 Redis 的 I/O 多路复用程序的底层实现性能降序排列。 注
evport Solaris 10
epoll Linux
kqueue OS XFreeBSD
select 通常作为fallback安装在所有平台上
EvportEpoll和KQueue具有 O(1)描述符选择算法复杂度并且它们都使用内部内核空间内存结构.他们还可以提供很多(数十万个)文件描述符.
除其他外select最多只能提供 1024个描述符并且对描述符进行完全扫描(因此每次迭代所有描述符以选择一个可使用的描述符)因此复杂性是 O(n).
文件事件分派器
文件事件分派器接收 I/O 多路复用程序传来的socket 并根据socket产生的事件类型 调用相应的事件处理器。
文件事件处理器
服务器会为执行不同任务的套接字关联不同的事件处理器 这些处理器是一个个函数 它们定义了某个事件发生时 服务器应该执行的动作。
Redis 为各种文件事件需求编写了多个处理器若客户端连接Redis对连接服务器的各个客户端进行应答就需要将socket映射到连接应答处理器写数据到Redis接收客户端传来的命令请求就需要映射到命令请求处理器从Redis读数据向客户端返回命令的执行结果就需要映射到命令回复处理器当主服务器和从服务器进行复制操作时 主从服务器都需要映射到特别为复制功能编写的复制处理器。
文件事件的类型
I/O 多路复用程序可以监听多个socket的 ae.h/AE_READABLE 事件和 ae.h/AE_WRITABLE 事件 这两类事件和套接字操作之间的对应关系如下
当socket可读比如客户端对Redis执行write/close操作或有新的可应答的socket出现时即客户端对Redis执行connect操作socket就会产生一个AE_READABLE事件。
当socket可写时比如客户端对Redis执行read操作socket会产生一个AE_WRITABLE事件。
I/O多路复用程序可以同时监听AE_REABLE和AE_WRITABLE两种事件要是一个socket同时产生这两种事件那么文件事件分派器优先处理AE_REABLE事件。即一个socket又可读又可写时 Redis服务器先读后写socket。
总结
最后让我们梳理一下客户端和Redis服务器通信的整个过程
Redis启动初始化时将连接应答处理器跟AE_READABLE事件关联。
若一个客户端发起连接会产生一个AE_READABLE事件然后由连接应答处理器负责和客户端建立连接创建客户端对应的socket同时将这个socket的AE_READABLE事件和命令请求处理器关联使得客户端可以向主服务器发送命令请求。
当客户端向Redis发请求时不管读还是写请求客户端socket都会产生一个AE_READABLE事件触发命令请求处理器。处理器读取客户端的命令内容 然后传给相关程序执行。
当Redis服务器准备好给客户端的响应数据后会将socket的AE_WRITABLE事件和命令回复处理器关联当客户端准备好读取响应数据时会在socket产生一个AE_WRITABLE事件由对应命令回复处理器处理即将准备好的响应数据写入socket供客户端读取。
命令回复处理器全部写完到 socket 后就会删除该socket的AE_WRITABLE事件和命令回复处理器的映射。