web 项目–服务器框架

服务器编程基本框架

  • 主要由 I/O 单元,逻辑单元和网络存储单元组成,其中每个单元之间通过请求队列进行通信,从而协同完成任务。

  • 其中

    • I/O 单元 处理客户端连接,读写网络数据
    • 逻辑单元 处理业务逻辑的线程
    • 网络存储单元 指本地数据库和文件等

image-20220902103330317


五种 I/O 模型

  • 阻塞 IO

    调用者调用了某个函数,等待这个函数返回,期间什么也不做,==不停的去检查这个函数有没有返回==,必须等这个函数返回才能进行下一步动作

  • 非阻塞 IO

    非阻塞等待,==每隔一段时间就去检测 IO 事件==是否就绪。

    没有就绪就可以做其他事。非阻塞 I/O 执行系统调用总是立即返回,不管时间是否已经发生,若时间没有发生,则返回-1,此时可以根据 errno 区分这两种情况,对于 accept,recv 和 send,事件未发生时,errno 通常被设置成 eagain

  • 信号驱动 IO

    linux 用套接口进行信号驱动 IO,安装一个信号处理函数,进程继续运行并不阻塞,==当 IO 事件就绪,进程收到 SIGIO 信号。然后处理 IO 事件==。

  • IO 复用

    linux 用 select/poll 函数实现 IO 复用模型,这两个函数也会使进程阻塞

    但是和阻塞 IO 所不同的是这两个函数可以同时阻塞多个 IO操作。而且可以同时对多个读操作、写操作的 IO 函数进行检测。==知道有数据可读或可写时,才真正调用 IO 操作==函数

  • 异步 IO

    linux 中,可以调用 aio_read 函数告诉内核描述字缓冲区指针和缓冲区的大小、文件偏移及通知的方式,然后立即返回,==当内核将数据拷贝到缓冲区后,再通知应用程序==。

注意:

阻塞 I/O,非阻塞 I/O,信号驱动 I/O 和 I/O 复用都是同步 I/O。

同步 I/O 指内核向应用程序通知的是就绪事件,比如只通知有客户端连接,要求用户代码自行执行 I/O 操作,

异步 I/O 是指内核向应用程序通知的是完成事件,比如读取客户端的数据后才通知应用程序,由内核完成 I/O 操作


事件处理模式

  • reactor 模式

    ==主线程(I/O 处理单元)==只负责监听文件描述符上是否有事件发生,有的话立即通知工作线程(逻辑单元 ),读写数据、接受新连接及处理客户请求均在工作线程中完成。通常由同步 I/O实现。

  • proactor 模式中

    ==主线程和内核==负责处理读写数据、接受新连接等 I/O 操作,工作线程仅负责业务逻辑,如处理客户请求。通常由异步 I/O实现。


同步 I/O 模拟 proactor 模式

由于异步 I/O 并不成熟,实际中使用较少,这里将使用同步 I/O 模拟实现 proactor 模式。

同步 I/O 模型的工作流程如下(epoll_wait 为例):

  • 主线程往 epoll 内核事件表注册 socket 上的读就绪事件。
  • 主线程调用 epoll_wait 等待 socket 上有数据可读
  • 当 socket 上有数据可读,epoll_wait 通知主线程,主线程从 socket 循环读取数据,直到没有更多数据可读,然后将读取到的数据封装成一个请求对象并插入请求队列。
  • 睡眠在请求队列上某个工作线程被唤醒,它获得请求对象并处理客户请求,然后往 epoll 内核事件表中注册该 socket 上的写就绪事件
  • 主线程调用 epoll_wait 等待 socket 可写。
  • 当 socket 上有数据可写,epoll_wait 通知主线程。主线程往 socket 上写入服务器处理客户请求的结果。