跳转至
内容字体
东观体
上图东观体
OPPO Sans
江城黑体
霞鹜臻楷
代码字体
DejaVu Sans Mono
DejaVu Sans Mono
Google Sans Code
JetBrains Mono
主题切换
返回顶部

项目介绍与准备

约 2082 个字 3 张图片 预计阅读时间 7 分钟

项目介绍

关于Muduo库文档中已经对Muduo进行了一个基本的介绍,但是为了更好得理解它的实现原理,本次项目将基于该网络库的实现思路自主实现一个高性能服务器,从底层去了解为什么Muduo库高效

在本次项目中,主要实现基于TCP协议的高性能服务器的服务端而不实现对应的客户端,另外还会基于一个高性能服务器的框架搭建一个基于HTTP协议的Web服务器

设计思想

实现的一个基于TCP的网络服务器实际上需要的就是两个功能:

  1. 接收客户端的连接
  2. 处理客户端发送的数据

在实现这两个功能中,有下面几种常见的思路:

Note

此处只考虑使用多路转接的方式

  1. 一个服务器进程有一个epoll,既进行监听套接字事件监听,也进行通信描述符事件监听,并同时进行数据处理:这种思路实现方式很简单,不需要考虑线程安全或者进程间通信问题,但是缺点也很明显,如果有很多客户端连接时,会出现明显的性能瓶颈
  2. 一个服务器进程有一个epoll,既进行监听套接字事件监听,也进行通信描述符事件监听,但是数据压入线程池等待处理:这种思路和上一个思路最大的区别就是充分利用了多核CPU的资源,但是缺点就是在epoll对两种描述符进行监听,在高并发情况下,可能就绪的事件非常多,从而依旧会出现明显性能瓶颈
  3. 一个服务器进程有一个epoll,但是这个epoll只对监听套接字进行事件监听,而一旦有一个客户端建立连接,就将其数据通信描述符交给对应线程的epoll进行监听,数据也交给该线程去处理:这种思路就是将主Reactor模型(负责监听套接字监控和处理)和从Reactor模型(负责数据通信描述符监控和处理)进行分离,从而保证高效进行连接获取和数据处理
  4. 一个服务器进程有一个epoll,但是这个epoll只对监听套接字进行事件监听,而一旦有一个客户端建立连接,就将其数据通信描述符交给对应线程的epoll进行监听,数据压入线程池等待处理:这种方式与上一种方式比较类似,只是进一步将数据描述符的监听和数据处理分开,但是这种设计思路中线程的数量会比较多,CPU频繁切换导致效率就会不高

基于上面的四种情况分析,本次项目实现的思路就是第三种:一个服务器进程有一个epoll,但是这个epoll只对监听套接字进行事件监听,而一旦有一个客户端建立连接,就将其数据通信描述符交给对应线程的epoll进行监听,数据也交给该线程去处理。这种思路也被称为One Thread One Loop,既一个线程拥有一个时间监控。示意图如下:

项目模块介绍

Note

本部分不介绍有关HTTP模块的内容

了解了基本的设计思想之后,下面基于该思想设计本次项目的模块:

  1. 缓冲区(Buffer模块):在本次实现的高性能服务器中,如果直接使用原生的接收接口,默认情况下就会出现一个用于数据处理的线程阻塞等待着数据的到来再进行数据读取,这无疑是对资源的浪费,对于发送接口也是同样的道理。另外,因为TCP是面向字节流的,可能客户端传递给服务端的数据比较大,一个TCP报文无法携带完整的数据,导致客户端需要多次发送,而服务端因为数据不完整就不应该进行完整处理,但是如果只是将数据留在TCP的缓冲区中,那么当数据非常大时,该缓冲区依旧会被填满。基于上面的原因,在本次项目中,需要设计一个应用层的缓冲区Buffer,该缓冲区的主要作用就是将TCP缓冲区的数据进行保存,当上层处理接口发现数据不完整时不对其进行处理而是继续留在该缓冲区,一旦发现数据完整时就继续处理,而因为发送和接收对应的文件描述符都是交给epoll进行事件监控和通知,所以提供给上层的发送和接收接口也只是将数据放到输出缓冲区和输入缓冲区中,等到对应的可读或者可写事件触发时再调用原生的发送和接收接口将缓冲区数据进行发送或者接收
  2. 套接字封装(Socket模块):该模块是对底层的网络相关接口和对应的套接字进行封装,简单来说就是一个文件描述符对应着一个Socket对象
  3. 事件管理(Channel模块):该模块用于管理每一个描述符拥有的事件以及对应的事件处理回调函数,简单来说就是一个文件描述符对应一个Channel对象。当一个文件描述符对应的事件触发时就会调用对应的回调函数
  4. 连接管理(Connection模块):该模块是用于对每一个客户端连接进行管理,简单来说就是一个客户端连接对应着一个Connection对象。在该模块中,要提供用于Channel模块的各种事件回调,便于进行网络通信,比如数据的接收和发送,但是这是一部分只是针对底层进行的行为,底层既然要处理数据首先就是要有数据,而数据都是来自于上层,所以需要提供一些回调函数,这些回调函数的设置就是由上层去决定
  5. 客户端连接接收(Acceptor模块):该模块主要是对接收客户端这一行为进行封装,具体接收到客户端连接建立对应的用于数据通信的套接字如何处理交给上层决定,简单来说,这个类只会用于服务端用于接收客户端连接
  6. 定时任务管理时间轮(TimingWheel模块):该模块主要对当前服务端的定时任务管理,例如在本次项目中实现超时释放客户端连接的功能
  7. 基于epoll的封装(Poller模块):该模块用于对epoll的操作进行封装,便于上层进行使用
  8. 事件监控(EventLoop模块):该模块是对触发事件的处理进行封装,主要用于启动事件监控,并对活跃的事件进行处理,同时如果上层有事件需要修改,也需要通过该类中的接口进行设置。在本次项目中,因为是一个线程对应着一个事件监控,也就是说,当前模块必须是在自己所属的线程中执行。另外提供对定时任务的管理,便于指定的线程设置、取消和刷新定时任务
  9. 事件监控线程池(LoopThreadPool模块):该模块用于管理事件监控线程,便于在接收到客户端连接时可以快速的交给一个线程而不需要频繁创建线程和销毁线程,减少性能的开销
  10. 基于TCP协议的服务器(TcpServer模块):该模块就是将上面的所有模块进行整合,便于上层搭建TCP服务器

模块间关系概览

  1. 服务器接收客户端连接功能:

  2. 服务端处理客户端数据功能: