Redis 中的 epoll 模型

1.多路复用

​ redis 采用网络IO多路复用技术来保证在多连接的时候, 系统的高吞吐量

存在的问题 Redis 是跑在单线程中的, 所有的操作都是按照顺序线性执行的, 但是由于读写操作等待用户输入或输出都是阻塞的, 所以 I/O 操作在一般情况下往往不能直接返回, 这会导致某一文件的 I/O 阻塞导致整个进程无法对其它客户提供服务

​ redis的io模型主要是基于epoll实现的, 不过它也提供了 select和kqueue的实现, 默认采用epoll

2. epoll机制

​ 有100万个客户端同时与一个服务器进程保持着TCP连接。而每一时刻, 通常只有几百上千个TCP连接是活跃的(事实上大部分场景都是这种情况)。如何实现这样的高并发?

​ 在select/poll时代, 服务器进程每次都把这100万个连接告诉操作系统(从用户态复制 句柄数据结构 到内核态), 让操作系统内核去查询这些套接字上是否有事件发生, 轮询完后, 再将句柄数据复制到用户态, 让服务器应用程序轮询处理已发生的网络事件, 这一过程资源消耗较大, 因此, select/poll一般只能处理几千的并发连接

存在的缺点:

​ 1.每次调用select/poll, 都需要把fd集合从用户态拷贝到内核态, 这个开销在fd很多时会很大

​ 2.同时每次调用select/poll都需要在内核遍历传递进来的所有fd, 这个开销在fd很多时也很大

​ 3.针对select支持的文件描述符数量太小了, 默认是1024

​ 4.select返回的是含有整个句柄的数组, 应用程序需要遍历整个数组才能发现哪些句柄发生了事件;

​ 5.select的触发方式是水平触发, 应用程序如果没有完成对一个已经就绪的文件描述符进行IO操作, 那么之后每次select调用还是会将这些文件描述符通知进程

​ 相比select模型, poll使用链表保存文件描述符, 因此没有了监视文件数量的限制, 但其他三个缺点依然存在。

epoll的优化

​ epoll是poll的一种优化,返回后不需要对所有的fd进行遍历,在 内核中 维持了fd的列表。select和poll是将这个 内核列表维持在用户态,然后传递到内核中

​ epoll不再是一个单独的系统调用,而是由epoll_create/epoll_ctl/epoll_wait三个系统调用组成

​ epoll通过在Linux内核中申请一个简易的文件系统(文件系统一般用什么数据结构实现?B+树), 将调用分成了3部分:

​ (1) 调用epoll_create()建立一个epoll对象(在epoll文件系统中为这个句柄对象分配资源)

​ (2) 调用epoll_ctl向epoll对象中添加这100万个连接的套接字

​ (3) 调用epoll_wait收集发生的事件的连接

只需要在进程启动时建立一个epoll对象,然后在需要的时候向这个epoll对象中添加或者删除连接。同时, epoll_wait的效率也非常高,因为调用epoll_wait时,并没有一股脑的向操作系统复制这100万个连接的句柄数据,内核也不需要去遍历全部的连接

epoll的优点

​ epoll 没有最大并发连接的限制,上限是最大可以打开文件的数目,这个数字一般远大于 2048

cat /proc/sys/fs/file-max 察看

​ 效率提升, epoll 最大的优点就在于它只管你“活跃”的连接 ,而跟连接总数无关,因此在实际的网络环境 中, epoll 的效率就会远远高于 select 和 poll

3.redis中的epoll模型

​ 当某一进程调用epoll_create方法时,Linux内核会创建一个eventpoll结构体,这个结构体中有两个成员与epoll的使用方式密切相关

每一个epoll对象都有一个独立的eventpoll结构体,用于存放通过epoll_ctl方法向epoll对象中添加进来的事件。这些事件都会挂载在红黑树中,如此,重复添加的事件就可以通过红黑树而高效的识别出来(红黑树的插入时间效率是lgn,其中n为树的高度)

​ 而所有添加到epoll中的事件都会与设备(网卡)驱动程序建立回调关系,也就是说,当相应的事件发生时会调用这个回调方法。这个回调方法在内核中叫ep_poll_callback,它会将发生的事件添加到rdlist双链表中

​ 在epoll中,对于每一个事件,都会建立一个epitem结构体

​ 当调用epoll_wait检查是否有事件发生时,只需要检查eventpoll对象中的rdlist双链表中是否有epitem元素即可。如果rdlist不为空,则把发生的事件复制到用户态,同时将事件数量返回给用户

优点

​ 1.不用重复传递。调用epoll_wait时就相当于以往调用select/poll,但是这时却不用传递socket句柄给内核,因为内核已经在epoll_ctl中拿到了要监控的句柄列表。

​ 2.在内核里, 一切皆文件。所以,epoll向内核注册了一个文件系统,用于存储上述的被监控socket。当你调用epoll_create时,就会在这个虚拟的epoll文件系统里创建一个file结点。当然这个file不是普通文件,它只服务于epoll。

​ epoll在被内核初始化时(操作系统启动),同时会开辟出epoll自己的内核高速cache区,用于安置每一个我们想监控的socket,这些socket会以红黑树的形式保存在内核cache里,以支持快速的查找、插入、删除。这个内核高速cache区,就是建立连续的物理内存页,然后在之上建立slab层,简单的说,就是物理上分配好你想要的size的内存对象,每次使用时都是使用空闲的已分配好的对象

​ 3.由于我们在调用epoll_create时,内核除了帮我们在epoll文件系统里建了个file结点,在内核cache里建了个红黑树用于存储以后epoll_ctl传来的socket外,还会再建立一个list链表,用于存储准备就绪的事件,当epoll_wait调用时,仅仅观察这个list链表里有没有数据即可。有数据就返回,没有数据就sleep,等到timeout时间到后即使链表没数据也返回。所以,epoll_wait非常高效。

这个准备就绪list链表是怎么维护的呢?

​ 当我们执行epoll_ctl时,除了把socket放到epoll文件系统里file对象对应的红黑树上之外,还会给内核中断处理程序注册一个回调函数,告诉内核,如果这个句柄的中断到了,就把它放到准备就绪list链表里;当一个socket上有数据到了,内核在把网卡上的数据copy到内核中后就来把socket插入到准备就绪链表里了

一颗红黑树,一张准备就绪句柄链表,少量的内核cache,就帮我们解决了大并发下的socket处理问题。执行epoll_create时,创建了红黑树和就绪链表,执行epoll_ctl时,如果增加socket句柄,则检查在红黑树中是否存在,存在立即返回,不存在则添加到树干上,然后向内核注册回调函数,用于当中断事件来临时向准备就绪链表中插入数据。执行epoll_wait时立刻返回准备就绪链表里的数据即可

​ LT, ET这件事怎么做到的呢?当一个socket句柄上有事件时,内核会把该句柄插入上面所说的准备就绪list链表,这时我们调用epoll_wait,会把准备就绪的socket拷贝到用户态内存,然后清空准备就绪list链表,最后,epoll_wait干了件事,就是检查这些socket,如果不是ET模式(就是LT模式的句柄了),并且这些socket上确实有未处理的事件时,又把该句柄放回到刚刚清空的准备就绪链表了。所以,非ET的句柄,只要它上面还有事件,epoll_wait每次都会返回这个句柄.

redis 中 epoll 模型 ~ 霓裳相关推荐

  1. Redis的epoll模型

    之前相关文章推荐:Redis高性能与epoll 本文,我们从源代码的角度,简单理解Redis是如何使用epoll以及epoll的实现原理.浅入浅出~ 找我交流 通过本文了解如下三件事儿,就算是达到了本 ...

  2. epoll源码分析以及在Redis中的实现

    1.概述 这篇文章分析一下linux中epoll的实现原理,主要为了增强自己对网络调用的理解.业界使用epoll的框架比较多,随便就能列出来很多,比如jdk的nio在linux下的实现,以及netty ...

  3. 【重难点】【Redis 01】为什么使用 Redis、Redis 的线程模型、Redis 的数据类型及其底层数据结构

    [重难点][Redis 01]为什么使用 Redis.Redis 的线程模型.Redis 的数据类型及其底层数据结构 文章目录 [重难点][Redis 01]为什么使用 Redis.Redis 的线程 ...

  4. redis的事件模型详解(结合Reactor设计模式)

    文章基于redis-4.0.1源码详细介绍一下redis的事件模型. 一.redis事件模型概览 redis是一个事件驱动的服务程序,在redis的服务程序中存在两种类型的事件,分别是文件事件和时间事 ...

  5. Redis为什么这么快?Redis的线程模型与Redis多线程

    一.Redis有多快? Redis是基于内存运行的高性能 K-V 数据库,官方提供的测试报告是单机可以支持约10w/s的QPS 二.Redis为什么这么快? (1)完全基于内存,数据存在内存中,绝大部 ...

  6. 03 Redis 网络IO模型简介

    1 Redis中的单线程模型 提起Redis,我们经常会说其底层是一个单线程模型,但这是不严谨的.Redis 单线程指的是网络请求模块使用了一个线程,即一个线程处理所有网络请求,其他模块该使用多线程, ...

  7. redis使用epoll

    redis使用epoll的代码在ae_epoll.c文件中. epoll_create:redis服务器在启动时,创建事件循环,调用epoll_create方法创建epoll实例. static in ...

  8. 朴素、Select、Poll和Epoll网络编程模型实现和分析——Poll、Epoll模型处理长连接性能比较

    在<朴素.Select.Poll和Epoll网络编程模型实现和分析--模型比较>一文中,我们分析了各种模型在处理短连接时的能力.本文我们将讨论处理长连接时各个模型的性能.(转载请指明出于b ...

  9. 朴素、Select、Poll和Epoll网络编程模型实现和分析——Epoll模型

    在阅读完<朴素.Select.Poll和Epoll网络编程模型实现和分析--Select模型>和<朴素.Select.Poll和Epoll网络编程模型实现和分析--Poll模型> ...

  10. linux epoll模型

    原文:http://yjtjh.blog.51cto.com/1060831/294119 Linux I/O多路复用技术在比较多的TCP网络服务器中有使用,即比较多的用到select函数.Linux ...

最新文章

  1. 程序员:我不学Python了!!
  2. Oracle表里的照片怎么导出来,如何导出oracle数据库中某张表到excel_oracle数据库表格导出到excel...
  3. 基于并联SVM支持向量机训练HOG特征提取的人员目标提取
  4. 【洛谷 P4168】[Violet]蒲公英(分块)
  5. 深度解析PHP数组函数array_chunk
  6. boost::gil::rgb8_image_t::recreate用法的测试程序
  7. 《C++覆辙录》——2.9:自反初始化
  8. Android之解决AppBarLayout 下面一道阴影
  9. ubuntu 16.04 apache 开启Rewrite功能
  10. php调用apache,apache调用php执行系统命令
  11. pytorch nn.ReLU
  12. C++基础知识(四)文件的基本操作
  13. 6.2 JAVA方法的三种调用格式
  14. 输入正方体的边长,计算正方体的面积和体积
  15. 如何测试5.1声卡测试软件,功能至上--德国坦克AUREON 5.1初步测试
  16. GPRS网络继电器SAC07GSA评估套件使用心得
  17. 新浪微博广告投放展现形式、展现位置!微博推广广告有效果吗?
  18. steam有没有c语言软件,Steam 免费游戏一键领取(已更新,需自行添加代码)
  19. Game.Physics.Engine.Development(游戏物理引擎开发)
  20. Centos6普通用户获取最高权限方法

热门文章

  1. 手机怎么修改浏览器的html,手机设置默认浏览器
  2. gcc编译链中i686和x86-64有什么区别?
  3. 千锋重庆IT学习之微信API接口文档
  4. Ubuntu20.04 安装matlab2017b
  5. java对人脸打马赛克,如何给视频中的人脸进行马赛克 视频人脸打马赛克软件|人脸跟踪马赛克...
  6. Git下载安装以及基本指令使用
  7. 中国全国行政区代码表
  8. WES 7 FBWF灾难性故障
  9. 计算机408重点知识及其他(面试)
  10. SNMPWALK命令解析