Linux 服务器编程学习笔记⑥
# 高性能服务器程序框架
# 模型
# C/S模型
所有客户端通过访问服务器来获取所需的资源。实现简单,但是使服务器成为通信中心,访问量较大时,所有客户端响应都变慢。
工作流程如下:
# P2P模型
让网络上的所有主机重新回到对等的地位,每台主机既是客户端又是服务端。云计算机群可以看作此类模型的一个典范。但是当用户之间请求较多时,网络负载会加重。
# 编程框架
基本框架如下:
I/O处理单元:
单机:处理客户连接,读写网络数据。
集群:作为接入服务器,实现负载均衡。
逻辑单元:
单机:业务进程或线程。
集群:逻辑服务器。
网络存储单元:
单机:本地数据库或者文件、缓存。
集群:数据库服务器。
请求队列:
单机:各单元间的通信方式。
集群:各服务器之间的永久连接。
# I/O模型
阻塞的文件描述符为阻塞I/O(accept、send、recv、connect);非阻塞的文件描述符为非阻塞I/O。
阻塞I/O通常要和其他I/O通知机制一起使用。
I/O复用是最常使用的I/O通知机制。它指的是应用程序通过I/O复用函数向内核注册一组事件,内核通过I/O复用函数把其中就绪的事件通知给应用程序(select、poll、epoll_wait)。
SIGIO信号也可以用来报告I/O事件。为一个文件描述符指定宿主进程,那么被指定的宿主进程将捕获到SIGIO信号,从而触发SIGIO的信号处理函数进行我们的操作了。
异步I/O由内核来执行I/O操作。
# 事件处理模型
服务器程序通常需要处理三类事件:I/O事件、信号、定时事件。所以需要有事件处理模式(Reactor和Proactor)。
# Reactor模式
主线程(I/O处理单元)只负责监听文件描述符上是否有事件发生,有的话就将该事件通知工作线程(逻辑处理单元),且除此之外主线程不做任何其他实质性的工作。
流程:
主线程向epoll内核事件表中注册socket上的读就绪事件。
主线程调用epoll_wait等待socket上的读就绪事件。
socket上有数据可读时,epoll_wait通知主线程,主线程则将socket可读事件放入请求队列。
睡眠在请求队列上的某个工作线程被唤醒,从socket读取数据并处理,然后向epoll内核事件表中注册该socket写就绪事件。
主线程调用epoll_wait等待socket可写。
socket可写时,epoll_wait通知主线程,主线程将socket可写事件放入请求队列。
睡眠在请求队列上的某个工作线程被唤醒,向socket写入服务器客户请求结果。
注:工作线程可从事件类型区分执行逻辑。
# Proactor模式
所有I/O操作都交给主线程和内核来处理,工作线程仅仅负责业务逻辑。
流程:
主线程调用aio_read函数向内核注册socket的读完成事件,并告诉内核用户读缓冲区的位置,以及读操作完成后如何通知应用程序(以信号为例)。
主线程继续处理其他逻辑。
socket上的数据被读入用户缓冲区后,内核将向应用程序发送一个信号,通知应用程序数据已可用。
应用程序预先定义好的信号处理函数选择一个工作线程来处理客户请求。处理完之后,工作线程调用aio_write函数向内核注册socket上的写完成事件,并告诉内核用户写缓冲区的位置,以及操作完成时如何通知应用程序。
主线程继续处理其他逻辑。
用户缓冲区的数据被写入socket后,内核向应用程序发送一个信号,以通知应用程序数据已经发送完毕。
应用程序预先定义好的信号处理函数选择一个工作线程来做善后处理。
# 模拟Proactor模式
使用同步I/O方式模拟Proactor模式。主线程执行数据读写操作,完成后向工作线程通知事件,然后工作线程只进行逻辑处理。
流程:
主线程往epoll内核事件表中注册socket上的读就绪事件。
主线程调用epoll_wait等待socket上有数据可读。
socket有数据可读时,epoll_wait通知主线程。主线程从socket循环读取数据,然后将数据封装成一个请求对象插入请求队列。
睡眠在请求队列上的某个工作线程被唤醒,处理客户请求,并往epoll内核事件表中注册socket上的写就绪事件。
主线程调用epoll_wait等待socket可写。
socket可写时,epoll_wait通知主线程,主线程往socket上写入服务器处理客户请求的结果。
# 并发模式
并发编程的目的是让程序”同时“执行多个任务。如果计算是密集型的,并发编程没有优势,反而会因为切换任务使效率降低。但是如果计算是IO密集型的(读写文件、访问数据库等),可以使等待IO的线程主动放弃CPU从而让CPU利用率上升。
并发模式是指I/O处理单元和多个逻辑单元之间协调完成任务的方法。两种模式:半同步/半异步模式和领导者/追随者模式。
# 半同步/半异步模式
此同步/异步概念与IO模型不同。同步指的是程序完全按照代码序列进行执行;异步指程序由系统事件来驱动(中断、信号等)。
此模式中,同步线程处理客户逻辑;异步线程处理I/O事件。异步线程监听到客户请求后,将其封装成对象并插入请求队列,请求队列再通知同步线程来处理逻辑。
# 领导者/追随者模式
由多个工作线程轮流获得事件源集合,轮流监听、分发并处理事件。任意时间下,程序仅有一个领导者,其负责监听I/O事件,其余都是追随者。当领导者检测到I/O事件,首先从线程池推选出新的领导者,然后处理I/O事件。
无需在线程间传递信息,但其也仅支持一个事件源集合,不能让每个工作线程独立地管理多个客户连接。
# 有限状态机
有限状态机是一种用来进行对象行为建模的工具,其作用主要是描述对象在它的生命周期内所经历的状态序列,以及如何响应来自外界的各种事件。
状态机可归纳为4个要素,即现态、条件、动作、次态。
现态:是指当前所处的状态。
条件:又称为“事件”。当一个条件被满足,将会触发一个动作,或者执行一次状态的迁移。
动作:条件满足后执行的动作。动作执行完毕后,可以迁移到新的状态,也可以仍旧保持原状态。动作不是必需的,当条件满足后,也可以不执行任何动作,直接迁移到新状态。
次态:条件满足后要迁往的新状态。“次态”是相对于“现态”而言的,“次态”一旦被激活,就转变成新的“现态”了。
# 其他提高服务器性能的方法
池:以空间换时间,提前预申请好各类资源,当需要使用时直接分配而非现场申请。内存池、进程池、线程池、连接池等等。
避免数据在内核空间和用户空间内的复制。在不需要对数据进行处理的情况下,可通过零拷贝的方式发送数据。