目录
现有rpc不好用的原因
baidurpc的解决方法
这里的fd指的是socket
使每个线程同时能做有用的事,而不是因为锁被一个线程抢占了,其他线程就只能空等(one-free),而如果用了锁,就两者都不能保证。每秒可以写入500w个16字节的消息。 解决:使用一个64-bid的id。多线程的时候,不能传指针之类的,因为不知道什么时候就被free了,可能访问的是一个非法内存。所以用一个64位的id,并且被一个类似sharedptr的包住,保证不会在中间被析构,当有一个地方将其标记为失效时,其他用到的地方也都会原子地失效
其他rpc存在的问题: 例1:当往一个连接开始写一个request之后,可能response在write这个函数完成之前就回来了。而如果write的时候会访问一个数据,而拿到response后也会访问同一个数据,那就会有问题 例2:当超时时间设置得很短,线程还在写连接的时候,超时时间就到了,而连接里的东西不完整,可能别人会去读它。
详见https://github.com/brpc/brpc/blob/master/docs/cn/lalb.md#doublybuffereddata
例如,round-robin(rr)。
例如对于rr算法而言,因为naming service对应的下游可能会变,所以每个线程在访问rr的这个列表时,常规的解决方法之一就是用读写锁。例如在查时,用的是读锁,而想要修改时,会用写锁。但在POSIX中,读写锁的性能会特别差。如果临界区不是特别大(例如rr其实临界区非常小),会发现直接用mutex反而比读写锁还要快…
brpc的解决方法:用一个特殊的双buffer读写锁,读时读前台的buffer,写时写后台的buffer,定时同步两个buffer(前后台切换)。但有一个限制就是前后台不能切换得太快,不然可能出现类似前面讲到的ABA的情况,可能一个线程正在读前台,但中间后台和前台切换了,然后又切换一次。所以会有一个切换频率,最低也要2s,一般是5-10s。但在loadbalance这个场景,这个等待时间是不可接受的。所以这里是一个特殊的双buffer读写锁,只需要和每一个前台线程抢一把thread-local锁就行了。
最适合混部的分流算法。是一个动态的迭代型的算法,总是能选择期望延时最低的server。例如,优先把流量分给同机房的机器,只有当同机房的机器或者负载达到一定临界值,或者出现故障的时候,才会导流给邻近机房的机器。
实现了多种一致性hash,便于各类caching使用。主要是两种:
与服务器通信
线程安全,都支持异步 也就是说,不需要每个线程都建一个channel。很多开源的都是线程不安全的
channel的析构是不影响这次rpc_call的。所以如果是点对点的channel,那可以在栈上直接声明一个channel变量,然后去异步地rpc_call,再做别的。但如果是在bns上的channel,因为本身这个init是比较重量级的,就不太好这么用,最好还是用一个类成员变量之类的。
rpc::Channel xx;
xx.Init("10.1.1.1", "la", NULL);
其中,SERVER_OWNS_SERVICE
指的是server析构的时候,一起把service给干掉;反之,SERVER_DOESNT_OWN_SERVICE
指的是service析构时,不把service干掉
rpc::Server xx;
xx.AddService(new MyService(), rpc::SERVER_OWNS_SERVICE);
xx.Start(8010, NULL);
方便监控和调试
因为同端口支持多协议,所以可以用同一个server端口在浏览器打开。可以看到支持的所有service,还有每个method的对应的指标,各种时间维度的流量、平响之类的
精确到微秒,有各种对端的ip之类的
进程内所有的gflags,有(R)的,就表示可以在浏览器里动态修改,改过的会高亮。如果一个gflag有检查函数,就会动态reload(gflag的特性)
所有用到bvar的都会显示在这里,bvar可以算各种cnt,max,min,90perlatency,80perlatency之类的,类似ubmonitor,但性能好很多,会定时写,noah会动态地读
会把进程内所有rpc_call列出来,会分配一个唯一的traceid,会有每一次rpc_call的详情
支持远程做profiling,可以在线做cpu /heap profiling,
同上
好于ub/hulu/sofa/thrift/zeromq…
why:
M:N的线程库,同步的代码可以获得异步的性能,和pthread接口同构
butex使bthread阻塞函数可同时被bthread和pthread调用,分别阻塞bthread和pthread,相互可唤醒。mutex/semaphore/condition之类的,都是基于futex(POSIX最底层的)的,而butex就相当于futex。
超快的创建:每个rpc请求建一个bthread,请求结束,bthread就结束。如果一个channel里有3个请求,会用一个bthread读进来,然后建两个bthread去处理后面两个请求,读数据的那个bthread处理第一个请求
超快的scheduler
从epoll-wait开始,后面的callback会等上面的结束了再执行,会进到epoll-wait状态,延时不可控,有的callback很慢,所以会等很久。
适用于高度定制的
ub_aserver用的就是eventloop,有n个eventloop。
io线程+worker线程
缺点:
需要执行callback时,复用io线程
修改是thread-local的,写数据的时候并不急着读,所以写自己的thread-local就行,不需要全局竞争,只在需要读的时候汇总就行了
基于chromium【https://chromium.googlesource.com/chromium/src/+/master/docs/linux_build_instructions.md】和百度的一些公共库写的