











如 envoy源码分析(八)--main线程专项分析 的分析,主线程有如下几部分功能:
1 xds消息处理
2 主动健康检查
3 admin接口请求处理
4 state信息周期合并
5 state sink 插件调用。
随着配置量增长,主动健康检查 与 admin 接口请求, 将成为主线程的性能瓶颈。导致CPU满载。
xds 消息可以配置成ads的增量模式,不对导致CPU瓶颈。
state的周期是全局可调的,另外 state sink 有 dog-stated 的 UDP 插件,可以将统计信息外送至stated统一处理。
总结有4个瓶颈
1 accpt时 alloc fd的锁
2 中断与worker分开后,tcp_push_pending_frames的锁
这个不是真的锁住了,中断在超线程上会有cache争抢问题,导致锁原子操作被放大。绑独立核心的时候没有这个问题。
3 libevent 启用多线程时的 mutex
4 libevnet 全局变量 event_debug_mode_on_ / event_debug_mode_too_late cache miss
如 envoy源码分析(十)--事件机制专项分析 的分析, libevent 的 多线程锁会成为瓶颈。
实测在32个CPU后,性能增长趋于平缓。
解法也比较简单直接。分成多进程,利用内核的port reuse,实现多进程无差别服务,
不需要做额外的角色管理,只需要同时订阅一份xds配置即可。
还有另一个解法,如 envoy源码分析(十)--事件机制专项分析 的分析
可以使用 io_uring
perf top 查看热点是 libevent 的锁:

使用bpftrace,找到热点锁的调用位置
/* * 追踪 pthread_mutex_lock 的等待耗时,并按锁地址和调用栈分组 */ BEGIN { printf("正在追踪 Envoy 锁竞争... 按 Ctrl-C 结束并查看结果\n"); } uprobe:libc:pthread_mutex_lock { @start[tid] = nsecs; @lock_addr[tid] = arg0; // 记录锁对象的内存地址 } uretprobe:libc:pthread_mutex_lock /@start[tid]/ { $duration = nsecs - @start[tid]; $lock = @lock_addr[tid]; // 只记录耗时超过 1000 纳秒(1微秒)的竞争,过滤掉极快的无竞争加锁 if ($duration > 1000) { @total_wait_ns[$lock] = sum($duration); // 该地址锁的总等待时间 @max_wait_ns[$lock] = max($duration); // 该地址锁的最大单次等待 @contention_stacks[ustack, $lock] = count(); // 导致竞争的调用栈 } delete(@start[tid]); delete(@lock_addr[tid]); } END { printf("\n--- 各个锁地址的总等待时长 (ns) ---\n"); // 结果会按总耗时排序输出 }
输出结果:
排名第一的函数,event_process_active_single_queue(),比第二名多了一个数量级

代码:
https://github.com/libevent/libevent/blob/release-2.1.12-stable/event.c#L1727
分析主要原因:
libevent的事件循环累计锁的原子操作,导致的 耗时操作的高频访问,引起的累计效果。

为什么线程数增多,会变的越来越严重?
猜测可能是缓存一致性问题引起。
函数 pthread_mutex_unlock_usrcnt

函数 event_del_nolock

event_debug_note_del_
全局变量:event_debug_mode_on_ / event_debug_mode_too_late = 1;
函数 event_add_nolock

event_debug_note_setup_
全局变量:event_debug_mode_on_ / event_debug_mode_too_late = 1;
修改设置全局变量cache line 对齐:
envoy/build_root/bazel_root/base/external/com_github_libevent_libevent::event.c


用 bpftrace 查看锁是否被竞争
[root@A06-R08-I208-229-925A008 caotong1]# cat envoy-4.bt #!/usr/bin/env bpftrace #include <linux/sched.h> BEGIN { printf("正在监测锁(Lock/Unlock)访问分布... 采样周期 5s\n"); printf("%-18s | %-16s | %s\n", "MUTEX_ADDR", "OP_TYPE", "THREAD_NAME:COUNT"); printf("-------------------|------------------|--------------------------\n"); } /* 监控加锁 */ uprobe:/lib64/libc.so.6:pthread_mutex_lock, uprobe:/lib64/libc.so.6:pthread_mutex_trylock, { @ops[arg0, "lock", comm] = count(); } uprobe:/lib64/libc.so.6:pthread_mutex_unlock { @ops[arg0, "unlock", comm] = count(); } END { print(@ops); clear(@ops); } [root@A06-R08-I208-229-925A008 caotong1]# #./envoy-4.bt > 1 [root@A06-R08-I208-229-925A008 caotong1]# cat 1 |awk '{print $1}' |sort |uniq -c |tail 4 @ops[8635020759488, 4 @ops[8635020759808, 4 @ops[8635020760128, 4 @ops[8635020760448, 4 @ops[8635020760768, 4 @ops[8635020761088, 4 @ops[8635020761344, 2 @ops[93987636247152, 4 @ops[93987636268544, 1 正在监测锁(Lock/Unlock)访问分布... [root@A06-R08-I208-229-925A008 caotong1]# cat 1 |grep 8635020761344 @ops[8635020761344, lock, envoy]: 144 @ops[8635020761344, unlock, envoy]: 146 @ops[8635020761344, lock, wrk:worker_47]: 746930 @ops[8635020761344, unlock, wrk:worker_47]: 747892 我现在想知道,锁8635020761344,在这些命中时的调用栈
针对锁 8635020761344 查看其调用栈
[root@A06-R08-I208-229-925A008 caotong1]# cat envoy-5.bt #!/usr/bin/env bpftrace /* * 专门针对特定热点锁地址抓取调用栈 * 热点地址: 8635020761344 */ BEGIN { printf("正在追踪锁 8635020761344 的调用栈... (Ctrl+C 结束)\n"); } uprobe:/lib64/libc.so.6:pthread_mutex_lock /arg0 == 8635020761344/ { @[comm, ustack] = count(); } uprobe:/lib64/libc.so.6:pthread_mutex_unlock /arg0 == 8635020761344/ { @[comm, ustack] = count(); } END { print(@); clear(@); }

是 state 的定时刷新在枪锁。
此内容由惯性聚合(RSS阅读器)自动聚合整理,仅供阅读参考。 原文来自 — 版权归原作者所有。