签哥肯定学过redis,不信你看嘛

252 阅读6分钟

刚在微博上面刷到,这两天签儿哥的事情闹得沸沸扬扬,渣渣掐指细品,这不就是Redis的io多路复用嘛。

什么是io多路复用:

签哥约炮这个流程就是典型io多路复用模型,简单的来说就是,用一个线程来处理多个IO流,避免上下文的切换,来减少不必要的开销,就相当于签哥+经济人的一对多模型。

套接字:socket,就是就是针对不同主机上的应用进程之间进行双向通信的端点抽象,一个套接字相当于通信的一端。

redis中的多路复用机制的实现:

首先要清楚的是,为什么要有IO复用机制。

在服务端和客户端进行网络IO的时候,在检测到客户端有连接请求accept(),但一直并没有成功的建立连接。在这个过程当中,服务端只能焦急的等待,让自己阻塞。

就好比签儿哥知道姑娘要来,就去门口的等待,但是姑娘就是迟迟不来,火急火燎,心烦气躁,只能干等,是的这时候的签儿哥就被阻塞了,不能做其他事情。

类似的还有从客户端读取数据recv(),等等一些IO场景。

好吧废话说多了:

看图

  

首先来看IO多路复用程序部分,它的所有功能都是通过封装select、epoll、evport、kqueue这些I/O多路复用函数实现的。每个操作系统都会在底层提供这些函数,redis也针对不同的操作系统进行了相应的封装,程序在编译的时候会自动选择系统中性能最高的I/O多路复用函数库来作为底层实现。(三遍面试考)

然后有了封装好的I/O多路复用程序,redis就可以监听多个套接字的读写事件(默念:一个套接字相当于通信的一端)。这样一旦有产生事件的套接字,就会其发送给文件事件分派器,因为redis是单线程嘛,就像是签哥只能一对一,所以会先将套接字放入一个队列,以每次一个的方式向文件事件分派器发送套接字,而文件事件分派器根据不同的事件类型来调用不同的文件事件处理器。就这样处理一个再发下一个的顺序。来完成自己的单线程操作,全程不用阻塞自己。

以上就是整个redis处理请求的过程,虽然redis是单线程的,但通过IO多路复用监听多个套接字。即实现的高性能的网络I/O,又可以使程序的设计简单。这种思路也可以用在我们自己的程序设计里面,比如。。(我也没用过),但签哥用过。

面试常问的三种实现(select/poll/epoll)

简单的来讲,IO多路复用程序就是来监视多个文件描述符,一旦某个文件描述符读写就绪,就返回通知相应的函数去处理,他们的优势在与能够同时处理多个连接而不阻塞Redis的文件事件处理器线程,也就是主线程。

网上会有很多对它们的详细解析,这里就不细讲了。

select
  • select内部维护了一个存放大量fd的, fd_size 在32位机器上大小是1024,在64位的机器上是2048
  • 每当有“可读”事件产生的时候,都要将整个集合从用户态拷贝到内核态,对整个集合进行扫描,然后由内核返回可读或写的fd。数据量越大,这种无差别轮训的时间就越长,时间复杂度O(n)。
  • 单个进程可以检测的fd的数量被限制
  • 对fd集合的扫描是采用轮训的方式,这样无论是一个还是多个可读写的fd产生,消耗的资源都是一样的
poll
  • 同select相同,每次调用都会阻塞,且需要将整个集合从从用户态拷贝到内核态,直到有可读写的fd才会返回。
  • 它没有最大连接数的限制,原因是它是基于链表来存储的,但是同样有一个缺点:
  • 大量的fd的数组被整体复制于用户态和内核地址空间之间,而不管这样的复制是不是有意义。                   
  • poll还有一个特点是“水平触发”,如果报告了fd后,没有被处理,那么下次poll时会再次报告该fd。

\

epoll

这个就厉害了,epoll可以叫做是event_poll,和无差别轮询不同,epoll会把产生了IO事件的流通知到我们,所以说是一个事件驱动。

  • 没有最大并发连接的限制,能打开的FD的上限远大于1024(1G的内存上能监听约10万个端口)。
  • 效率提升,不是轮询的方式,不会随着FD数目的增加效率下降。只有活跃可用的FD才会调用callback函数。即Epoll最大的优点就在于它只管你“活跃”的连接,而跟连接总数无关,因此在实际的网络环境中,Epoll的效率就会远远高于select和poll;
  • 内存拷贝,利用mmap()文件映射内存加速与内核空间的消息传递,他将一小块内核空间和用户空间的虚拟内存映射到同一块物理地址,使得这块物理内存对内核和对用户均可见,减少了用户态和内核态之间的数据交换。
epoll LT和ET的区别

LT代表水平出发,ET代表边缘触发。

  • 水平触发  当文件描述符就绪时,如果用户程序没有一次性将数据读写完成,下次还会发出可读可写的通知。
  • 边缘触发  仅当描述符编程已就绪的时候通知一次,无论读写成功与否,后面都不会再有通知。
小结

1、select 就是轮询,一般数量限制为1024个。

2、poll解决了select的单个数量限制,但还是轮询。

3、解决了个数的限制,同时解决了轮询。

以上便是对三种不同IO复用机制的介绍,整个模块使得redis可以同时和多个客户端建立连接,同时又可以单线程处理每个请求。避免引入多线程提升系统复杂度,减少出错的可能。

  

签哥现在得偿所愿,同时也祈祷最后结果是大家想看到的。