高性能服务器程序框架,Linux 高性能服务器编程——高性能服务器程序框架

问题聚焦:

核心章节。

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

服务器模型

C/S模型

结构:

097acf1f769e44d082d9c76f.html

特色:

逻辑简单。

工做流程:

097acf1f769e44d082d9c76f.html

097acf1f769e44d082d9c76f.html

I/O复用技术:select,同时监听多个客户请求。

优势:适合资源相对集中的场合。

缺点:当访问量过大,可能全部客户都将获得很慢的相应。

P2P模型

结构:两种结构

097acf1f769e44d082d9c76f.html

097acf1f769e44d082d9c76f.html

结构b比结构a增长了发现服务器,用于主机之间的互相发现,尽快找到本身须要的资源。

特色:

摒弃了服务器为中心的格局,让网络上全部主机处于对等的地位。

每台机器在消耗服务的同时也给别人提供服务

缺点:当用户之间传输的请求过多时,网络的负载将加剧

服务器编程框架

基本框架:

097acf1f769e44d082d9c76f.html

097acf1f769e44d082d9c76f.html

模块说明:

模块

单个服务器程序

服务器集群

I/O处理单元

处理客户链接,读写网络数据

做为接入服务器,实现负载均衡

逻辑单元

业务进程或线程

逻辑服务器

网络存储单元

本地数据库、文件或缓存

数据库服务器

请求队列

各单元之间的通讯方式

各服务器之间的永久TCP链接

I/O模型

阻塞I/O

socket在建立的时候是阻塞的。咱们能够给socket系统调用的第2个参数传递SOCK_NONBLOCK 标志,或者经过fcntl 系统调用的F_SETFL 命令,将其设置为非阻塞。

阻塞模型和非阻塞模型:

阻塞I/O:阻塞的文件描述符,系统调用可能由于没法当即完成而被操做系统挂起。

例如:客户端connect发起链接,服务器相应以前的这段时间,connect调用将被挂起,直到确认报文段到达将之唤起。

可能被阻塞的系统调用包括accept,send,recv和connect 。

非阻塞I/O:非阻塞的文件描述符,老是当即返回,无论时间是否发生。

若是事件没有当即发生,这些系统调用返回-1,这时咱们就要确认是延迟仍是出错,此时咱们必须根据errno来区分这两种状况。

对accept.send和recv而言,事件未发生时errno一般被设置成EAGAIN(再来一次)或者EWOULDBLOCK(指望阻塞);对connect而言,errno则被设置成EINPROGRESS(在处理中)。

注意:一般状况下,非阻塞I/O要和其余I/O通知机制一块儿使用才能提升程序的效率。

I/O复用

经常使用:I/O通知机制

描述:应用程序经过I/O复用函数向内核注册一组事件,内核经过I/O复用函数把其中就绪的事件通知应用程序。

I/O复用函数:select、poll和epoll_wait,后面的章节会讨论这些函数。

注意:

I/O复用函数自己是阻塞的,它们能提升程序效率的缘由在于它们具备同时监听多个I/O事件的能力。

SIGIO信号

做用:报告I/O事件

描述:咱们能够为一个目标文件描述符指定宿主进程,那么指定的宿主进程将捕获到SIGIO信号。这样,当目标文件描述符上有事件发生时,SIGIO信号的信号处理函数将被触发,咱们也就能够在该信号处理函数中对目标文件描述符执行非阻塞I/O操做了。

异步I/O模型

上面讨论的三种模型都属于同步I/O模型

同步I/O模型和异步I/O模型的区别

同步:I/O的读写操做发生在I/O事件以后,由应用程序(用户代码)来完成。

异步:异步I/O的读写操做老是当即返回的,不论I/O事件是否被阻塞,由于真正的读写操做被内核接管,即内核来执行I/O操做,具体表现为数据在内核缓冲区和用户缓冲区之间的移动。

能够认为,

同步I/O向应用程序通知I/O就绪事件,异步I/O向应用程序通知I/O完成事件(可能并无真正的完成)。

I/O模型对好比下:

I/O模型

读写操做和阻塞阶段

阻塞I/O

程序阻塞于读写函数

I/O复用

程序阻塞于I/O复用系统调用,但可同时监听多个I/O事件,对I/O自己的读写操做是非阻塞的

SIGIO信号

信号触发读写就绪事件,用户程序执行读写操做,程序没有阻塞阶段

异步I/O

内核执行读写操做并触发读写完成事件,程序没有阻塞阶段

两种高效的事件处理模式

服务器程序一般须要处理三类事件:I/O事件,信号和定时事件。后面会一次介绍。

这一节先介绍两种高效的事件处理模式:Reactor(同步I/O模型)和Proactor(异步I/O模型)。

Reactor模式

描述:

它要求主线程只负责监听文件描述上是否有事件发生,有的话就当即将该事件通知工做线程。

除此以外,主线程不作任何其余实质性的工做。

工做线程负责读写数据,接受新的链接,以及处理客户请求。

流程:

使用同步I/O模型(以epoll_wait为例)实现的Reactor模式的工做流程是:

主线程往epoll内核事件表中注册socket上的读就绪事件。

主线程调用epoll_wait等待socket上有数据可读。

当socket上有数据可读时,epoll_wait通知主线程,主线程则将socket可读事件放入请求队列。

睡眠在请求队列上的某个工做线程被唤醒,它从socket读取数据,并处理客户请求,而后往epoll内核事件表中注册该socket上的写就绪事件。

当socket可写时,epoll_wait通知主线程,主线程将socket可写事件放入请求队列。

睡眠在请求队列上的某个工做线程被唤醒,它往socket上写入服务器处理客户请求的结果。

流程图以下:

097acf1f769e44d082d9c76f.html

097acf1f769e44d082d9c76f.html

Proactor模式

描述:将全部I/O操做都交给主线程和内核来处理,工做线程仅仅负责业务逻辑。更符合以前提到的服务器编程框架。

流程:使用异步I/O模型(以aio_read和aio_write为例)实现Proactor模式的工做流程是:

主线程调用aio_read函数向内核注册socket上的读写完成事件,并告诉内核用户读缓冲区的位置,以及读操做完成后如何通知应用程序。

主线程继续处理其余逻辑。

当socket上的数据被读入用户缓冲区后,内核将向应用程序发送一个 信号,以通知应用程序数据可用。

应用程序预先定义好的信号处理函数选择一个工做线程来处理客户请求。工做线程处理完客户请求以后,调用aio_write函数想内核注册socket的写完成事件,并告诉内核用户写缓冲区的位置,以及写操做完成时如何通知应用程序。

主线程继续处理其余逻辑。

当用户缓冲区的数据被写入socket以后,内核将向应用程序发送一个信号,以通知应用程序数据已经发送完毕。

应用程序预先定义好的信号处理函数选择一个工做线程来作善后处理,好比决定是否关闭socket。

流程图以下:

097acf1f769e44d082d9c76f.html

链接socket上的读写事件是经过aio_read/aio_write 向内核注册的,所以内核将经过信号来向应用程序报告链接socket上的读写事件。因此,主线程中的epoll_wait调用仅能用来检测监听socket 上的链接请求事件,而不能用来检测链接socket 上的读写事件。

097acf1f769e44d082d9c76f.html

同步I/O方式模拟Proactor模式

原理:主线程执行数据读写操做,读写完成以后,主线程向工做线程通知这一“完成事件”,工做线程处理后续逻辑。

流程:

主线程往epoll内核事件表中注册socket上的读就绪事件。

主线程调用epoll_wait等待socket上有数据可读。

当socket上有数据可读时,epoll_wait通知主线程。主线程从socket循环读取数据,直到没有更多数据可读,而后将读取到的数据封装成一个请求对象并插入请求队列。

睡眠在请求队列上的某个工做线程被唤醒,它得到请求对象并处理客户请求,而后往epoll内核事件表中注册socket上的写就绪事件。

主线程调用epoll_wait等待socket可写。

当socket可写时,epoll_wait通知主线程。主线程往socket上写入服务器处理客户请求的结果。

流程图以下:

097acf1f769e44d082d9c76f.html

097acf1f769e44d082d9c76f.html

两种高效的并发模式

并发模式适合:I/O密集型任务

方式:多进程和多线程(后面讨论)

描述:并发模式是指I/O处理单元和多个逻辑单元之间协调完成任务的方法。

服务器主要有两种并发编程模式:

半同步/半异步模式

领导者/追随者模式

半同步/半异步模式

解释:这里的“同步”和“异步”

同步:程序彻底按照代码序列的顺序执行

异步:程序的执行须要由系统事件来驱动,这里的系统事件包括中断、信号等。

097acf1f769e44d082d9c76f.html

097acf1f769e44d082d9c76f.html

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

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

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

半同步/半反应堆模式

结合考虑两种事件处理模式(Reactor和Proactor)和几种I/O模型(阻塞I/O,I/O复用,SIGIO信号,异步I/O),则半同步/半异步就存在多种变体

半同步/半反应堆模式就是其中的一种。

以下图所示:

097acf1f769e44d082d9c76f.html

097acf1f769e44d082d9c76f.html

特色:

异步线程只有一个,由主线程来充当,负责监听全部socket上的事件。

若是有新的链接请求,主线程就接受之,以获得新的链接socket

在epoll内核事件表中注册该socket上的读写事件

若是链接socket上有读写事件发生,即有新的客户请求到来或有数据要发送到客户端,主线程就将该链接socket插入请求队列。

全部工做线程都睡眠在请求队列上,当有任务到来时,它们将经过竞争得到任务的接管权。

领导者/追随者模式

描述:多个工做线程轮流得到事件源集合,轮流监听、分发并处理事件的一种模式。

关键:领导者的变换和I/O事件的处理

实现:在任意时间点,程序都仅有一个领导者线程, 它负责监听I/O事件,而其余线程都是追随者,它们休眠在进程池等待成为新的领导者。当前领导者若是检测到I/O事件,首先要从线程池中推选出新的领导者线程,而后处理I/O事件。

结构:

097acf1f769e44d082d9c76f.html

097acf1f769e44d082d9c76f.html

说明:

句柄集:表示I/O资源,在Linux下一般就是一个文件描述符。

线程集:全部工做线程的管理者。负责各线程之间的同步和新领导者线程的推选。

事件处理器及其子类: 用回调函数的方式处理某事件发生时对应的业务。

工做流程:

097acf1f769e44d082d9c76f.html

097acf1f769e44d082d9c76f.html

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

小结:

这篇主要介绍了服务器方面的核心框架和设计模式,是这个系列的核心。后续的篇幅都是实现这些模型的技术相关的介绍。

服务器编程的路很深,但技术方面也是稳定的,不像前端技术那样技术革新很频繁和有趣。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值