c++项目 网络聊天服务器 实现
源码 https://github.com/DBWGLX/SZU_system_programming
文章目录
- 技术设计
- 编码
- JSON的替换
- Protobuf
- 网络
- 线程池更高效率
- 网络字节序的考虑
- send可能无法一次性发送全部数据!
- EPOLLHUP , EPOLLERR 的正确处理
- IO
- 数据库操作的更高性能
- 开发日志
- 2025.3
- a.粘包问题
- 2025.4
- b.多个线程同时读取同一个文件描述符问题
- 第一次批量注册成功1W用户
- 500qps
- c.数据库连接池没必要问题
- 700qps
- d. 高并发均匀处理问题 [ EPOLLONESHOT ]
技术设计
编码
JSON的替换
JSON 不提供强类型,必须手动检查字段类型。而Protobuf 反序列化时,会自动检查字段类型。
JSON编程时,每个 k 的设置解析都得检查。
json_t *response = json_object(); 对象创建,最后还要释放。
Protobuf
已结合 K-L-V
官方编码文档学习 https://blog.csdn.net/JK01WYX/article/details/146919585?spm=1001.2014.3001.5501
网络
线程池更高效率
网络字节序的考虑
htol
send可能无法一次性发送全部数据!
EPOLLHUP , EPOLLERR 的正确处理
直接看 events 部分:https://blog.csdn.net/JK01WYX/article/details/132699613
IO
数据库操作的更高性能
数据库查询需要时间,可以不去等其操作完
开发日志
2025.3
项目搭好,跑了下,感觉一点问题没有。
a.粘包问题
压测下,注册5W个用户: !json解析错误,粘包了!json是一起解析的,毕竟找 {} 即可。
和同学交流后得知,json报文不是很好的选择,启动protobuf
2025.4
protobuf + KLV 启动
b.多个线程同时读取同一个文件描述符问题
任务队列recv 出问题
接着注册5w个用户
我用一个客户端发起5w份注册请求 —— 只注册了前5个
epoll 收到 IO 只放到任务队列,那同一个socket文件描述符就可能被多个线程同时读取,这是错误的
可以分离 接收数据 和 数据处理,后期接收数据用协程和高性能的io_uring
第一次批量注册成功1W用户
通过减慢发送速度,提升缓冲区 net.core.rmem_max,维护 recv接收完整 (服务器这边是一个线程再处理)
用时 2m40s (第一个创建时间和最后一个创建时间之差)
用的默认 net rmem 缓冲区大小
wyx@ubuntu:~/work/SZU_system_programming/ChatServer$ sysctl net.core.rmem_max
net.core.rmem_max = 212992
wyx@ubuntu:~/work/SZU_system_programming/ChatServer$ sysctl net.core.rmem_default
net.core.rmem_default = 212992
不限制发送方发送速度,程序正常运行
2m18s
提升 net.core.rmem_max 为 16MB,速度提升至 52s
sudo sysctl -w net.core.rmem_max=16777216
sudo sysctl -w net.core.rmem_default=8388608
500qps
现在分出4个客户端,并行请求:(此时服务器4个线程并行处理)
18s => 1e4 / 18 = 555 queries per second!
(插入时分配主键,根据主键id排序的)
c.数据库连接池没必要问题
700qps
数据库连接池的思想是不用频繁创建释放数据库连接。然而多线程从连接池中取连接,也会出现竞态条件
我这里4核4任务线程,不算多,可以给每个线程分配一个连接
14s => 1w注册请求 : 1e4 ➗ 14 = 714 qps !
(top)运行时占用:
客户端发送第一个前的时间:
服务器同一时刻收到注册请求:
d. 高并发均匀处理问题 [ EPOLLONESHOT ]
在 epoll 的边缘触发(Edge Triggered,ET)模式下结合 EPOLLONESHOT 使用时,一个 socket 文件描述符在加入 epoll 实例前收到消息,加入后仍可以收到这次 IO 事件
连接一直连接断开前,recv可以一直阻塞,导致任务线程阻塞,服务器没法处理其他请求了。
所以应设置超时,或者就按任务队列顺序处理。
但是!按照任务队列顺序处理,之前补丁加的set 有漏洞,可能会忽视一些边缘驱动, 应使用 EPOLLONESHOT
19s => 1e4 / 19 = 526 qps
接着就可以写 io_uring 啦!