【服务器】服务器模型

问题聚焦:

核心章节。
服务器一般分为如下三个主要模块:I/O处理单元(四种I/O模型,两种高效事件处理模块),逻辑单元(两种高效并发模式,有效状态机)和存储单元(不讨论)。


服务器模型

C/S模型

结构:


c/s

特点:
逻辑简单。
工作流程:


c/s流程

I/O复用技术:select,同时监听多个客户请求。
优点:适合资源相对集中的场合。
缺点:当访问量过大,可能所有客户都将得到很慢的相应。


P2P模型

结构:两种结构


p2p

结构b比结构a增加了发现服务器,用于主机之间的互相发现,尽快找到自己需要的资源。
特点:
摒弃了服务器为中心的格局,让网络上所有主机处于对等的地位。
每台机器在消耗服务的同时也给别人提供服务
缺点:当用户之间传输的请求过多时,网络的负载将加重



服务器编程框架

基本框架:


框架

模块说明:

模块 单个服务器程序 服务器集群
IO处理单元 处理客户连接,读写网络数据 作为接入服务器,实现负载均衡
逻辑单元 业务进程或线程 逻辑服务器
网络存储单元 本地数据库、文件或缓存 数据库服务器
请求队列 各单元之间的通信方式 各服务器之间的永久TCP连接

IO模型

阻塞IO

socket在创建的时候是阻塞的。
阻塞模型和非阻塞模型:
阻塞IO:阻塞的文件描述符,系统调用可能因为无法立即完成而被操作系统挂起。
例如:客户端connect发起连接,服务器相应之前的这段时间,connect调用将被挂起,直到确认报文段到达将之唤起。
可能被阻塞的系统调用包括accept,send,recv和connect
非阻塞IO:非阻塞的文件描述符,总是立即返回,不管时间是否发生。
如果事件没有立即发生,这些系统调用返回-1,这是,我们就要确认是延迟还是出错,确认方式是却分报错信息errno。
对accept.send和recv而言,事件未发生时errno通常被设置成EAGAIN(再来一次)或者EWOULDBLOCK(期望阻塞);对connect而言,errno则被设置成EINPROGRESS(在处理中)。
注意:通常情况下,非阻塞IO要和其他IO通知机制一起使用才能提高程序的效率。

IO复用

常用:IO通知机制
描述:应用程序通过IO复用函数向内核注册一组事件,内核通过IO复用函数把其中就绪的事件通知应用程序。
IO复用函数:select、poll和epoll_wait,后面的章节会讨论这些函数。
注意:IO复用函数本身是阻塞的,它们能提高程序效率的原因在于它们具有同时监听多个IO事件的能力。

SIGIO信号

作用:报告IO事件
描述:我们可以为一个目标文件描述符指定宿主进程,那么指定的宿主进程将捕获到SIGIO信号,这样,当目标文件描述符上有事件发生时,SIGIO信号的信号处理函数将被出发,我们也就可以在该信号处理函数中对目标文件描述符执行非阻塞IO操作了。

异步IO模型

上面讨论的三种模型都属于同步IO模型

同步IO模型和异步IO模型的区别

同步:IO的读写操作发生在IO事件之后,由应用程序(用户代码)来完成。
异步:异步IO的读写操作总是立即返回的,不论IO事件是否被阻塞,因为真正的读写操作被内核接管,即内核来执行IO操作,具体表现为数据在内核缓冲区和用户缓冲区之间的移动。
可以认为,同步IO向应用程序通知IO就绪事件,异步IO向应用程序通知IO完成事件(可能并没有真正的完成)

IO模型对比如下:

IO模型 读写操作和阻塞阶段
阻塞IO 程序阻塞于读写函数
IO复用 程序阻塞于IO复用系统调用,但可同时监听多个IO事件,对IO本身的读写操作是非阻塞的
SIGIO信号 信号触发读写就绪事件,用户程序执行读写操作,程序没有阻塞阶段
异步IO 内核执行读写操作并触发读写完成事件,程序没有阻塞阶段

两种高效的事件处理模式

服务器程序通常需要处理三类事件:IO事件,信号和定时事件。后面会一次介绍。
这一节先介绍两种高效的事件处理模式:Reactor(同步IO模型)和Proactor(异步IO模型)。

Reactor模式

描述:
它要求主线程只负责监听文件描述上是否有事件发生,有的话就立即将该事件通知工作线程。
除此之外,主线程不做任何其他实质性的工作。
工作线程负责读写数据,接受新的连接,以及处理客户请求。
流程:
使用同步IO模型(以epoll_wait为例)实现的Reactor模式的工作流程是:
主线程往epoll内核事件表中注册socket上的读就绪事件
主线程调用epoll_wait等待socket上有数据可读
当socket上有数据可读时,epoll_wait通知主线程,主线程则将socket可读事件放入请求队列
睡眠在请求队列上的某个工作线程被唤醒,它从socket读取数据,并处理客户请求,然后往epoll内核事件表中注册该socket上的写就绪事件
当socket可写时,epoll_wait通知主线程,主线程将socket可写事件放入请求队列
睡眠在请求队列上的某个工作线程被唤醒,它往socket上写入服务器处理客户请求的结果。
流程图如下:


Proactor模式

描述:将所有IO操作都交给主线程和内核来处理,工作线程仅仅负责业务逻辑。更符合之前提到的服务器编程框架。
流程:使用异步IO模型(以aio_read和aio_write为例)实现Proactor模式的工作流程是:
主线程调用aio_read函数向内核注册socket上的读写完成事件,并告诉内核用户读缓冲区的位置,以及读操作完成后如何通知应用程序
主线程继续处理其他逻辑
当socket上的数据被读入用户缓冲区后,内核将向应用程序发送一个 信号,以通知应用程序数据可用
应用程序预先定义好的信号处理函数选择一个工作线程来处理客户请求。工作线程处理完客户请求之后,调用aio_write函数想内核注册socket的写完成事件,并啊公诉内核用户写缓冲区的位置,以及写操作完成时如何通知应用程序。
主线程继续处理其他逻辑
当用户缓冲区的数据被写入socket之后,内核将向应用程序发送一个信号,以通知应用程序数据已经发送完毕。
应用程序预先定义好的信号处理函数选择一个工作线程来做善后处理,比如决定是否关闭socket
流程图如下:

同步IO方式模拟Proactor模式

原理:主线程执行数据读写操作,读写完成之后,主线程向工作线程通知这一“完成事件”,工作线程处理后续逻辑。
流程:
主线程往epoll内核事件表中注册socket上的读就绪事件
主线程调用epoll_wait等待socket上有数据可读
当socket上有数据可读时,epoll_wait通知主线程。主线程从socket循环读取数据,直到没有更多数据可读,然后将读取到的数据封装成一个请求对象并插入请求队列
睡眠在请求队列上的某个工作线程被唤醒,它获得请求对象并处理客户请求,然后往epoll内核事件表中注册socket上的写就绪事件
主线程调用epoll_wait等待socket可写
当socket可写时,epoll_wait通知主线程。主线程往socket上写入服务器处理客户请求的结果。
流程图如下:



两种高效的并发模式

并发模式适合:IO密集型任务
方式:多进程和多线程(后面讨论)
描述:并发模式是指IO处理单元和多个逻辑单元之间协调完成任务的方法。
服务器主要有两种并发编程模式:
半同步/半异步模式
领导者/追随者模式

半同步/半异步模式

解释:这里的“同步”和“异步”
同步:程序完全按照代码序列的顺序执行
异步:程序的执行需要由系统事件来驱动,这里的系统事件包括中断、信号等。

同步线程:按照同步方式运行的线程称为同步线程
异步线程:按照异步方式运行的线程称为异步线程

半同步/半异步模式:同步线程用于处理客户逻辑,异步线程用于处理IO事件。

半同步/半反应堆模式

结合考虑两种事件处理模式(Reactor和Proactor)和几种IO模型(阻塞IO,IO复用,SIGIO信号,异步IO),则半同步/半异步就存在多种变体
半同步/半反应堆模式就是其中的一种。
如下图所示:


特点:
异步线程只有一个,由主线程来充当,负责监听所有socket上的事件。
如果有新的连接请求,主线程就接受之,以得到新的连接socket
在epoll内核事件表中注册该socket上的读写事件
如果连接socket上有读写事件发生,即有新的客户请求到来或有数据要发送到客户端,主线程就将该连接socket插入请求队列。
所有工作线程都睡眠在请求队列上,当有任务到来时,它们将通过竞争获得任务的接管权。

领导者/追随者模式

描述:多个工作线程轮流获得事件源集合,轮流监听、分发并处理事件的一种模式。
关键:领导者的变换和IO事件的处理
实现:在任意时间点,程序都仅有一个领导者线程, 它负责监听IO事件,而其他线程都是追随者,它们休眠在进程池等待成为新的领导者。当前领导者如果检测到IO事件,首先要从线程池中推选出新的领导者线程,然后处理IO事件。
结构:


说明:
句柄集:表示IO资源,在Linux下通常就是一个文件描述符。
线程集:所有工作线程的管理者。负责各线程之间的同步和新领导者线程的推选。
事件处理器及其子类: 用回调函数的方式处理某事件发生时对应的业务。
工作流程:

To be continued:后面的专题将介绍有限状态机和提高服务器性能的一些建议

小结:
这篇主要介绍了服务器方面的核心框架和设计模式,是这个系列的核心。后续的篇幅都是实现这些模型的技术相关的介绍。
服务器编程的路很深,但技术方面也是稳定的,不像前端技术那样技术革新很频繁和有趣。

参考资料:
《Linux高性能服务器编程》
————————————————
版权声明:本文为CSDN博主「_suzhou」的原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接及本声明。
原文链接:https://blog.csdn.net/zs634134578/article/details/19806429

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 205,033评论 6 478
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 87,725评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 151,473评论 0 338
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,846评论 1 277
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,848评论 5 368
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,691评论 1 282
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,053评论 3 399
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,700评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 42,856评论 1 300
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,676评论 2 323
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,787评论 1 333
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,430评论 4 321
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,034评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,990评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,218评论 1 260
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 45,174评论 2 352
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,526评论 2 343