《Linux高性能服务器编程》---读书笔记(二)---高性能服务器程序框架

本文是《Linux高性能服务器编程》读书笔记的第二部分,主要探讨了服务器程序的框架设计,包括C/S模型、服务器基本框架、I/O处理模块和逻辑处理模块。重点介绍了I/O模型,如阻塞与非阻塞、I/O复用(select、poll、epoll)、信号驱动和异步I/O。此外,还讨论了Reactor和Proactor两种事件处理模式,以及半同步/半异步和领导者/追随者两种高效的并发模式。最后提到了有限状态机的编程方法和提高服务器性能的建议,如资源池和减少数据复制。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >


高性能服务器程序框架

  • 服务器模型:

    • C/S模型:服务器、客户端模型。客户端访问服务器获取资源。
      • 服务器启动后,创建监听socket,调用bind绑定服务器端口上,然后listen来等待客户端连接。
      • 服务器稳定后,客户端可以发起连接服务器。对于服务端,请求是随意异步事件,需要通过一些I/O模型来监听。使用I/O复用。当监听到请求后,accept接受,然后分配一个逻辑单元为这个连接服务。在处理服务的同时,还需要同时监听其他客户的请求,可以通过select系统调用来实现。
        在这里插入图片描述
  • 服务器基本框架
    在这里插入图片描述

    • I/O处理模块:服务器管理客户连接的模块。它通常要完成以下工作:等待并接受新的客户连接,接收客户数据,将服务器响应数据返回给客户端。但是,数据的收发也有可能在逻辑单元。
    • 逻辑处理模块:通常是一个进程或线程。它分析并处理客户数据,然后将结果传递给IO处理单元或者直接发送给客户端(具体使用哪种方式取决于事件处理模式)服务器通常拥有多个逻辑单元,以实现对多个客户任务的并行处理。
    • 请求队列:请求队列,各单元之间通信;
    • 网络存储单元:本队数据库文件等。
  • I/O模型

    • 默认的读写操作都是阻塞的,而可以设置为非阻塞,那么则以返回值以及errno(记录系统的最后一次错误代码)来标识结果。
    • I/O复用:程序阻塞在I/O复用函数上,但是同时监听多个I/O事件,所以虽然也阻塞,但是大大提高了效率。具体原理是应用程序通过向内核注册一组事件,然后阻塞在复用函数上,内核通过复用函数将就绪事件告知应用程序。有select, poll, epoll_wait三种。
    • 信号非阻塞方式:信号触发读写就绪事件,程序无阻塞。
    • 异步I/O:用户直接对I/O读写,这些操作会让内核具体操作,内核完成后,则通知用户。
  • 两种高效的事件处理模式

    • Reactor模式:主线程I/O处理单元只负责监听文件描述符上的事件,有的话则立即通知工作线程(逻辑处理单元),主线程不做别的,读写数据,接受连接,业务处理都在工作线程。

      • 使用向步1O模型(以 epoll_wait2为例)实现的 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操作交给主线程和内核来处理,工作线程只进行业务处理。一般使用异步模型。

      • Proactor模式工作流程如下:
      1. 主线程调用 aio_read函数,向内核注册 socket 上的读完成事件,并告诉内核用户读缓冲区的位置,以及读操作完成时如何通知应用程序。
      2. 主线程执行其他逻辑。
      3. 当内核读操作完成,向应用程序发送信号,通知。
      4. 应用程序预先定义好信号处理函数,信号处理函数选择一个工作线程来处理请求。处理完成后,同样是注册一个aio_write,向内核注册一个写完成事件,告诉内核写缓冲区的位置。
      5. 主线程执行其他逻辑。
      6. 内核写入socket之后,通过信号告诉应用程序。
      7. 应用程序的信号处理函数来进行收尾工作,比如是否关闭socket等。
        在这里插入图片描述
  • 两种高效的并发模式:

    • 并发是再I/O处理单元和多个逻辑单元之间协调完成任务的方法。

    • 半同步/半异步模式:

      • 这里的同步异步与上面I/O模型中说的不是一回事儿。同步是指程序顺序执行,而异步是说由事件驱动执行。同步线程的效率低,但是逻辑清晰简单;而异步线程的效率高,但是难以调试拓展。
      • 在这个模式中,同步线程用于处理逻辑操作,而异步线程用于处理I/O事件。
      • 大体概述起来就是:异步线程处理I/O事件,监听到客户端请求后,将其封装为请求对象并插入到请求队列中。请求队列将通知某个同步模式的工作线程来处理。
      • 由于有多种io复用以及两种事件处理模式,则此并发模式有多种实现方式。书中介绍两种
        • 半同步/半反应堆模式
          在这里插入图片描述
          • 异步线程即为主线程,负责监听所有socket事件。如果监听socket上有新的连接,即可读事件,则accept然后往epoll内核里注册该连接的读写事件。
          • 如果连接socket上有读写事件的发生,那么则将其插入请求队列中。
          • 所有的同步逻辑线程都睡眠在请求队列,通过竞争获取任务。
        • 另一种高效的半同步/半异步模式:
          在这里插入图片描述
          • 首先可以看出,监听socket在主线程,而连接socket的I/O被分配到工作线程了。
          • 主线程只管监听socket,然后accept,将连接socket发送到队列中。工作线程发现有新的连接socket,则将其上的读写事件注册到自己的epoll内核。
          • 但又说这里不同线程都是异步了,作者有点矛盾啊。
    • 领导者/追随者模式

      • 多个工作线程轮流获得事件源集合,轮流监听、分发并处理事件。像一个排队的状态,每个工作进程在排队领面包,第一个人要负责自己取这个面包,他是领导者。领到面包了之后,则自己取消化这个面包,下一个人又变成了领导者。
  • 有限状态机:一种逻辑单元的编程方法

    • 可以将处理设置为多种状态,在不同状态下处理方式不一样。比如有些应用协议会有包含数据包的类型,那么对于不同的类型则需要又相应的处理逻辑。再比如说HTTP协议头部长度不定,那么在TCP字节流的接收的过程中,则需要标定状态来处理。
  • 高性能服务器的其他建议

    • 池:
      • 池是一组资源的集合,在一开始就把资源初始化好,静态资源分配。 当运行中需要用到的时候,则可以避免初始化带来的效率问题,避免了对于内核的频繁访问。
      • 常见的又内存池,进程池,线程池,连接池等。
    • 数据复制:
      • 避免不必要的数据复制。
      • 比如传送文件可以不用读入内存再使用send函数,可以直接使用sendfile。
      • 比如两个工作进程之间要传送大量数据,则可以通过共享内存而非管道或消息队列。
    • 上下文切换和锁:
      • 不应使用过多的工作线程/进程,否则上下文的切换会占用CPU时间。
      • 再者,锁的加入也会影响效率,所以尽量避免使用,或者减小粒度。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值