周末程序猿
鹅厂程序猿,专注后台开发和人工智能领域~~
99篇原创内容
公众号
想起很久之前拖更的一篇关于《Linux高性能网络编程十谈》结尾的博客,于是周末继续撸代码,整理这篇用 C++11 实现 22 种高并发模型。
github代码地址:https://github.com/linkxzhou/mylib/tree/master/c%2B%2B/concurrency_server
concurrency_server/
├── base/ # 基础组件目录
│ └── server_base.h # 服务器基类,提供通用的socket操作
├── benchmark/ # 性能测试工具目录
├── main.cpp # 主程序入口,支持多种服务器模型切换
├── Makefile # 编译配置文件
├── README.md # 项目说明文档
│
├── 基础并发模型
├── single_process_server.h # 单进程模型 - 串行处理,适合学习
├── multi_thread_server.h # 多线程模型 - 每连接一线程
├── multi_process_server.h # 多进程模型 - 每连接一进程
├── thread_pool_server.h # 线程池模型 - 固定数量工作线程
├── process_pool1_server.h # 进程池模型1 - 预创建进程池
├── process_pool2_server.h # 进程池模型2 - 改进的进程池实现
│
├── I/O多路复用模型
├── select_server.h # Select模型 - 跨平台I/O多路复用
├── poll_server.h # Poll模型 - 改进的select实现
├── epoll_server.h # Epoll模型 - Linux高性能I/O复用
├── kqueue_server.h # Kqueue模型 - BSD/macOS高性能I/O复用
│
├── 高级并发架构
├── reactor_server.h # Reactor模式 - 事件驱动架构
├── proactor_server.h # Proactor模式 - 异步I/O架构
├── event_loop_server.h # 事件循环模型 - 单线程事件驱动
├── half_sync_async_server.h # 半同步半异步模式 - 分层处理
├── leader_follower_server.h # Leader-Follower模式 - 动态角色切换
├── producer_consumer_server.h # 生产者消费者模式 - 解耦处理
├── pipeline_server.h # 管道模式 - 流水线处理
├── work_stealing_server.h # 工作窃取模式 - 负载均衡
├── hybrid_server.h # 混合模式 - 多种技术结合
│
├── 现代并发技术
├── coroutine_server.h # 协程模型 - 用户态轻量级线程
├── fiber_server.h # 纤程模型 - 协作式多任务
├── actor_server.h # Actor模型 - 消息传递并发single_process_server.h详细介绍: 单进程模型是最简单的服务器架构,采用传统的阻塞I/O方式处理客户端请求。 服务器在单个进程中运行,使用一个主循环来依次处理每个客户端连接。 当有新连接到达时,服务器调用accept()接受连接,然后同步读取客户端数据、处理请求并发送响应。整个过程是串行的,一次只能处理一个客户端。
实现架构:
multi_process_server.h详细介绍: 多进程模型通过为每个客户端连接创建独立的子进程来实现并发处理。 主进程负责监听新连接,当accept()接受到新客户端时,立即调用fork()创建子进程来处理该连接,而主进程继续监听下一个连接。 每个子进程拥有独立的内存空间和资源,可以并行处理不同的客户端请求。 这种模型提供了最强的隔离性,一个进程的崩溃不会影响其他进程或主服务器。
实现架构:
multi_thread_server.h详细介绍: 多线程模型通过为每个客户端连接创建独立线程来实现并发处理。 主线程负责监听和接受新连接,当有新客户端连接时,创建一个工作线程来处理该连接的所有I/O操作。 所有线程共享同一个进程的内存空间,可以方便地共享数据和资源。 相比多进程模型,线程的创建和切换开销更小,内存使用更高效。但需要特别注意线程安全问题,避免竞态条件和数据竞争。
实现架构:
process_pool1_server.hprocess_pool2_server.hthread_pool_server.h详细介绍: 线程池模型通过预先创建固定数量的工作线程来处理客户端请求,避免了为每个连接动态创建线程的开销。 主线程负责接受新连接并将连接放入任务队列,工作线程从队列中取出任务进行处理。 这种模型有效控制了系统资源使用,避免了线程数量无限增长导致的系统崩溃。 线程池的大小通常根据CPU核心数和预期负载来设定,实现了更好的资源管理和性能优化。
实现架构:
leader_follower_server.h详细介绍: 领导者/跟随者模型是一种高性能的并发模式,通过动态角色切换来优化线程利用率。 在任何时刻,只有一个线程担任领导者角色,负责监听和接受新的连接或事件,其他线程处于跟随者状态等待被激活。 当领导者接收到事件后,它会将自己降级为跟随者去处理该事件,同时从跟随者中选举出新的领导者继续监听。 这种模型避免了传统模型中的线程池调度开销,减少了线程间的竞争,提高了CPU缓存的局部性。特别适合高并发、低延迟的网络服务场景。
实现架构:
select_server.h详细介绍: Select模型是最经典的I/O多路复用技术,通过select()系统调用在单线程中同时监控多个socket的状态变化。 服务器维护读、写、异常三个文件描述符集合,select()会阻塞等待直到至少一个描述符就绪。 当select()返回时,服务器遍历描述符集合,处理所有就绪的I/O操作。 这种模型避免了多线程的复杂性,用单线程就能处理多个并发连接,是事件驱动编程的基础。
实现架构:
poll_server.h详细介绍: Poll模型是select模型的改进版本,使用poll()系统调用来监控多个文件描述符的I/O事件。 与select不同,poll使用pollfd结构数组来描述要监控的文件描述符和事件类型,没有FD_SETSIZE的限制,可以监控任意数量的描述符。 poll()返回时,通过检查每个pollfd结构的revents字段来确定哪些描述符就绪。这种模型保持了select的单线程优势,同时解决了描述符数量限制问题。
实现架构:
epoll_server.h详细介绍: Epoll是Linux内核提供的高性能I/O多路复用机制,专门为解决C10K问题而设计。 与select/poll不同,epoll使用事件驱动的方式,只返回就绪的文件描述符,避免了线性扫描。 Epoll内部使用红黑树管理文件描述符,使用就绪列表存储活跃事件,实现了O(1)的事件通知效率。 支持水平触发(LT)和边缘触发(ET)两种模式,为高性能服务器提供了极大的灵活性。
实现架构:
kqueue_server.h详细介绍: Kqueue是FreeBSD和macOS系统提供的高性能事件通知机制,类似于Linux的epoll但功能更强大。 Kqueue不仅支持网络I/O事件,还支持文件系统变化、信号、定时器等多种事件类型。通过kevent()系统调用统一管理所有事件,提供了一致的编程接口。 Kqueue使用内核事件队列,只通知发生变化的事件,避免了轮询开销。 其设计哲学是提供统一的事件处理框架,让应用程序能够高效地响应各种系统事件。
实现架构:
reactor_server.h详细介绍: Reactor模式是一种事件驱动的设计模式,将事件检测和事件处理分离,提供了高度可扩展的架构。 该模式定义了一个事件循环,负责监听各种I/O事件,当事件发生时分发给相应的事件处理器。 Reactor模式的核心思想是"不要调用我们,我们会调用你",应用程序注册事件处理器,由Reactor负责在适当时机调用。 这种模式广泛应用于网络编程框架,如Java NIO、Node.js等,提供了优雅的异步编程模型。
实现架构:
coroutine_server.h详细介绍: 协程模式通过状态机模拟协程行为,在C++11环境下实现异步编程。与传统的回调方式不同,协程允许函数在执行过程中暂停并在稍后恢复,使异步代码看起来像同步代码。 本实现使用状态机来跟踪每个连接的处理状态,当遇到会阻塞的I/O操作时,协程会yield让出控制权,等待I/O就绪后再resume继续执行。 这种模型特别适合处理大量并发连接,因为协程的内存开销远小于线程,可以创建成千上万个协程而不会耗尽系统资源。
实现架构:
actor_server.h详细介绍: Actor模型是一种基于消息传递的并发计算模型,每个Actor都是独立的计算单元,拥有自己的状态和行为。 Actor之间不共享内存,只能通过异步消息进行通信。 当Actor接收到消息时,可以执行三种操作:处理消息并更新内部状态、向其他Actor发送消息、创建新的Actor。 这种模型天然避免了传统并发编程中的锁和竞态条件问题,提供了更安全的并发处理方式。 Actor模型特别适合构建分布式系统,因为Actor可以分布在不同的机器上,通过网络进行消息传递。
实现架构:
event_loop_server.h详细介绍: 事件循环模型是一种单线程异步编程模式,通过一个无限循环来处理所有的I/O事件和回调函数。 事件循环不断地检查事件队列,当有事件就绪时执行相应的回调函数。 这种模型的核心思想是将所有阻塞操作转换为非阻塞的异步操作,通过事件通知机制来处理I/O完成。 事件循环模型广泛应用于Node.js、Redis等高性能服务器中,特别适合I/O密集型应用。 由于采用单线程设计,避免了多线程编程中的锁和同步问题,大大简化了编程复杂度。
实现架构:
fiber_server.h详细介绍: 纤程(Fiber)是一种用户态的轻量级线程,也称为绿色线程或协作式线程。 与操作系统线程不同,纤程的创建、销毁和调度都在用户空间完成,不需要内核参与。 纤程之间采用协作式调度,只有当纤程主动让出控制权时才会发生切换,这避免了抢占式调度的开销和复杂性。 每个纤程只需要很少的内存(通常几KB的栈空间),因此可以创建数十万个纤程而不会耗尽系统资源。 纤程特别适合I/O密集型应用,当遇到阻塞操作时可以快速切换到其他纤程继续执行。
实现架构:
work_stealing_server.h详细介绍: 工作窃取模型是一种动态负载均衡的并行计算模式,每个工作线程维护自己的任务队列,当线程完成自己队列中的任务后,会尝试从其他线程的队列中"窃取"任务来执行。 这种模型能够自动适应任务执行时间的不均匀性,避免某些线程空闲而其他线程过载的情况。 工作窃取算法最初由Cilk项目提出,后来被广泛应用于Java的ForkJoinPool、Intel TBB等并行计算框架中。 该模型特别适合处理递归分治算法和任务执行时间差异较大的场景。
实现架构:
producer_consumer_server.h详细介绍: 生产者-消费者模型是一种经典的并发设计模式,通过缓冲区将数据的生产和消费过程解耦。 生产者负责生成数据并放入缓冲区,消费者从缓冲区取出数据进行处理。 这种模型特别适合处理生产和消费速度不匹配的场景,缓冲区起到了削峰填谷的作用。 在网络服务器中,可以将接收连接作为生产过程,处理请求作为消费过程,通过任务队列进行解耦。 这种模型提高了系统的吞吐量和响应性,同时简化了系统设计。
实现架构:
half_sync_async_server.h详细介绍: 半同步/半异步模型是一种混合架构模式,将系统分为同步处理层和异步处理层,结合两种模式的优势。 异步层负责高效的I/O处理,使用事件驱动的方式处理网络事件;同步层负责业务逻辑处理,使用传统的同步编程模型。 两层之间通过队列进行通信,异步层将接收到的请求放入队列,同步层的工作线程从队列中取出请求进行处理。 这种模型既保证了I/O处理的高效性,又保持了业务逻辑的简洁性,是实际项目中常用的架构模式。
实现架构:
proactor_server.h详细介绍: Proactor模式是一种基于异步I/O的设计模式,与Reactor模式相对应。 在Reactor模式中,应用程序在I/O就绪时被通知并自己执行I/O操作,而在Proactor模式中,应用程序发起异步I/O操作,操作系统完成I/O后通知应用程序处理结果。 这种模式真正实现了I/O操作的异步化,应用程序无需阻塞等待I/O完成,可以继续处理其他任务。 Proactor模式特别适合I/O密集型应用,能够充分利用系统资源,提供更高的并发性能。
实现架构:
pipeline_server.h详细介绍: 管道模型将请求处理过程分解为多个连续的阶段,每个阶段由专门的线程或线程池负责,形成流水线式的处理架构。 请求按顺序通过各个阶段,每个阶段专注于特定的处理任务,如解析、验证、业务逻辑、响应生成等。 这种模型类似于工厂的流水线生产,能够显著提高系统的吞吐量,因为多个请求可以同时在不同阶段并行处理。 管道模型特别适合处理步骤固定、可以分解的复杂业务流程,在数据处理、图像处理、编译器等领域应用广泛。
实现架构:
hybrid_server.h详细介绍: 混合模型是一种综合性的并发架构,根据不同的业务需求和性能要求,在同一个系统中组合使用多种并发模型,可以在I/O处理层使用Reactor模型实现高效的事件处理,在业务逻辑层使用线程池模型保证处理能力,在数据访问层使用异步I/O模型提高数据库访问效率。 这种模型允许开发者针对系统的不同部分选择最适合的并发策略,从而在整体上达到最优的性能表现。 混合模型在大型企业级应用、微服务架构、分布式系统中应用广泛,是现代高性能服务器的主流架构选择。
实现架构:
make
# 基本用法
./concurrency_server <model> [port]
# 示例
./concurrency_server thread_pool 8080
./concurrency_server epoll 9000
./concurrency_server reactor 8888
通过 https://github.com/linkxzhou/http_bench 工具对部分 server 进行性能对比如下(开启日志):
模型名称 | QPS |
|---|---|
single_process_server | 19075.400 |
multi_process_server | 6904.800 |
multi_thread_server | 18137.834 |
process_pool1_server | 18337.299 |
process_pool2_server | 18589.268 |
thread_pool_server | 18483.166 |
leader_follower_server | 20180.701 |
poll_server | 18817.867 |
kqueue_server | 19096.133 |
reactor_server | 18945.467 |
event_loop_server | 19442.801 |
work_stealing_server | 8903.566 |
actor_server | 18681.500 |
fiber_server | 18994.268 |
producer_consumer_server | 18208.633 |
proactor_server | 18130.533 |