学会了 API 只是第一步,如何将 Libevent 组织成一个高并发、高吞吐的服务器架构才是关键。本篇将介绍几种主流的并发模型。
1. One Loop Per Thread (推荐)
这是目前最流行、性能最好的模型(Nginx, Memcached, Netty 都在用)。
1.1. 架构设计
- 结构: 启动 N 个线程(通常等于 CPU
核数),每个线程运行一个独立的
event_base_loop。 - 无锁: 每个线程只处理分配给它的连接,互不干扰,几乎没有锁竞争。
1.2. 连接分发
如何将客户端连接分配给这些线程?
方案 A: SO_REUSEPORT (内核分发)
Linux 3.9+
支持多个线程绑定到同一个端口(bind 时设置
SO_REUSEPORT)。 * 优点:
极其简单。所有线程都监听同一个端口,内核负责负载均衡(哈希分发)。
* 缺点: 某些老旧系统不支持。
方案 B: 主从 Reactor (应用层分发)
- Master 线程: 只负责
accept新连接。 - 分发: Master 拿到新 socket fd 后,通过 Round-Robin 算法选择一个 Worker 线程。
- 通知: Master 通过管道(Pipe)或 SocketPair 将 fd 传递给 Worker。
- Worker 线程: 从管道读到
fd,将其注册到自己的
event_base中,开始处理业务。
2. 单线程 Reactor + Worker 线程池
如果你的业务逻辑包含大量计算(CPU 密集型)或阻塞操作(如数据库查询),One Loop Per Thread 会导致 Event Loop 阻塞,影响其他连接。
2.1. 架构设计
- IO 线程: 单个(或少量)Libevent 线程只负责网络收发。
- Worker 线程池: 一个标准的线程池(如 Java 的 ThreadPoolExecutor)。
- 流程:
- IO 线程收到数据 -> 封装成 Task -> 投递给 Worker 线程池。
- Worker 线程处理业务 -> 产生结果 -> 通知 IO 线程发送。
2.2. 适用场景
- 业务逻辑复杂,耗时不可控。
- 需要调用阻塞的第三方库。
3. Leader/Follower 模型
这是一种更古老的模型。 * 线程池:
一组线程共享同一个 event_base。 *
竞争: 所有线程都尝试获取
event_base 的锁来运行 epoll_wait。
* Leader: 抢到锁的线程成为
Leader,等待事件。 * Follower:
其他线程休眠。 * 缺点:
锁竞争激烈,且惊群效应(Thundering Herd)难以避免。Libevent
虽然支持多线程共享 base,但并不推荐这种用法。
4. 总结
| 模型 | 适用场景 | 复杂度 | 性能 |
|---|---|---|---|
| One Loop Per Thread | IO 密集型,短业务逻辑 (Redis, Nginx) | 中 | 极高 |
| Reactor + Thread Pool | CPU 密集型,长业务逻辑 (Tomcat 模式) | 高 | 高 |
| Leader/Follower | 特殊场景 | 低 | 中 |
对于大多数基于 Libevent 的 C/C++ 服务,One Loop Per Thread (配合 SO_REUSEPORT) 是首选方案。
上一篇: 04-architecture/threading.md - 线程安全与锁 下一篇: 04-architecture/process-pitfalls.md - 进程模型陷阱