You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
[replied by brpc-oncall robot] 结合当前 master 的实现补充几点(引用的都是仓库里的实际代码):
1. ED 数量目前只支持全局配置 FLAGS_event_dispatcher_num 是全局 flag(src/bthread/task_control.cpp:60)。每个 tag 会有自己独立的 num 个 ED 实例,但数量对所有 tag 相同:g_edisp 直接按 FLAGS_task_group_ntags * FLAGS_event_dispatcher_num 一次性分配(src/brpc/event_dispatcher.cpp:78),TaskControl 里的 ED 优先队列也按 tag * _ed_priority_queue_num_of_each_tag + index 布局(src/bthread/task_control.h:173-176、src/bthread/task_control.cpp:216-218)。所以“每组独立”指的是每组有自己的实例,不是可以分别配数量,确实没有 per-tag 接口。
2. #1120 的约束已由 #3683 解除
ED 和 bthread 内部 epoll 线程是永不退出的循环,会长期占住承载它的 worker pthread(docs/cn/server.md:598)。#3683 之后它们走独立的“预留并发数”额度:初始化 ED 时按 tag 调用 bthread_add_reserved_concurrency_by_tag(FLAGS_event_dispatcher_num, tag)(src/brpc/event_dispatcher.cpp:60-75),预留 worker 不再从 bthread_concurrency/num_threads 里扣(docs/cn/server.md:600-604、src/bthread/bthread.h:157-188)。因此 event_dispatcher_num >= worker 数 导致所有 worker 卡在 epoll_wait、业务不响应甚至进程退不掉的问题已不存在,对应回归用例见 test/brpc_event_dispatcher_unittest.cpp:621 起。现在的硬约束变成每个 tag 的预留累计不超过 BTHREAD_MAX_RESERVED_CONCURRENCY(512,src/bthread/types.h:159)。注意该修复在 1.17.0 之后,1.17.0 及更早版本没有。
3. ED 数量如何取值,以及副作用
fd 到 ED 的映射是 butil::fmix32(fd) % FLAGS_event_dispatcher_num(src/brpc/event_dispatcher.cpp:98),同一个 ED 上的 fd 串行处理事件,所以调大主要是把热点 fd 打散、提高事件处理/唤醒的并行度,和你实测的结论一致。副作用:
每个 ED 占一个常驻 worker pthread(现在计入预留额度),总线程数变为 普通并发数 + ntags * num (+ 内部 epoll),可用 bvar bthread_worker_count 观察;
每个 ED 有自己的 epoll fd,开启 -enable_bthread_priority_queue 后每个 ED 还有一个 BTHREAD_MAX_CONCURRENCY(1024)容量的 steal queue(src/bthread/task_control.cpp:225-241);
4. signal_task 里的 2
这是硬编码上限,src/bthread/task_control.cpp:718-720,注释说明是“唤醒足够 worker”和“避免大量无用 signal”之间的折中,目前没有 flag 可调。调用时它会在该 tag 的 parking lot 之间轮转(src/bthread/task_control.cpp:722-731),parking lot 数量由 -bthread_parking_lot_of_each_tag 控制(默认 4,src/bthread/bthread.cpp:65)。所以一次 flush 最多唤醒 2 个 worker 确实是当前实现的上限。想绕开这条路径,可以考虑 usercode_in_coroutine(docs/cn/coroutine.md:112,此时实际处理能力由 ED 数量决定,不再依赖 worker 唤醒),或让事件处理尽量在 ED 上就地完成,减少串行入队再唤醒的环节。
5. per-tag 配置的落地方式
实现上可行(ED 本身是按 tag 独立启动的),但要同时把 TaskControl 的 _ed_priority_queues 改为按 tag 变长布局(现在假定每组数量相同),且必须在 InitializeGlobalDispatchers() 的 pthread_once 之前生效(src/brpc/event_dispatcher.cpp:49、GetGlobalEventDispatcher),运行时改 gflag 是无效的。接口形态上倾向新增 API 按 tag 设置,而不是把 flag 改成字符串再解析。如果你确认这个需求,我可以整理成开发任务推进。
Activity
yanglimingcn commentedon Jan 12, 2026
其实每个tag下的event_dispatch数量是可以不同的,只是现在都是通过FLAGS_event_dispatcher_num这个flags来设置的,为了保持兼容性,所以就不太好修改,目前也没想到提供怎么样API用着更合理,你有什么好的建议吗?
MJY-HUST commentedon Jan 14, 2026
如果仍然还是需要通过 gflags 初始化,不支持动态修改,目前只想到下面两种方式
yanglimingcn commentedon Mar 11, 2026
现在组worker里面的epoll数量应该会很大吗?都设置成你希望的比较大的,把每组的worker调整的稍多一些,是不是也满足需求了。
Lfengjie commentedon May 15, 2026
请问一下你用的那个版本的brpc,我也遇到类似的问题,但是调大 event dispatcher 的个数,client端会报连接错误的错,server端过了一会儿直接就core了
chenBright commentedon Oct 30, 2026
@MJY-HUST #3683 已经解决了 #1120 的问题了。
wwbmmm commentedon Oct 31, 2026
[replied by brpc-oncall robot] 结合当前 master 的实现补充几点(引用的都是仓库里的实际代码):
1. ED 数量目前只支持全局配置
FLAGS_event_dispatcher_num是全局 flag(src/bthread/task_control.cpp:60)。每个 tag 会有自己独立的 num 个 ED 实例,但数量对所有 tag 相同:g_edisp直接按FLAGS_task_group_ntags * FLAGS_event_dispatcher_num一次性分配(src/brpc/event_dispatcher.cpp:78),TaskControl 里的 ED 优先队列也按tag * _ed_priority_queue_num_of_each_tag + index布局(src/bthread/task_control.h:173-176、src/bthread/task_control.cpp:216-218)。所以“每组独立”指的是每组有自己的实例,不是可以分别配数量,确实没有 per-tag 接口。2. #1120 的约束已由 #3683 解除
ED 和 bthread 内部 epoll 线程是永不退出的循环,会长期占住承载它的 worker pthread(
docs/cn/server.md:598)。#3683 之后它们走独立的“预留并发数”额度:初始化 ED 时按 tag 调用bthread_add_reserved_concurrency_by_tag(FLAGS_event_dispatcher_num, tag)(src/brpc/event_dispatcher.cpp:60-75),预留 worker 不再从bthread_concurrency/num_threads里扣(docs/cn/server.md:600-604、src/bthread/bthread.h:157-188)。因此event_dispatcher_num >= worker 数导致所有 worker 卡在 epoll_wait、业务不响应甚至进程退不掉的问题已不存在,对应回归用例见test/brpc_event_dispatcher_unittest.cpp:621起。现在的硬约束变成每个 tag 的预留累计不超过BTHREAD_MAX_RESERVED_CONCURRENCY(512,src/bthread/types.h:159)。注意该修复在 1.17.0 之后,1.17.0 及更早版本没有。3. ED 数量如何取值,以及副作用
fd 到 ED 的映射是
butil::fmix32(fd) % FLAGS_event_dispatcher_num(src/brpc/event_dispatcher.cpp:98),同一个 ED 上的 fd 串行处理事件,所以调大主要是把热点 fd 打散、提高事件处理/唤醒的并行度,和你实测的结论一致。副作用:普通并发数 + ntags * num (+ 内部 epoll),可用 bvarbthread_worker_count观察;-enable_bthread_priority_queue后每个 ED 还有一个BTHREAD_MAX_CONCURRENCY(1024)容量的 steal queue(src/bthread/task_control.cpp:225-241);建议从 1 开始按 IO 密集度往上加,数量级控制在不超过可用核数,用 bvar
event_dispatcher_read/event_dispatcher_write(src/brpc/event_dispatcher.cpp:76-77注册)配合长尾指标来定,没有通用的推荐值。4. signal_task 里的 2
这是硬编码上限,
src/bthread/task_control.cpp:718-720,注释说明是“唤醒足够 worker”和“避免大量无用 signal”之间的折中,目前没有 flag 可调。调用时它会在该 tag 的 parking lot 之间轮转(src/bthread/task_control.cpp:722-731),parking lot 数量由-bthread_parking_lot_of_each_tag控制(默认 4,src/bthread/bthread.cpp:65)。所以一次 flush 最多唤醒 2 个 worker 确实是当前实现的上限。想绕开这条路径,可以考虑usercode_in_coroutine(docs/cn/coroutine.md:112,此时实际处理能力由 ED 数量决定,不再依赖 worker 唤醒),或让事件处理尽量在 ED 上就地完成,减少串行入队再唤醒的环节。5. per-tag 配置的落地方式
实现上可行(ED 本身是按 tag 独立启动的),但要同时把 TaskControl 的
_ed_priority_queues改为按 tag 变长布局(现在假定每组数量相同),且必须在InitializeGlobalDispatchers()的pthread_once之前生效(src/brpc/event_dispatcher.cpp:49、GetGlobalEventDispatcher),运行时改 gflag 是无效的。接口形态上倾向新增 API 按 tag 设置,而不是把 flag 改成字符串再解析。如果你确认这个需求,我可以整理成开发任务推进。关于 @Lfengjie 的报错:需要 brpc 版本(tag/commit)、启动参数(
event_dispatcher_num、task_group_ntags、bthread_concurrency/num_threads)、core 栈,以及 client 端“连接错误”的具体错误码和日志。1.17.0 及更早版本没有 #3683 的预留逻辑,event_dispatcher_num逼近或超过 worker 数时正好会触发上面说的所有 worker 卡在 epoll_wait 的问题,建议先换到包含 #3683 的代码后复测。