-
Notifications
You must be signed in to change notification settings - Fork 892
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
braft+brpc长时间大压力时的死锁问题求解决方法 #219
Comments
从栈上看是因为没有足够的线程运行bthread,导致了 bthread 的堆积,这个得找为什么堆积了 |
pthread mutex 里有线程 id的字段,可以看看持有锁的线程在做什么 |
另外,参考一下这个问题:#139 |
这样的。有挺多的栈,分成了两种,1类型的栈的pthread mutex里的owner是2类型的栈的线程所持有的,在我的问题中我标注了一下类型。 |
本质上是这个问题:apache/brpc#473 。可以先切换成 bthread mutex 试试 ,参见 macros.h: //#define USE_BTHREAD_MUTEX 使用 bthread mutex 后,类型 1 的栈会在类型 2 后排队,可以让用于执行 bthread 的线程更多,不造成堆积的话,也就不会有这种死锁 case |
多谢解答! 所以请问这里还需要做一些什么别的改动吗? |
我看下 |
我本地可以编译过,你们改代码了?butil和bthread下各有一个ConditionVariable,用bthread命名空间下的就可以了 |
多谢,我们是有做一些代码的修改,现在已经能够编译通过,并用bthread_mutex代替pthread_mutex做了一下验证,发现仍然有可能导致死锁的问题。 |
关键还是看为什么请求会堆积。可以看下 bthread usage,在brpc的bvar页面里能找到,如果这个usage和cpu usage能对上,说明是cpu瓶颈了,处理能力已经超出单机的能力,需要考虑做限流。如果usage远低于cpu usage,说明代码内阻塞的操作比较多,这时候需要找耗时比较长的地方,可能是磁盘操作或者其他类型的阻塞,具体的得靠抓栈和一些监控来分析,调大max_concurrency可以缓解情况,不过还是得先找到根因。 |
另外,brpc内置了一个contention分析的页面,可以分析锁冲突的耗时。到了rq is full发生的时候,现场已经无法恢复了,得往前找情况开始恶化的时间段 |
流控可以依赖pending的任务数量(自己在代码里用bvar统计,或者检查status页面rpc请求的processing的统计)来做,这里的case,应该会有一个不断增加的过程 |
多谢 @PFZheng 我们在考虑流控的事情,但我这里有一个小疑问,现在我们用的是bthread mutex了,但仍然死锁,查看死锁的线程栈,发现有如下几种: 1 `Thread 113 (Thread 0x7f7117fff700 (LWP 30422)): #0 0x00007f840ece6419 in syscall () from /lib64/libc.so.6 2 #0 0x00007f840ece6419 in syscall () from /lib64/libc.so.6 3 `Thread 78 (Thread 0x7f708cdfa700 (LWP 30457)): #0 0x00007f840ece6419 in syscall () from /lib64/libc.so.6 这些栈的最后几帧都是一样的结果,看起来都是在去拿bthread mutex。我们的问题是:协程锁的话理应不block线程才对,也就是说通过线程栈应该看不见或者很少看见去拿协程锁才对,因为如果拿协程锁的话应该就会被换出排队去了。但是这里的线程栈里却都是这样的去拿协程锁block的。我们的max_concurrency是0,我理解实际效果是无限大的。 想请教一下这里是否符合预期? |
你们是开启了 usercode_in_pthread或者混用了其他的线程池? futex_wait_private 这个说明是在普通pthread线程里持有了锁,这个时候butex会退化成futex。如果是混用了其他的线程池,可以主动发起一个bthread去执行操作,切换到bthread线程池里 |
braft::SegmentLogStorage::get_segment (this=0x7f6f7d45af70, group_id=12638, index=62666) 看起来根源是这个锁,这里有做改动吗? 这儿有个嵌套。replicator 的 bthread id lock 里会去拿 Segment 的锁。Node 里有周期性的定时器会拿 replicator的 bthread id lock(获取 last_rpc_send_timestamp),Segment 锁住会造成整个 node 的锁竞争。这个地方目前在内部版本已经改了,还没同步过来。不过这个锁竞争仍然是 butex 的,不应该占住线程,主要还是看 get_segment 为什么会锁住。 |
我看了下,braft里面唯一让用户去控制usercode_in_pthread的地方就是在创建node的时候init()函数传入的option,这个option我们没有去设置usercode_in_pthread这个值,我看默认值是false的。所以我们应该没有开启usercode_in_pthread。 |
_apply_queue->execute(m, &bthread::TASK_OPTIONS_INPLACE, NULL) 这儿改一下,第二个参数改成 bthread::TASK_OPTIONS_NORMAL。原来的参数意思是原地执行 execution queue,减少一次线程切换开销,这里看起来是从你们自己的线程里发起的,这样执行就没在 bthread 里。 |
我们对写日志这块有一些优化,是改过code的 |
我试下这个修改 |
按照这个修改了,还是会hang住,但是和之前不一样的是,现在hang住之后在pthread stack里面没有发现之前的来自braft的接口调用apply()了。现在hang住的时候33个worker里有这样的几种,其中第二种占大部分,第一种只有一个,第5种也只有一个 #0 0x00007fd48abc4499 in syscall () from /lib64/libc.so.6 2 #0 0x00007fd48abc4499 in syscall () from /lib64/libc.so.6 3 #0 0x00007fd48abc4499 in syscall () from /lib64/libc.so.6 4 #0 0x00007fd48abc4499 in syscall () from /lib64/libc.so.6 5 #0 0x00007fd48ab8bcbd in nanosleep () from /lib64/libc.so.6 看起来还是会有futex_wait_private。。。这还需要做什么修改么?现在看起来所有的栈都在内部了,但看起来都是从task_runner调度出来的栈。 |
@PFZheng 大佬有v没,我也在BJ,我们比较着急,可以当面聊聊么 |
brpc 源码里,只有这两种情况是 butex_wait_from_pthread,看栈是 is_current_pthread_task() 这个判断的概率比较大。这儿不太科学,理论上 bthread stack 这里应该是 false,你们应该没动过 bthread 调度部分的代码吧?这儿需要一个确认一下在 bthread TaskGroup::task_runner 里面发生了什么,看起来是原地执行了 bthread 函数 |
你往我的邮箱发一个邮件吧,告知一下微信号,我加一下你: [email protected] |
我看你之前贴的栈的栈顶还是正常的,这个是正常的 bthread 协程栈: 现在贴的栈里这种是不正常的,worker_thread 这个是调度器的栈,不应该在这里直接执行: |
这个锁想问下内部版本是怎么改的,优化了吗 |
比较简单,把 last_rpc_send_timestamp 拆出来了,需要的话这周单独拆个patch同步过来。github上的版本和内部版本代码上是一致的,只不过是内部测试比较稳定后才会同步过来,这个地方是刚改没多久 |
可以发出来我们看看吗,这个get_segment函数调用比较频繁,想看看性能优化效果 |
好的。不过对 get_segment 整体估计无太大收益,get_segment主要是很多地方需要获取 term 等信息。 |
我们用braft+brpc来做强一致性解决方案,目前在压力大持续时间长时,会导致死锁,通过gdb可以直接bt看到的栈分为两类。
类型1的线程在等类型2的线程中的锁,而2在执行过程中拿了1所需要的锁,在试图往rq中push时由于_rq是满的会失败然后usleep。而_rq里的task pop出来执行很可能也是log_manager的相关操作,也需要拿同一把锁。这样就形成了循环,死锁
在这里log_manager中的锁有可能进行逻辑上的拆分,从而使得从bthread里push进入时需要拿的锁和pop出来时调用的函数拿锁使用不同的锁吗?
请教一下这里的解决思路 ~~
类型1
`#0 0x00007f9edd212334 in __lll_lock_wait () from /lib64/libpthread.so.0
#1 0x00007f9edd20d5d8 in _L_lock_854 () from /lib64/libpthread.so.0
#2 0x00007f9edd20d4a7 in pthread_mutex_lock () from /lib64/libpthread.so.0
#3 0x0000000000df637b in pthread_mutex_lock_impl (__mutex=0x7f89f75ff4e8) at xxx/deps/incubator-brpc/src/bthread/mutex.cpp:555
#4 pthread_mutex_lock (__mutex=0x7f89f75ff4e8) at xxx/deps/incubator-brpc/src/bthread/mutex.cpp:813
#5 0x0000000000d14ca0 in lock (this=0x7f89f75ff4b0, index=14541) at /usr/include/butil/synchronization/lock.h:69
#6 lock (this=0x7f89f75ff4b0, index=14541) at /usr/include/c++/4.9.2/mutex:474
#7 unique_lock (this=0x7f89f75ff4b0, index=14541) at /usr/include/c++/4.9.2/mutex:406
#8 braft::LogManager::get_term (this=0x7f89f75ff4b0, index=14541) at xxx/src/braft/log_manager.cpp:787
#9 0x0000000000d2ad2d in braft::Replicator::_fill_common_fields (this=0x7f86cc92c460, request=0x7f86a8f9d360, prev_log_index=14541, is_heartbeat=Unhandled dwarf expression opcode 0xf3
) at xxx/src/braft/replicator.cpp:496
#10 0x0000000000d3063c in braft::Replicator::_send_entries (this=0x7f86cc92c460) at xxx/src/braft/replicator.cpp:611
#11 0x0000000000d326bf in braft::Replicator::_on_rpc_returned (id=Unhandled dwarf expression opcode 0xf3
) at xxx/src/braft/replicator.cpp:489
#12 0x0000000000d33b36 in brpc::internal::FunctionClosure5<unsigned long, brpc::Controller*, braft::AppendEntriesRequest*, braft::AppendEntriesResponse*, long>::Run (this=0x7f86d16ad950) at /usr/include/brpc/callback.h:339
#13 0x0000000000e03ef8 in brpc::Controller::EndRPC (this=0x7f86d16ad5d0, info=Unhandled dwarf expression opcode 0xf3
) at xxx/deps/incubator-brpc/src/brpc/controller.cpp:893
#14 0x0000000000e05bc4 in brpc::Controller::OnVersionedRPCReturned (this=0x7f86d16ad5d0, info=..., new_bthread=Unhandled dwarf expression opcode 0xf3
) at xxx/deps/incubator-brpc/src/brpc/controller.cpp:676
#15 0x0000000000e6de9a in OnResponse (msg_base=0x7f897544b140) at xxx/deps/incubator-brpc/src/brpc/details/controller_private_accessor.h:48
#16 brpc::policy::ProcessRpcResponse (msg_base=0x7f897544b140) at xxx/deps/incubator-brpc/src/brpc/policy/baidu_rpc_protocol.cpp:618
#17 0x0000000000e5b9fa in brpc::ProcessInputMessage (void_arg=Unhandled dwarf expression opcode 0xf3
) at xxx/deps/incubator-brpc/src/brpc/input_messenger.cpp:136
#18 0x0000000000decee1 in bthread::TaskGroup::task_runner (skip_remained=Unhandled dwarf expression opcode 0xf3
) at xxx/deps/incubator-brpc/src/bthread/task_group.cpp:297
#19 0x0000000000e3d5b1 in bthread_make_fcontext ()
#20 0x0000000000000000 in ?? ()`
类型2
`#0 0x00007f9edb9c1cbd in nanosleep () from /lib64/libc.so.6
#1 0x00007f9edb9f6f14 in usleep () from /lib64/libc.so.6
#2 0x0000000000deb891 in bthread::TaskGroup::ready_to_run_remote (this=0x7f8b6c0008c0, tid=142391050791690, nosignal=Unhandled dwarf expression opcode 0xf3
) at xxx/deps/incubator-brpc/src/bthread/task_group.cpp:675
#3 0x0000000000dee52a in bthread::TaskGroup::start_background (this=Unhandled dwarf expression opcode 0xf3
) at xxx/deps/incubator-brpc/src/bthread/task_group.cpp:448
#4 0x0000000000df4d6c in start_from_non_worker (tid=0x7f8aed7e9800, attr=0x7f89f7582988, fn=0xddcbd0 bthread::ExecutionQueueBase::_execute_tasks(void*), arg=0x7f8511942ef0)
at xxx/deps/incubator-brpc/src/bthread/bthread.cpp:146
#5 bthread_start_background (tid=0x7f8aed7e9800, attr=0x7f89f7582988, fn=0xddcbd0 bthread::ExecutionQueueBase::_execute_tasks(void*), arg=0x7f8511942ef0)
at xxx/deps/incubator-brpc/src/bthread/bthread.cpp:194
#6 0x0000000000dddb97 in bthread::ExecutionQueueBase::start_execute (this=0x7f89f75828d0, node=0x7f8511942ef0) at xxx/deps/incubator-brpc/src/bthread/execution_queue.cpp:115
#7 0x0000000000d1a898 in execute (id=, task=@0x7f8aed7e98d8, options=0x0, handle=0x0) at /usr/include/bthread/execution_queue_inl.h:318
#8 bthread::execution_queue_executebraft::LogManager::StableClosure* (id=, task=@0x7f8aed7e98d8, options=0x0, handle=0x0) at /usr/include/bthread/execution_queue_inl.h:363
#9 0x0000000000d16251 in execution_queue_executebraft::LogManager::StableClosure* (this=0x7f89f75ff4b0, entries=0x7f8aed7e9a70, done=0x7f84d06daa00) at /usr/include/bthread/execution_queue_inl.h:352
#10 execution_queue_executebraft::LogManager::StableClosure* (this=0x7f89f75ff4b0, entries=0x7f8aed7e9a70, done=0x7f84d06daa00) at /usr/include/bthread/execution_queue_inl.h:345
#11 braft::LogManager::append_entries (this=0x7f89f75ff4b0, entries=0x7f8aed7e9a70, done=0x7f84d06daa00) at xxx/src/braft/log_manager.cpp:485
#12 0x0000000000cf3821 in braft::NodeImpl::apply (this=0x7f89f75feb50, tasks=Unhandled dwarf expression opcode 0xf3
) at xxx/src/braft/node.cpp:1959
#13 0x0000000000cf3b1e in braft::NodeImpl::execute_applying_tasks (meta=0x7f89f75feb50, iter=...) at xxx/src/braft/node.cpp:724
#14 0x0000000000dda82d in bthread::ExecutionQueueBase::_execute (this=0x7f89f75827d0, head=0x7f8572168a90, high_priority=Unhandled dwarf expression opcode 0xf3
) at xxx/deps/incubator-brpc/src/bthread/execution_queue.cpp:273
#15 0x0000000000dddc08 in bthread::ExecutionQueueBase::start_execute (this=0x7f89f75827d0, node=0x7f8572168a90) at xxx/deps/incubator-brpc/src/bthread/execution_queue.cpp:95
#16 0x0000000000c3ba4c in cellar::raft::BucketStateMachine::AsyncApply (this=0x3efba20, op_type=Unhandled dwarf expression opcode 0xf3
) at bucket_state_machine.cpp:87
...
...`
通过gdb_bthread_stack.py拿到的bthread stack基本上(99%)都是:
`#0 0x0000000000dea8a8 in jump_stack (pg=0x0, next_meta=0x5a7f77d934dc6) at xxx/deps/incubator-brpc/src/bthread/stack_inl.h:133
#1 bthread::TaskGroup::sched_to (pg=0x0, next_meta=0x5a7f77d934dc6) at xxx/deps/incubator-brpc/src/bthread/task_group.cpp:605
#2 0x0000000000deadce in sched_to (pg=0x7f894ebeba08) at xxx/deps/incubator-brpc/src/bthread/task_group_inl.h:80
#3 bthread::TaskGroup::sched (pg=0x7f894ebeba08) at xxx/deps/incubator-brpc/src/bthread/task_group.cpp:563
#4 0x0000000000de1cc7 in bthread::butex_wait (arg=0x7f87bceb60c0, expected_value=1, abstime=0x0) at xxx/deps/incubator-brpc/src/bthread/butex.cpp:660
#5 0x0000000000de93a0 in bthread::CountdownEvent::wait (this=0x7f894ebebb48) at xxx/deps/incubator-brpc/src/bthread/countdown_event.cpp:65
#6 0x0000000000d13991 in wait (this=Unhandled dwarf expression opcode 0xf3
) at xxx/src/braft/log_manager.cpp:164
#7 braft::LogManager::last_log_id (this=Unhandled dwarf expression opcode 0xf3
) at xxx/src/braft/log_manager.cpp:201
#8 0x0000000000cf1d7a in braft::NodeImpl::handle_pre_vote_request (this=0x7f89d42a3c30, request=0x7f87bc3c30a0, response=0x7f87bd29ef20) at xxx/src/braft/node.cpp:2027
#9 0x0000000000d7d892 in braft::RaftServiceImpl::pre_vote (this=Unhandled dwarf expression opcode 0xf3
) at xxx/src/braft/raft_service.cpp:62
#10 0x0000000000d67c6b in braft::RaftService::CallMethod (this=Unhandled dwarf expression opcode 0xf3
) at xxx/bld/braft/raft.pb.cc:5130
#11 0x0000000000e70cab in brpc::policy::ProcessRpcRequest (msg_base=Unhandled dwarf expression opcode 0xf3
) at xxx/deps/incubator-brpc/src/brpc/policy/baidu_rpc_protocol.cpp:499
#12 0x0000000000e5b9fa in brpc::ProcessInputMessage (void_arg=Unhandled dwarf expression opcode 0xf3
) at xxx/deps/incubator-brpc/src/brpc/input_messenger.cpp:136
#13 0x0000000000decee1 in bthread::TaskGroup::task_runner (skip_remained=Unhandled dwarf expression opcode 0xf3
) at xxx/deps/incubator-brpc/src/bthread/task_group.cpp:297
#14 0x0000000000e3d5b1 in bthread_make_fcontext ()
#15 0x0000000000000000 in ?? ()`
The text was updated successfully, but these errors were encountered: