linux高性能服务器编程 (八) --高性能服务器程序框架

第八章 高性能服务器编程框架数据库

  这一章主要介绍服务器的三个主要模块: I/O处理单元、逻辑单元、存储单元。另外服务器的模型有:C/S模型和P2P模型。虽然服务器模型比较多,可是其核心框架都同样,只是在于逻辑处理方面。以下图简单的介绍一台服务器或服务器机群模型的基本框架:编程

  

  一、I/O模型缓存

  I/O处理单元:I/O处理单元是服务器管理客户链接的模块。主要是等待并受理新的客户链接接收客户数据,将服务器响应数据返回给客户端。服务器

      逻辑单元:就是一个个进程或者线程。用于处理客户数据,将结果传递给I/O处理单元或者直接发送给客户端。服务器中一般由多个逻辑单元,以实现多个客户端任务的并行处理。网络

  网络存储单元:网络存储单元用于存储数据库、缓存以及文件。框架

  请求队列:请求队列是各个单元之间的通讯方式的抽象。请求队列一般被实现为池的一部分。请求队列是各台服务器之间预先创建的静态的永久的TCP链接。异步

  I/O模型 有阻塞I/O模型和 非阻塞I/O模型。由于socket在建立的时候默认是阻塞的,在建立socket的时候讲第二个参数设置为SOCK_NONBLOCK标志,或经过fcntl系统调用 F_SETFL 命令,将其设置为非阻塞的。对于阻塞I/O执行的系统调用会由于没法当即完成而被操做系统挂起,直到等待事情发生为止。好比:1)客户端经过connect向服务器端发起链接,connect将发送同步报文段给服务器而后等待服务器返回确认报文段。2)若是服务器确认报文段没有当即到达客户端,则connect调用将被挂起,直到客户端收到确认报文段唤醒connect调用。在socket的基础API中,可能被阻塞的系统调用包括 accept send  recv  和 connect.socket

  对于非阻塞I/O老是须要和其余I/O通知机制一块儿使用,若是不和其余通知机制一块儿使用仍是阻塞的。好比:I/O复用 和 SIGIO 信号等 另行去处理I/O,处理是异步的。 在非阻塞I/O执行系统调用老是当即返回一个(通知事件结果)。无论事件是否已经发生都会返回。因此对于非阻塞I/O就须要根据errno来区分红功仍是失败的状况。事件返回的结果类型有(再来一次 EAGAIN) (指望阻塞 EWOULDBLOCK) (在处理中 EINPROGRESS) 。函数

  I/O复用是最经常使用的 I/O通知机制。例如:应用程序经过I/O复用函数向内核注册一组事件,内核经过I/O复用函数把其中就绪的事件通知给应用程序。Linux上经常使用的I/O复用函数有 select  pull  epoll_wait. 它们能提升程序效率的缘由在于它们具备同时监听多个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模型对比

    1)阻塞I/O : 程序阻塞于读写函数;

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

    3)SIGIO信号: 信号触发读写就绪事件,用户陈谷执行读写操做。程序没有阻塞阶段。

    4)异步I/O: 内核执行读写操做并触发读写完成事件。程序没有阻塞阶段。     

  

  二、Reactor 和 Proactor 事件处理模式

  事件处理的两种模式:Reactor 和 Proactor  一般服务器程序须要处理三类事件:I/O事件、信号、定时事件。同步I/O模型一般使用 Reactor模式,异步I/O模式用Proactor处理。也能够经过同步I/O模拟出 Proactor模式;

  Reactor模式:她只要求主线程(i/o处理单元)监听文件描述上是否有事件发生,有就当即通知通知工做线程(逻辑单元)处理任务。全部的读写处理数据都在线程上执行。使用同步I/O模型(epoll_wait为例)实现Reactor模式工做流程是:1)主线程往 epoll 内核事件表注册 socket 上的读就绪事件。2)主线程调用 epoll_wait 等待socket 上有数据可读。3)当socket上有数据可读时,epoll_wait通知主线程,主线程则将socket可读事件放入请求队列。4)请求队列上的某一个线程将会被唤醒。它从socket读取数据,并处理客户请求,而后往epoll内核事件表中注册socket上的写就绪事件。5)主线程调用 epoll_wait等待socket可写。6)当socket可写时,epoll_wait通知主线程。主线程将socket可写事件放入请求队列。7)请求队列上的某一个线程将会被唤醒。它从socket上写入服务器处理客户请求的结果。这是一个环形的操做,以下图:

  

  Proactor模式:她是将I/O操做都交给主线程和内核处理。工做线程仅负责业务逻辑。使用异步I/O(aio_read和 aio_write为例) 实现Proactor模式。1)主线程调用aio_read函数向内核注册socket上的读完成事件,并告诉内核用户读缓冲区的位置,以及读操做完成时如何通知应用程序。2)主线程继续处理其余逻辑。3)当socket上的数据被读入用户缓冲区后,内核将向应用程序发送一个信号,已通知应用程序数据已可用。4)应用程序预先定义好信号处理函数选择一个工做线程来处理客户请求。工做线程处理完客户请求以后,调用aio_write函数向内核注册socket上的写完成事件,并告诉内核用户写缓冲区的位置,以及写操做完成时如何通知应用程序。5)主线程继续处理其余逻辑。6)当用户缓冲区的数据被写入socket以后,内核将向应用程序发送一个信号,以通知应用程序数据已经发送完毕。7)应用程序预先定义好的信号处理函数选择一个工做线程来作善后处理,决定是否关闭socket.以下图:

  

 

  三、池

  池有不少种,常见的有 内存池、进程池、线程池、链接池。池是在服务器启动时预先初始化建立好的长链接。已达到空间换时间的概念提升效率。固然逾期初始化好的数据对它的大小就难以把控了,固然也能够动态扩容。