CentOS6.9下C++ epoll服务器SSL_accept/SSL_do_handshake性能缓慢求助
针对高并发SSL握手场景的性能优化方案
我之前在CentOS 6.9 + OpenSSL 1.0.2的环境下做过高并发SSL服务器的优化,碰到过几乎一模一样的50k连接场景下的握手性能瓶颈,给你整理几个实际落地有效的优化方向:
1. 优化OpenSSL会话缓存与复用策略
SSL握手的最大开销之一就是会话密钥协商,复用已有的会话能直接跳过大部分握手步骤:
- 启用会话复用:在初始化
SSL_CTX时设置缓存模式,避免自动清理会话:SSL_CTX_set_session_cache_mode(ctx, SSL_SESS_CACHE_SERVER | SSL_SESS_CACHE_NO_AUTO_CLEAR); - 调整缓存容量:根据你的50k连接规模,把缓存调大到足够容纳活跃会话:
SSL_CTX_sess_set_cache_size(ctx, 100000); // 可以根据实际连接数调整 - 若内存紧张,可尝试自定义会话缓存回调(
SSL_CTX_sess_set_get_cb/SSL_CTX_sess_set_new_cb),把会话存到共享内存里,不过优先先试内置缓存优化,成本更低。
2. 改用非阻塞式SSL握手流程
你现在可能是在EPOLL_CTL_ADD后直接阻塞调用SSL_accept/SSL_do_handshake,这会导致事件循环被卡住,无法同时处理其他连接。改成非阻塞模式:
- 先将socket设置为非阻塞:
int flags = fcntl(sockfd, F_GETFL, 0); fcntl(sockfd, F_SETFL, flags | O_NONBLOCK); - 调用
SSL_accept时,若返回SSL_ERROR_WANT_READ或SSL_ERROR_WANT_WRITE,就把对应的读写事件注册到epoll,等事件触发后再继续握手。这样整个事件循环不会被单个握手操作阻塞,能同时处理大量连接。 - 可以给新连接的epoll事件加上
EPOLLONESHOT,避免重复触发事件,减少不必要的上下文切换。
3. 调整系统内核参数,释放并发能力
CentOS 6.9的默认参数不足以支撑50k级别的并发连接,先把系统层面的限制解开:
- 提升文件描述符限制:
# 临时生效 ulimit -n 100000 # 永久生效,修改/etc/security/limits.conf,添加两行: # * soft nofile 100000 # * hard nofile 100000 - 调整TCP内核参数(修改
/etc/sysctl.conf后执行sysctl -p生效):net.core.somaxconn = 65535 net.ipv4.tcp_max_syn_backlog = 65535 net.ipv4.tcp_syncookies = 1 net.core.netdev_max_backlog = 65535
这些参数能让系统同时处理更多的连接请求,避免因为队列满导致的握手延迟。
4. 启用OpenSSL硬件加速与编译优化
OpenSSL 1.0.2支持CPU的AES-NI指令集,能大幅降低加密解密的CPU开销:
- 先检查CPU是否支持AES-NI:
有输出说明支持。cat /proc/cpuinfo | grep aes - 重新编译OpenSSL,启用硬件加速和最高级别编译优化:
./config --prefix=/usr/local/openssl enable-ec_nistp_64_gcc_128 enable-aesni enable-asm -O3 make && make install
CentOS 6.9自带的OpenSSL通常没有启用这些优化,自定义编译后握手速度能提升30%-50%。
5. 拆分SSL握手与业务处理的负载
SSL握手是CPU密集型操作,单进程处理50k握手容易把CPU打满:
- 可以采用主进程+多子进程架构:主进程负责监听并接受连接,把socket传递给子进程处理SSL握手,子进程完成握手后再把连接交给业务线程。这样能把握手负载分散到多个CPU核心上。
- 进程间传递socket用
sendmsg,这是最高效的方式,避免拷贝数据。
6. 选择更高效的SSL协议与加密套件
过时的协议和重加密套件会拖慢握手速度:
- 只启用TLSv1.2(OpenSSL 1.0.2支持),禁用旧协议:
SSL_CTX_set_options(ctx, SSL_OP_NO_SSLv3 | SSL_OP_NO_TLSv1 | SSL_OP_NO_TLSv1_1); - 选择轻量级的加密套件,比如ECDHE密钥交换的GCM套件,比传统RSA套件握手快很多:
SSL_CTX_set_cipher_list(ctx, "ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256");
最后建议用perf工具分析CPU热点,确认瓶颈是否真的在SSL操作上,比如:
perf top -p <你的服务器进程PID>
如果看到SSL_accept、SSL_do_handshake占比很高,就针对性优化上面的方向;如果是系统调用或epoll本身的问题,再调整事件循环逻辑。
内容的提问来源于stack exchange,提问作者M. Mike




