• 多线程IO模型


    服务端编程,首要问题是选取IO模型。即如何处理大量连接,服务更多的客户端?

    我们最早有2种解法,各有不足:

    1、阻塞IO,每个连接都需要一个线程。

    随着连接数增多,线程数剧增,系统开销太大。

    2、非阻塞IO,采用“忙轮询”的方式处理多个连接。

    空闲连接很多时,太浪费CPU。

    现在,业内常用方案是IO复用。

    单线程处理大量连接,应用不需要“忙轮询”,内核发现“活跃连接”通知应用。所有连接都空闲时,阻塞应用线程,释放CPU。

    目前,最成熟的IO复用方案是epoll,我们今天的主角。

    epoll的3个API

    如上图,epoll的核心是3个API,核心数据结构是:1个红黑树 和 1个链表

    1. epoll_create()

    功能:创建句柄epollfd。

    内核准备数据结构:1个Map(就那个红黑树) 和 1个List(就那个链表)

    2. epoll_ctl(epollfd, my_events)

    功能:注册我关心的事件(fd, 可读/可写)。

    内核把事件写入Map,方便快速查找。

    当某个fd发生IO事件时,内核到Map中查找my_events,复制到List。

    3. epoll_wait(epollfd, ready_events)

    功能:返回就绪事件。

    copy一份List返回。

    2类fd与3件事

    服务端支持多连接,标识连接的fd分为2类:

    1. listenfd

    一般情况,只有一个。用来监听一个特定的端口(如80)。

    2. connfd

    每个连接都有一个connfd。用来收发数据。

    针对这2种fd,服务端主要做3件事,如下图:

    1. accept listenfd,创建一个connfd

    2. 读/写 connfd

    应用/内核间copy数据。

    每个connfd对应着2个应用缓冲区:readbuf、writebuf

    3. 处理 connfd发来的数据

    业务逻辑处理,准备response到 writebuf。

    单线程下使用epoll

    把上文提到的:3个API、2种fd、3件事,揉到一块,用伪码表示。

    注:下面的代码,在手机上建议横屏阅读。

    多线程下的IO模型

    我们听过很多多线程IO模型:Reactor、Proactor、领导者/追随者balabala...

    太多了,傻傻分不清楚。

    从上文,我们知道,服务端IO编程主要处理3件事,我们给这3件事编号:1、2 和 3。

    不管单线程,还是多线程,核心都是做这3件事。

    各种多线程IO模型的区别就是:这3件事交给哪些线程做了。

    按照这个规则,我们把常见的多线程IO模型,整理到一棵“决策树”上。

    希望能帮助大家记忆。

    1. redis模式

    处理:单线程独自干3件事。

    类比:饭馆,夫妻店。

    老板自己当服务员,把所有的活都干了(接待、点菜、上菜等)。

    2. nginx模式

    处理:开启8个工作进程,各自独立。

    新连接来了,大家一起抢,谁抢到算谁的(这是第1件事,每个进程都调用accept,但只有一个能成功,得到connfd)。

    抢到之后(得到connfd),各进程独自干剩下的2件事(读写connfd、业务处理)。

    类比:小饭馆。

    雇了8个服务员。

    新客人一进门,8个服务员一拥而上,谁抢到算谁的。

    抢到之后,独自服务该客人。

    惊群效应:新客人一进门,8个服务员都被唤醒,注定只有1个能抢到,浪费7个服务员的注意力。

    3. 线程分角色

    处理:3件事分配给不同的线程。

    线程间需要通信,分配任务。

    类比:大饭馆。

    门口专门站一个“大服务员”(主线程),负责排号。

    客人到号了,“大服务员”指派一个专门的“小服务员”,接待客人。

    大厅里有一群“小服务员”(工作线程),负责接待客人到餐桌。

    “小服务员”还可以再拆分工作(拆出业务线程),领路、点菜、上菜、收钱可以交给不同的“小服务员”做。

    4. 领导者/追随者

    处理:线程的角色在3种状态间变换,每种角色职责不同。

    事件集和数据是公用的,线程间不需要通信。

    任一瞬间,最多只有一个Leader线程,负责响应新事件。

    Leader:获取事件后,变成Processing,处理、读写数据。

    同时指定一个Follower接替自己。

    Processing:完成业务处理后,如果没有Leader,自己成为Leader;

    如果有Leader,自己成为Follower。

    Follower:等待被指派为Leader。

    类比:机场出租车。

    出租车排了个长队。

    队首的出租车,是Leader:响应新乘客。

    后面排队的出租车,是Follower:等待成为Leader。

    乘客上车后,Leader出租车转为Processing:服务乘客。

    转自:http://www.360doc.com/content/17/0807/14/33093582_677312246.shtml

  • 相关阅读:
    JavaScript:综合案例---房贷计算器的实现
    iOS:如何将自己的SDK用CocoaPods管理
    JavaScript:综合案例-表单验证
    JavaScript:日期选择器组件的使用
    JavaScript : 基本的处理事件
    JavaScript:window窗口对象
    JavaScript:文本域事件处理
    JavaScript:下拉列表框的事件处理
    JavaScript:复选框事件的处理
    JavaScript:单选钮的事件处理
  • 原文地址:https://www.cnblogs.com/GHzcx/p/9505640.html
Copyright © 2020-2023  润新知