fausturs

Results 7 comments of fausturs

补充一下,我跑的测试代码 brpc部分,就是随便加点输出信息。 搞一个server,类似这样,在处理request得时候提交两个子任务。 一个可能的输出是这样的。 可以看到,在时间轴上,两个线程分别设置了span->_next_client之后,再分别设置了parent->_next_client。这个时候容易知道链表中只有一个节点了(预期是两个)。此时内存泄漏了。 ![image](https://github.com/apache/brpc/assets/25004087/dc90a118-dd5f-4942-8e9f-ba2506a4ec07) con从rpcz页面中也能看到,client span只有一个了。 如果同时写parent->_next_client,则可能写坏内存,导致出core。这个就比较难复现了。

![image](https://github.com/apache/brpc/assets/25004087/58488732-1335-4021-9153-a54439636d3a) rpcz的截图补充

> 上述问题可以通过使用cas进行链表插入来解决,当然brpc的span不止这一处问题,收到请求包后发起异步调用,svr span会被析构。收到异步调用回包后再发起rpc就可能core。done->Run()提前回包后再发起rpc也有类似的问题。 为了解决这个问题我们对svr span加入了引用计数机制。在合适的位置为当前的svr span引用计数+1,析构时-1。算是比较彻底的解决了这个问题。 在这些问题解决前,不建议在生产环境使用brpc的trace。 hello,想请教一下“收到异步调用回包后再发起rpc就可能core”,异步回调中发起rpc时,所在的bthread的thread local的变量中,应该不包含原来那个svr span的指针吧。因为这可能会是一个新的bthread,那么会是什么原因导致的core呢? 从源代码来看(https://github.com/apache/brpc/blob/master/src/brpc/controller.cpp#L718),创建新的bthread的时候,也没有使用BTHREAD_INHERIT_SPAN标志。brpc这个时候,可能不认为新的rpc请求是老的svr span的child span了。不知道我理解的是否有问题 @bigolcy

> 优势在于,创建span仍然是能够完全规避了线程竞争,所以对性能应该很友好。 这个修改没有想象中那么简单,至少还涉及了rpcz相关的代码。这里会出现多个trace id,span id一样的span对象。 那么存入leveldb时,可能要考虑是否合并到一起。 如果不合并则要考虑rpcz的展示需要做一些修改。 所以,加一个锁,或者一个lockfree的队列会来的更快。 另外还注意到TRACEPRINTF这个宏其实可能也有线程安全问题,最终其实是调用到Span::Annotate这个函数。 如果是多个线程同时写,则显然需要一个锁。 所以总的来说,除非trace部分整体重新设计一遍,否则,使用锁是一个看上去更清晰的方案。

> > > 优势在于,创建span仍然是能够完全规避了线程竞争,所以对性能应该很友好。 > > > > > > 这个修改没有想象中那么简单,至少还涉及了rpcz相关的代码。这里会出现多个trace id,span id一样的span对象。 那么存入leveldb时,可能要考虑是否合并到一起。 如果不合并则要考虑rpcz的展示需要做一些修改。 > > 所以,加一个锁,或者一个lockfree的队列会来的更快。 > > 另外还注意到TRACEPRINTF这个宏其实可能也有线程安全问题,最终其实是调用到Span::Annotate这个函数。 如果是多个线程同时写,则显然需要一个锁。 > > 所以总的来说,除非trace部分整体重新设计一遍,否则,使用锁是一个看上去更清晰的方案。 > > 单线程调用内部多次调用createClientSpan,这个应该不会造成重复spanId吧,多线程调用Annotate相当于多线程并发处理一个请求? 如果span id不相同,那么是不是会意味着逻辑上的一个span,在代码中实际上被处理成多个不同的span了?当然,处理成多个不同的span倒是也还蛮好,或许可以参考open...

我提供一个个人的角度,可以讨论一下。 因为新的keytable的策略,是return到一个pthread 的thread local的list中。但没办法保证的是,同一个keytable被borrow后,会被return到同一个pthread里。 本来其实也没什么问题,只要等待足够时间,每个pthread中,就会create足够的keytable。但是可能,目前的一些task的调度策略,会使得pthread中的keytable数量像波浪一样震荡,使得keytable稳定的总数大大增加。 可以看这么一个例子 ```c++ void* print_log(void *) { std::random_device rd; std::mt19937 mt{rd()}; std::uniform_int_distribution uid(0, 100000); bthread_usleep(50000 + uid(mt)); LOG(INFO) name() + "!\n"; response->set_message(message); } }; ``` ![image](https://github.com/user-attachments/assets/1587951d-3a11-474a-ac96-f19f57b768e6)...

这个解决方案看上去很棒。 因为现有的borrow_keytable中,已经包含了对thread local 的keytable为空时,落到另一个全局的list(pool->free_keytables)中的逻辑。 看上去,只需要对return_keytable逻辑进行一些调整。 期待大佬们给出修复。