Skip to content

Latest commit

 

History

History
118 lines (88 loc) · 4.98 KB

整体架构.md

File metadata and controls

118 lines (88 loc) · 4.98 KB

整体架构


1、初始化

  • 创建监听描述符

    • 绑定地址和端口,创建非阻塞监听描述符
  • 创建Epoll实例

  • 创建线程池

    • 初始化线程池参数
    • 创建线程
    • 子线程运行以下代码:
      • 对mutex加锁
      • 通过condition_variable判断是否任务队列是否有待处理任务,如果没有任务则阻塞并对mutex解锁
      • 如有待处理任务,取出任务队列的第一个节点
      • 对线程池解锁
      • 执行任务
  • 创建定时器管理器

    • 更新当前时间

2、运行服务器

  • 注册监听描述符的可读事件

  • 注册新连接、可读事件、可写事件和关闭连接的事件处理函数

  • 进入事件循环

    • 通过定时器管理器获取下一个超时时间点与现在的时间差timeMs
    • 设置超时时间timeMs,调用epoll_ -> wait(),阻塞等待监听事件发生
    • 调用epoll_ -> wait(),分配事件处理函数,新连接处理函数和断开连接处理函数在I/O线程调用,连接描述符的读写事件处理函数在线程池调用
    • 处理超时事件

3、接受连接

  • 监听描述符设置为ET模式,所以需要循环读监听描述符直到返回EAGAIN错误

  • 调用accept4接受新连接,accept4函数可以直接设置新连接描述符为非阻塞模式

  • 为新连接分配一个HttpRequest对象,设置定时器和注册可读事件到epoll

4、断开连接

  • 判断该连接是否活跃,若活跃则退出,不断开连接

  • 若不活跃,删除定时器,从epoll的监听描述符中删除该文件描述符,回收HttpRequest对象,断开连接

5、可读事件

  • 删除文件描述符的超时定时器

  • 从文件描述符中读数据,根据read的返回值处理

    • 返回0,断开连接
    • EAGAIN错误,对文件描述符进行epoll的MOD操作,注册可读事件(因为使用了EPOLLONESHOT),设置超时定时器并返回
    • 其它错误,断开连接
    • 返回值大于0,解析报文,若解析报文出错,则返回400响应报文并断开连接
    • 若解析报文完成,则通过HttpResponse类构造响应报文,并注册文件描述符可写事件(使用了LT模式)
    • 解析报文未完成,对文件描述符进行epoll的MOD操作,注册可读事件(因为使用了EPOLLONESHOT)

6、可写事件

  • 删除文件描述符的超时定时器

  • 若文件描述符的输出缓冲区为空,设置超时定时器,直接返回

  • 往文件描述符中写数据,根据write的返回值处理

    • EAGAIN错误,对文件描述符进行epoll的MOD操作,注册可写事件(因为使用了EPOLLONESHOT),返回
    • 其它错误,断开连接
    • 缓冲区的数据写完,如果是HTTP长连接,重置HTTP解析状态,对文件描述符进行epoll的MOD操作,注册可读事件(因为使用了EPOLLONESHOT),设置超时定时器,返回;不是HTTP长连接则断开连接
    • 缓冲区的数据没有写完,对文件描述符进行epoll的MOD操作,注册可读事件(因为使用了EPOLLONESHOT),设置超时定时器,返回

7、线程池

  • ThreadPool类的数据成员如下

    using JobFunction = std::function<void()>;
    std::vector<std::thread> threads_;
    std::mutex lock_;
    std::condition_variable cond_;
    std::queue<JobFunction> jobs_;
    bool stop_;
  • 外部对线程池的操作只有添加任务,添加任务流程如下

    • 对互斥量加锁
    • 把任务push进任务队列
    • 解锁
    • 通过condition_variable唤醒一个阻塞线程

8、定时器

  • 定时器包含两个类Timer和TimerManager

  • Timer类是内部类,不对外部开放,外部直接使用TimerManger类

  • TimerManager类用小根堆管理Timer,根据超时时间排序

  • TimerManager的关键函数是addTimer、delTimer、handleExpireTimers和getNextExpireTime

  • addTimer用于添加定时器,流程如下

    • 对定时器管理器加锁
    • 更新当前时间
    • 创建一个Timer对象并添加进小根堆
    • 使HttpRequest对象的timer_指向该Timer对象
  • delTimer用于删除定时器(懒删除)

    • 通过HttpRequest对象的timer_指针对该定时器设置删除标志,并设置timer_为空
  • handleExpireTimers用于调用超时定时器的超时回调函数

    • 遍历小根堆获取定时器,直到小根堆的根节点不超时
    • 若定时器被设置删除标志,则回收定时器资源,继续下一次遍历
    • 定时器超时,调用超时回调函数
  • getNextExpireTime用于获取最近的超时时间,用于设置epoll_wait超时时间

    • 遍历小根堆获取定时器
    • 若小根堆为空,返回-1
    • 若定时器被设置删除标志,则回收定时器资源,继续下一次遍历
    • 若定时器已超时,返回0
    • 若定时器未超时,返回定时器的超时时间 - 当前时间

9、缓冲区

  • 对muduo缓冲区做了修改,基本原理相似,具体参见muduo缓冲区