写在前面

上一篇文章并发模型:Actors与CSP简单介绍了Actors和CSP两种并发模型。如果认真推敲会发现,无论是Actors还是CSP,直观上来说其实都是内存模型,那么高并发的CPU模型是怎么样的呢?

或者说:只有8颗CPU核的一台主机,同一时间至多运行8个线程,如何实现一秒时间内对上万个请求的响应?

select/poll与epoll

select/poll模型工作机理

在epoll之前,存在两种高并发模型:select和poll,大体的步骤是:

# select和poll模式会专门对下面的连接链表进行轮询,查看那个连接上有请求
head->connetion1->connection2->connection3->connection4->...
  1. 创建连接链表。简单讲,同一时间来了一万连接请求,给用户A发来的连接请求创建一个专门的connection1,用户B发来的连接请求创建一个专门的connection2;给用户C发来的……
  2. 遍历步骤1生成的连接链表,从connection1一个个地看直到connection10000,查看哪个连接(connection)上面用户发了新的请求,如果有发现新的请求,则想办法通知负责当前连接的进程(比如我们自己的服务)去响应,然后继续遍历。
  3. 终于遍历到了最后一个连接,继续从头开始遍历。

select/poll模型的局限

从上面的描述可以知道,select/poll模型里面存在一个遍历查找过程。当链表的长度较短,且每个连接(connection)上的请求很频繁时,select/poll的模型工作的很好;但是一旦连接数增加,select/poll模式遍历查找的过程会消耗大量的CPU时间,而且连接数越多情况越恶化,因此限制了这种模式在高访问量场景下的使用。

epoll模型工作机理

既然遍历连接(可以看做一小块内存,是文件描述符的一种)限制了select/poll模型的天花板,那么能不能不要再让CPU遍历那么多连接了。

linux说:可以。

# 连接的列表,每个连接存在一个唯一的id
[0]connection0 | [1]connection1 | [2]connection2 | ... # 发现connection1和connection10有请求
# 把它们加入到一个特殊的链表
head->connection1->connection10

大体的步骤如下:

  1. 创建连接数组列表。同一时间来了一万连接请求,给用户A发来的连接请求创建一个专门的connection0,ID为0;用户B发来的连接请求创建一个专门的connection1,ID为1;给用户C发来的……
  2. linux内核和网卡驱动的约定:当某个连接上有新的请求时,网卡驱动把请求的内容和对应的连接ID一起发给内核。
  3. linux内核拿到了带连接ID的请求,找到对应的connectionID并把它加入到一个特殊链表。
  4. 遍历这个特殊的链表,想办法通知负责当前连接的进程(比如我们自己的服务)去响应,然后继续遍历

注意第4步,因为linux内核已经把存在实际请求的连接拣出来了,因此不存在徒劳功,老老实实处理请求就好了。

epoll的局限

像上面所描述的,epoll杜绝了无意义的遍历,因此在高访问量场景中有很大的发挥空间。但是不能不说,一切都是基于web请求计算量低请求低频的场景。

试想,对于epoll中的connection,如果网卡突然对linux内核说:哥,现在所有的连接都有请求。那么特殊链表里其实就是所有的连接实例了,这种场景下epoll反而不如select/poll模式,毕竟后者步骤少啊。

幸运的是,我们所说的百万访问量,都是人发起的,很契合epoll的使用场景。

golang中的epoll

参见 大专栏  应对百万访问量的epoll模式golang源码的src/runtime/proc.go文件,其在main函数启动时,既开始在系统栈开始运行sysmon函数。

func main() {
//...
systemstack(func() {
newm(sysmon, nil)
})
//...
}

golang源码中的sysmon函数

通过查看sysmon函数可以知道,这个函数主要的是一个无穷的for循环,负责调整时序、GC(垃圾回收)以及epoll检查等。

// sysmon
// Go runtime启动时创建的,负责监控所有goroutine的状态判断是否需要GC,
// 进行netpoll等操作。sysmon函数中会调用retake函数进行抢占式调度
func sysmon() {
//...
for {
if netpollinited() && lastpoll != 0 && lastpoll+10*1000*1000 < now {
//更新最后一次查询G时间,为了下一次做判断
atomic.Cas64(&sched.lastpoll, uint64(lastpoll), uint64(now))
// 从网络I/O查找已经就绪的G,不阻塞
gp := netpoll(false) // non-blocking - returns list of goroutines
//...
}
}
}

进一步查看netpoll函数,能发现主要有下面几个函数:

  1. epollcreate/epollcreate1 创建epoll
  2. epollctl 设置epoll事件
  3. epollwait等待epoll事件

到这里,golang与epoll就算对接上了。因为时间问题,细节暂时就不展开了,大家感兴趣可以自己探索。

小结

本文简单介绍了epoll模型。直观上来讲,并发模型中的Actors模型、CSP模型等,侧重的是内存的分配与信号的管理;但是,如何能充分发挥这些并发模型的优势,满足高并发的真实场景呢?

答案就是epoll模型。相比较于传统的select/poll模型,epoll能更充分地利用cpu的时间,把性能投入到有效的运算中去。

参考

应对百万访问量的epoll模式的更多相关文章

  1. python 简单搭建非阻塞式单进程,select模式,epoll模式服务

    由于经常被抓取文章内容,在此附上博客文章网址:,偶尔会更新某些出错的数据或文字,建议到我博客地址 :  --> 点击这里 可以看我的上篇文章 <python 简单搭建阻塞式单进程,多进程, ...

  2. 浅谈Python-IO多路复用(select、poll、epoll模式)

    1. 什么是IO多路复用 在传统socket通信中,存在两种基本的模式, 第一种是同步阻塞IO,其线程在遇到IO操作时会被挂起,直到数据从内核空间复制到用户空间才会停止,因为对CPython来说,很多 ...

  3. Netty socket.io 启用Epoll 模式异常

    Epoll 环境为Linux 内核2.6 以上版本  Windows下不能启动 1:判断Linux环境 public static boolean isOSLinux() { Properties p ...

  4. [记录]Python的master-worker和epoll模式

    #master-worker模型: #coding:utf-8 import os import sys import socket import time import traceback impo ...

  5. 【Nginx】epoll事件驱动模块

    Linux 2.4之前的内核版本号,Nginx事件驱动的方法是使用poll.select功能.过程必须等待一个事件发生在连接上(接收数据)时间,部连接都告诉内核,由内核找出哪些连接上有事件发生.因为须 ...

  6. select epoll poll

    如何理解 Epoll select 和 poll 三种模型,能否用生活中的例子做比喻? 比如说你从某宝下单买了几个东西,这几个东西分别由N个快递员分别给你送过来.在某一时刻,你开始等快递.对于sele ...

  7. PLG SaaS 产品 Figma 商业模式拆解

    9 月 15 日,Figma 的 CEO Dylan Field 发布消息:今天,Figma 宣布接受 Adobe 的收购... Adobe 以约 200 亿美元收购 Figma,这也是 Adobe ...

  8. [Linux]I/O多路复用和epoll

    首先我们来定义流的概念,一个流可以是文件,socket,pipe等等可以进行I/O操作的内核对象. 不管是文件,还是套接字,还是管道,我们都可以把他们看作流. 之后我们来讨论I/O的操作,通过read ...

  9. python网络编程——IO多路复用之epoll

    1.内核EPOLL模型讲解     此部分参考http://blog.csdn.net/mango_song/article/details/42643971博文并整理 首先我们来定义流的概念,一个流 ...

随机推荐

  1. 虚拟机virtualBox

    在笔记本了装了一个虚拟机, 并安装了Linux系统, 方便测试linux 命令. 考虑到不需要图形界面, 学习了用命令行操作虚拟机, 配置如下 linux 下安装openssh-server 虚拟机设 ...

  2. python导入自定义的库

    一.导入项目文件夹下的模块 1.导入整个模块 import 模块名 2.导入模块的某个函数 from 模块名 import 函数名 示例 untitled是项目文件夹,文件结构如下 ①在a.py导入c ...

  3. ThreadPoolExecutor自定义线程池

    1.ThreadPoolExecutor创建线程池的构造函数 public ThreadPoolExecutor(int corePoolSize, int maximumPoolSize, long ...

  4. drf框架概况-resful接口规范-请求模块-渲染模块-Postman-drf请求生命周期

    drf框架 全称:django-rest- framework 知识点: """ 1.接口:什么是接口.restful接口规范 2.CBV生命周期源码-基于restful ...

  5. Educational Codeforces Round 68 差G

    Educational Codeforces Round 68 E 题意:有 n 个线段,每个都是平行 x 或者 y 轴,只有互相垂直的两线段才会相交.问形成了多少个矩形. \(n \le 5000, ...

  6. 量化投资_TB交易开拓者A函数和Q函数常见组合应用

    1 在交易开拓者当中,关于交易的做单方式一般分为:图表函数和A函数两类. 两类的主要区别为:如果采用图表函数的话,所有的交易内容都是以图表上面的信号为准,当前仓位运行的实际状态是没有的,但是可以显示交 ...

  7. Office 365 邮件流

    进入Exchange管理中心->点击左侧的“邮件流”->进入邮件流配置页面. 一.规则 规则也称传输规则,对通过组织传递的邮件,根据设定条件进行匹配,并对其进行操作.传输规则与众多电子邮件 ...

  8. Python_自动化运维

    1. 生成磁盘使用情况的日志文件 #!/usr/bin/env python#!coding=utf-8​import timeimport os​new_time = time.strftime(' ...

  9. C# 基类派生类构造函数执行顺序分析

    using System; namespace ConsoleApp1 { class Program{ static void Main(string[] args){ B b = new B(); ...

  10. O - 覆盖的面积(线段树+扫描线)

    给定平面上若干矩形,求出被这些矩形覆盖过至少两次的区域的面积. Input输入数据的第一行是一个正整数T(1<=T<=100),代表测试数据的数量.每个测试数据的第一行是一个正整数N(1& ...