Commit 478b4343 authored by gejun's avatar gejun

replace baidu-rpc with brpc

parent 0d5be861
This diff is collapsed.
......@@ -25,7 +25,7 @@
- 一个依赖全局多生产者多消费者队列(MPMC)的程序难有很好的多核扩展性,因为这个队列的极限吞吐取决于同步cache的延时,而不是核心的个数。最好是用多个SPMC或多个MPSC队列,甚至多个SPSC队列代替,在源头就规避掉竞争。
- 另一个例子是全局计数器,如果所有线程都频繁修改一个全局变量,性能就会很差,原因同样在于不同的核心在不停地同步同一个cacheline。如果这个计数器只是用作打打日志之类的,那我们完全可以让每个线程修改thread-local变量,在需要时再合并所有线程中的值,性能可能有几十倍的差别。
做不到完全不共享,那就尽量少共享。在一些读很多的场景下,也许可以降低写的频率以减少同步cacheline的次数,以加快读的平均性能。一个相关的编程陷阱是避免false sharing:这指的是那些不怎么被修改的变量,由于同一个cacheline中的另一个变量被频繁修改,而不得不经常等待cacheline同步而显著变慢了。多线程中的变量尽量按访问规律排列,频繁被其他线程的修改要放在独立的cacheline中。要让一个变量或结构体按cacheline对齐,可以include <base/macros.h>然后使用BAIDU_CACHELINE_ALIGNMENT宏,用法请自行grep一下baidu-rpc的代码了解。
做不到完全不共享,那就尽量少共享。在一些读很多的场景下,也许可以降低写的频率以减少同步cacheline的次数,以加快读的平均性能。一个相关的编程陷阱是避免false sharing:这指的是那些不怎么被修改的变量,由于同一个cacheline中的另一个变量被频繁修改,而不得不经常等待cacheline同步而显著变慢了。多线程中的变量尽量按访问规律排列,频繁被其他线程的修改要放在独立的cacheline中。要让一个变量或结构体按cacheline对齐,可以include <base/macros.h>然后使用BAIDU_CACHELINE_ALIGNMENT宏,用法请自行grep一下brpc的代码了解。
# Memory fence
......@@ -91,7 +91,7 @@ if (ready.load(std::memory_order_acquire)) {
# wait-free & lock-free
原子指令能为我们的服务赋予两个重要属性:[wait-free](http://en.wikipedia.org/wiki/Non-blocking_algorithm#Wait-freedom)[lock-free](http://en.wikipedia.org/wiki/Non-blocking_algorithm#Lock-freedom)。前者指不管OS如何调度线程,每个线程都始终在做有用的事;后者比前者弱一些,指不管OS如何调度线程,至少有一个线程在做有用的事。如果我们的服务中使用了锁,那么OS可能把一个刚获得锁的线程切换出去,这时候所有依赖这个锁的线程都在等待,而没有做有用的事,所以用了锁就不是lock-free,更不会是wait-free。为了确保一件事情总在确定时间内完成,实时系统的关键代码至少是lock-free的。在我们广泛又多样的在线服务中,对时效性也有着严苛的要求,如果RPC中最关键的部分满足wait-free或lock-free,就可以提供更稳定的服务质量。比如,由于[fd](https://en.wikipedia.org/wiki/File_descriptor)只适合被单个线程操作,baidu-rpc中使用原子指令最大化了fd的读写的并发度,具体见[IO](io.md)
原子指令能为我们的服务赋予两个重要属性:[wait-free](http://en.wikipedia.org/wiki/Non-blocking_algorithm#Wait-freedom)[lock-free](http://en.wikipedia.org/wiki/Non-blocking_algorithm#Lock-freedom)。前者指不管OS如何调度线程,每个线程都始终在做有用的事;后者比前者弱一些,指不管OS如何调度线程,至少有一个线程在做有用的事。如果我们的服务中使用了锁,那么OS可能把一个刚获得锁的线程切换出去,这时候所有依赖这个锁的线程都在等待,而没有做有用的事,所以用了锁就不是lock-free,更不会是wait-free。为了确保一件事情总在确定时间内完成,实时系统的关键代码至少是lock-free的。在我们广泛又多样的在线服务中,对时效性也有着严苛的要求,如果RPC中最关键的部分满足wait-free或lock-free,就可以提供更稳定的服务质量。比如,由于[fd](https://en.wikipedia.org/wiki/File_descriptor)只适合被单个线程操作,brpc中使用原子指令最大化了fd的读写的并发度,具体见[IO](io.md)
值得提醒的是,常见想法是lock-free或wait-free的算法会更快,但事实可能相反,因为:
......
......@@ -9,13 +9,13 @@
1. A可能对B发起了过于频繁的基于超时的重试。这不仅会让A的最大qps降到**线程数 / 超时**,还会让B处的qps翻**重试次数**倍。这就可能陷入恶性循环了:只要**线程数 / 超时 \* 重试次数**大于B的最大qps**,**B就无法恢复 -> A处的client会继续超时 -> A继续重试 -> B继续无法恢复。
2. A或B没有限制某个缓冲或队列的长度,或限制过于宽松。拥塞请求会大量地积压在那里,要恢复就得全部处理完,时间可能长得无法接受。由于有限长的缓冲或队列需要在填满时解决等待、唤醒等问题,有时为了简单,代码可能会假定缓冲或队列不会满,这就埋下了种子。即使队列是有限长的,恢复时间也可能很长,因为清空队列的过程是个追赶问题,排空的时间取决于**积压的请求数 / (最大qps - 当前qps)**,如果当前qps和最大qps差的不多,积压的请求又比较多,那排空时间就遥遥无期了。
了解这些因素后可以更好的理解baidu-rpc中相关的设计。
了解这些因素后可以更好的理解brpc中相关的设计。
1. 拥塞时A服务最大qps的跳变是因为线程个数是**硬限**,单个请求的处理时间很大程度上决定了最大qps。而baidu-rpc server端默认在bthread中处理请求,个数是软限,单个请求超时只是阻塞所在的bthread,并不会影响为新请求建立新的bthread。baidu-rpc也提供了完整的异步接口,让用户可以进一步提高io-bound服务的并发度,降低服务被打满的可能性。
2. baidu-rpc中[重试](client.md#重试)默认只在连接出错时发起,避免了流量放大,这是比较有效率的重试方式。如果需要基于超时重试,可以设置[backup request](client.md#backuprequest),这类重试最多只有一次,放大程度降到了最低。baidu-rpc中的RPC超时是deadline,超过后RPC一定会结束,这让用户对服务的行为有更好的预判。在之前的一些实现中,RPC超时是单次超时*重试次数,在实践中容易误判。
3. baidu-rpc server端的[max_concurrency选项](server.md#id-创建和设置Server-限制最大并发)控制了server的最大并发:当同时处理的请求数超过max_concurrency时,server会回复client错误,而不是继续积压。这一方面在服务开始的源头控制住了积压的请求数,尽量避免延生到用户缓冲或队列中,另一方面也让client尽快地去重试其他server,对集群来说是个更好的策略。
1. 拥塞时A服务最大qps的跳变是因为线程个数是**硬限**,单个请求的处理时间很大程度上决定了最大qps。而brpc server端默认在bthread中处理请求,个数是软限,单个请求超时只是阻塞所在的bthread,并不会影响为新请求建立新的bthread。brpc也提供了完整的异步接口,让用户可以进一步提高io-bound服务的并发度,降低服务被打满的可能性。
2. brpc中[重试](client.md#重试)默认只在连接出错时发起,避免了流量放大,这是比较有效率的重试方式。如果需要基于超时重试,可以设置[backup request](client.md#backuprequest),这类重试最多只有一次,放大程度降到了最低。brpc中的RPC超时是deadline,超过后RPC一定会结束,这让用户对服务的行为有更好的预判。在之前的一些实现中,RPC超时是单次超时*重试次数,在实践中容易误判。
3. brpc server端的[max_concurrency选项](server.md#id-创建和设置Server-限制最大并发)控制了server的最大并发:当同时处理的请求数超过max_concurrency时,server会回复client错误,而不是继续积压。这一方面在服务开始的源头控制住了积压的请求数,尽量避免延生到用户缓冲或队列中,另一方面也让client尽快地去重试其他server,对集群来说是个更好的策略。
对于baidu-rpc的用户来说,要防止雪崩,主要注意两点:
对于brpc的用户来说,要防止雪崩,主要注意两点:
1. 评估server的最大并发,设置合理的max_concurrency值。这个默认是不设的,也就是不限制。无论程序是同步还是异步,用户都可以通过 **最大qps \* 非拥塞时的延时**(秒)来评估最大并发,原理见[little's law](https://en.wikipedia.org/wiki/Little),这两个量都可以在baidu-rpc中的内置服务中看到。max_concurrency与最大并发相等或大一些就行了。
2. 注意考察重试发生时的行为,特别是在定制RetryPolicy时。如果你只是用默认的baidu-rpc重试,一般是安全的。但用户程序也常会自己做重试,比如通过一个Channel访问失败后,去访问另外一个Channel,这种情况下要想清楚重试发生时最差情况下请求量会放大几倍,服务是否可承受。
1. 评估server的最大并发,设置合理的max_concurrency值。这个默认是不设的,也就是不限制。无论程序是同步还是异步,用户都可以通过 **最大qps \* 非拥塞时的延时**(秒)来评估最大并发,原理见[little's law](https://en.wikipedia.org/wiki/Little),这两个量都可以在brpc中的内置服务中看到。max_concurrency与最大并发相等或大一些就行了。
2. 注意考察重试发生时的行为,特别是在定制RetryPolicy时。如果你只是用默认的brpc重试,一般是安全的。但用户程序也常会自己做重试,比如通过一个Channel访问失败后,去访问另外一个Channel,这种情况下要想清楚重试发生时最差情况下请求量会放大几倍,服务是否可承受。
有时为了保证可用性,需要同时访问两路服务,哪个先返回就取哪个。在baidu-rpc中,这有多种做法:
有时为了保证可用性,需要同时访问两路服务,哪个先返回就取哪个。在brpc中,这有多种做法:
# 当后端server可以挂在一个名字服务内时
Channel开启backup request。这个Channel会先向其中一个server发送请求,如果在ChannelOptions.backup_request_ms后还没回来,再向另一个server发送。之后哪个先回来就取哪个。在设置了合理的backup_request_ms后,大部分时候只会发一个请求,对后端服务只有一倍压力。
示例代码见[example/backup_request_c++](http://icode.baidu.com/repo/baidu/opensource/baidu-rpc/files/master/blob/example/backup_request_c++)。这个例子中,client设定了在2ms后发送backup request,server在碰到偶数位的请求后会故意睡眠20ms以触发backup request。
示例代码见[example/backup_request_c++](http://icode.baidu.com/repo/baidu/opensource/brpc/files/master/blob/example/backup_request_c++)。这个例子中,client设定了在2ms后发送backup request,server在碰到偶数位的请求后会故意睡眠20ms以触发backup request。
运行后,client端和server端的日志分别如下,“index”是请求的编号。可以看到server端在收到第一个请求后会故意sleep 20ms,client端之后发送另一个同样index的请求,最终的延时并没有受到故意sleep的影响。
......@@ -18,7 +18,7 @@ Channel开启backup request。这个Channel会先向其中一个server发送请
## 选择合理的backup_request_ms
可以观察baidu-rpc默认提供的latency_cdf图,或自行添加。cdf图的y轴是延时(默认微秒),x轴是小于y轴延时的请求的比例。在下图中,选择backup_request_ms=2ms可以大约覆盖95.5%的请求,选择backup_request_ms=10ms则可以覆盖99.99%的请求。
可以观察brpc默认提供的latency_cdf图,或自行添加。cdf图的y轴是延时(默认微秒),x轴是小于y轴延时的请求的比例。在下图中,选择backup_request_ms=2ms可以大约覆盖95.5%的请求,选择backup_request_ms=10ms则可以覆盖99.99%的请求。
![img](../images/backup_request_4.png)
......@@ -41,6 +41,6 @@ my_func_latency << tm.u_elapsed(); // u代表微秒,还有s_elapsed(), m_elap
# 当后端server不能挂在一个名字服务内时
【推荐】建立一个开启backup request的SelectiveChannel,其中包含两个sub channel。访问这个SelectiveChannel和上面的情况类似,会先访问一个sub channel,如果在ChannelOptions.backup_request_ms后没返回,再访问另一个sub channel。如果一个sub channel对应一个集群,这个方法就是在两个集群间做互备。SelectiveChannel的例子见[example/selective_echo_c++](http://icode.baidu.com/repo/baidu/opensource/baidu-rpc/files/master/tree/example/selective_echo_c++),具体做法请参考上面的过程。
【推荐】建立一个开启backup request的SelectiveChannel,其中包含两个sub channel。访问这个SelectiveChannel和上面的情况类似,会先访问一个sub channel,如果在ChannelOptions.backup_request_ms后没返回,再访问另一个sub channel。如果一个sub channel对应一个集群,这个方法就是在两个集群间做互备。SelectiveChannel的例子见[example/selective_echo_c++](http://icode.baidu.com/repo/baidu/opensource/brpc/files/master/tree/example/selective_echo_c++),具体做法请参考上面的过程。
【不推荐】发起两个异步RPC后Join它们,它们的done内是相互取消的逻辑。示例代码见[example/cancel_c++](http://icode.baidu.com/repo/baidu/opensource/baidu-rpc/files/master/tree/example/cancel_c++)。这种方法的问题是总会发两个请求,对后端服务有两倍压力,这个方法怎么算都是不经济的,你应该尽量避免用这个方法。
【不推荐】发起两个异步RPC后Join它们,它们的done内是相互取消的逻辑。示例代码见[example/cancel_c++](http://icode.baidu.com/repo/baidu/opensource/brpc/files/master/tree/example/cancel_c++)。这种方法的问题是总会发两个请求,对后端服务有两倍压力,这个方法怎么算都是不经济的,你应该尽量避免用这个方法。
This diff is collapsed.
可代替[ab](https://httpd.apache.org/docs/2.2/programs/ab.html)测试http server极限性能。ab功能较多但年代久远,有时本身可能会成为瓶颈。benchmark_http基本上就是一个baidu-rpc http client,性能很高,功能较少,一般压测够用了。
可代替[ab](https://httpd.apache.org/docs/2.2/programs/ab.html)测试http server极限性能。ab功能较多但年代久远,有时本身可能会成为瓶颈。benchmark_http基本上就是一个brpc http client,性能很高,功能较少,一般压测够用了。
使用方法:
首先你得[下载和编译](getting_started.md)了baidu-rpc源码,然后去example/http_c++目录编译,成功后应该能看到benchmark_http。
首先你得[下载和编译](getting_started.md)了brpc源码,然后去example/http_c++目录编译,成功后应该能看到benchmark_http。
bthread([代码](http://icode.baidu.com/repo/baidu/opensource/baidu-rpc/files/master/tree/src/bthread))是baidu-rpc使用的M:N线程库,目的是在提高程序的并发度的同时,降低编码难度,并在核数日益增多的CPU上提供更好的scalability和cache locality。”M:N“是指M个bthread会映射至N个pthread,一般M远大于N。由于linux当下的pthread实现([NPTL](http://en.wikipedia.org/wiki/Native_POSIX_Thread_Library))是1:1的,M个bthread也相当于映射至N个[LWP](http://en.wikipedia.org/wiki/Light-weight_process)。bthread的前身是[DP](http://wiki.babel.baidu.com/twiki/bin/view/Com/Ecom/DistributedProcess)中的fiber,一个N:1的合作式线程库,等价于event-loop库,但写的是同步代码。
bthread([代码](http://icode.baidu.com/repo/baidu/opensource/brpc/files/master/tree/src/bthread))是brpc使用的M:N线程库,目的是在提高程序的并发度的同时,降低编码难度,并在核数日益增多的CPU上提供更好的scalability和cache locality。”M:N“是指M个bthread会映射至N个pthread,一般M远大于N。由于linux当下的pthread实现([NPTL](http://en.wikipedia.org/wiki/Native_POSIX_Thread_Library))是1:1的,M个bthread也相当于映射至N个[LWP](http://en.wikipedia.org/wiki/Light-weight_process)。bthread的前身是[DP](http://wiki.babel.baidu.com/twiki/bin/view/Com/Ecom/DistributedProcess)中的fiber,一个N:1的合作式线程库,等价于event-loop库,但写的是同步代码。
# Goals
......@@ -22,7 +22,7 @@ bthread是一个M:N线程库,一个bthread被卡住不会影响其他bthread
##### Q: 我应该在程序中多使用bthread吗?
不应该。除非你需要在一次RPC过程中[让一些代码并发运行](bthread_or_not.md),你不应该直接调用bthread函数,把这些留给baidu-rpc做更好。
不应该。除非你需要在一次RPC过程中[让一些代码并发运行](bthread_or_not.md),你不应该直接调用bthread函数,把这些留给brpc做更好。
##### Q:bthread和pthread worker如何对应?
......@@ -43,7 +43,7 @@ pthread worker在任何时间只会运行一个bthread,当前bthread挂起时
##### Q:若有大量的bthread调用了阻塞的pthread或系统函数,会影响RPC运行么?
会。比如有8个pthread worker,当有8个bthread都调用了系统usleep()后,处理网络收发的RPC代码就暂时无法运行了。只要阻塞时间不太长, 这一般没什么影响, 毕竟worker都用完了, 除了排队也没有什么好方法.
在baidu-rpc中用户可以选择调大worker数来缓解问题, 在server端可设置[ServerOptions.num_threads](server.md#id-创建和设置Server-worker线程数)[-bthread_concurrency](http://brpc.baidu.com:8765/flags/bthread_concurrency), 在client端可设置[-bthread_concurrency](http://brpc.baidu.com:8765/flags/bthread_concurrency).
在brpc中用户可以选择调大worker数来缓解问题, 在server端可设置[ServerOptions.num_threads](server.md#id-创建和设置Server-worker线程数)[-bthread_concurrency](http://brpc.baidu.com:8765/flags/bthread_concurrency), 在client端可设置[-bthread_concurrency](http://brpc.baidu.com:8765/flags/bthread_concurrency).
那有没有完全规避的方法呢?
......
......@@ -8,9 +8,9 @@ bthread_id是一个特殊的同步结构,它可以互斥RPC过程中的不同
- 通过correlation_id在O(1)时间内找到对应的RPC上下文,而无需建立从correlation_id到RPC上下文的全局哈希表。
- 取消RPC。
上文提到的bug在其他rpc框架中广泛存在,下面我们来看下baidu-rpc是如何通过bthread_id解决这些问题的。
上文提到的bug在其他rpc框架中广泛存在,下面我们来看下brpc是如何通过bthread_id解决这些问题的。
bthread_id包括两部分,一个是用户可见的64位id,另一个是对应的不可见的bthread::Id结构体。用户接口都是操作id的。从id映射到结构体的方式和baidu-rpc中的[其他结构](memory_management.md)类似:32位是内存池的位移,32位是version。前者O(1)时间定位,后者防止ABA问题。
bthread_id包括两部分,一个是用户可见的64位id,另一个是对应的不可见的bthread::Id结构体。用户接口都是操作id的。从id映射到结构体的方式和brpc中的[其他结构](memory_management.md)类似:32位是内存池的位移,32位是version。前者O(1)时间定位,后者防止ABA问题。
bthread_id的接口不太简洁,有不少API:
......
baidu-rpc提供了[异步接口](client.md#异步访问),所以一个常见的问题是:我应该用异步接口还是bthread?
brpc提供了[异步接口](client.md#异步访问),所以一个常见的问题是:我应该用异步接口还是bthread?
短回答:延时不高时你应该先用简单易懂的同步接口,不行的话用异步接口,只有在需要多核并行计算时才用bthread。
......@@ -6,7 +6,7 @@ baidu-rpc提供了[异步接口](client.md#异步访问),所以一个常见的
异步即用回调代替阻塞,有阻塞的地方就有回调。虽然在javascript这种语言中回调工作的很好,接受度也非常高,但只要用过,就会发现这和我们需要的回调是两码事,这个区别不是[lambda](https://en.wikipedia.org/wiki/Anonymous_function),也不是[future](https://en.wikipedia.org/wiki/Futures_and_promises),而是javascript是单线程的。javascript的回调放到多线程下可能没有一个能跑过,竞争太多,单线程的同步方法和多线程的同步方法是完全不同的。那是不是服务能搞成类似的形式呢?多个线程,每个都是独立的eventloop。可以,ub**a**server就是(注意带a),但实际效果糟糕,因为阻塞改回调可不简单,当阻塞发生在循环,条件分支,深层子函数中时,改造特别困难,况且很多老代码、第三方代码根本不可能去改造。结果是代码中会出现不可避免的阻塞,导致那个线程中其他回调都被延迟,流量超时,server性能不符合预期。如果你说,”我想把现在的同步代码改造为大量的回调,除了我其他人都看不太懂,并且性能可能更差了”,我猜大部分人不会同意。别被那些鼓吹异步的人迷惑了,他们写的是从头到尾从下到上全异步且不考虑多线程的代码,和你要写的完全是两码事。
baidu-rpc中的异步和单线程的异步是完全不同的,异步回调会运行在与调用处不同的线程中,你会获得多核扩展性,但代价是你得意识到多线程问题。你可以在回调中阻塞,只要线程够用,对server整体的性能并不会有什么影响。不过异步代码还是很难写的,所以我们提供了[组合访问](combo_channel.md)来简化问题,通过组合不同的channel,你可以声明式地执行复杂的访问,而不用太关心其中的细节。
brpc中的异步和单线程的异步是完全不同的,异步回调会运行在与调用处不同的线程中,你会获得多核扩展性,但代价是你得意识到多线程问题。你可以在回调中阻塞,只要线程够用,对server整体的性能并不会有什么影响。不过异步代码还是很难写的,所以我们提供了[组合访问](combo_channel.md)来简化问题,通过组合不同的channel,你可以声明式地执行复杂的访问,而不用太关心其中的细节。
当然,延时不长,qps不高时,我们更建议使用同步接口,这也是创建bthread的动机:维持同步代码也能提升交互性能。
......@@ -22,7 +22,7 @@ baidu-rpc中的异步和单线程的异步是完全不同的,异步回调会
# 异步或bthread
有了bthread这个工具,用户甚至可以自己实现异步。以“半同步”为例,在baidu-rpc中用户有多种选择:
有了bthread这个工具,用户甚至可以自己实现异步。以“半同步”为例,在brpc中用户有多种选择:
- 发起多个异步RPC后挨个Join,这个函数会阻塞直到RPC结束。(这儿是为了和bthread对比,实现中我们建议你使用[ParallelChannel](combo_channel.md#parallelchannel),而不是自己Join)
- 启动多个bthread各自执行同步RPC后挨个join bthreads。
......
# 什么是内置服务?
内置服务以多种形式展现服务器内部状态,提高你开发和调试服务的效率。baidu-rpc通过HTTP协议提供内置服务,可通过浏览器或curl访问,服务器会根据User-Agent返回纯文本或html,你也可以添加?console=1要求返回纯文本。我们在自己的开发机上启动了[一个长期运行的例子](http://brpc.baidu.com:8765/),你可以点击后随便看看。服务端口只有在8000-8999内才能被笔记本访问到,对于范围之外的端口可以使用[rpc_view](rpc_view.md)或在命令行中使用curl <SERVER-URL>
内置服务以多种形式展现服务器内部状态,提高你开发和调试服务的效率。brpc通过HTTP协议提供内置服务,可通过浏览器或curl访问,服务器会根据User-Agent返回纯文本或html,你也可以添加?console=1要求返回纯文本。我们在自己的开发机上启动了[一个长期运行的例子](http://brpc.baidu.com:8765/),你可以点击后随便看看。服务端口只有在8000-8999内才能被笔记本访问到,对于范围之外的端口可以使用[rpc_view](rpc_view.md)或在命令行中使用curl <SERVER-URL>
从浏览器访问:
......
# 1.什么是bvar?
[public/bvar](http://icode.baidu.com/repo/baidu/opensource/baidu-rpc/files/master/blob/src/)是多线程环境下的计数器类库,方便记录和查看用户程序中的各类数值,它利用了thread local存储避免了cache bouncing,相比UbMonitor几乎不会给程序增加性能开销,也快于竞争频繁的原子操作。baidu-rpc集成了bvar,[/vars](http://brpc.baidu.com:8765/vars)可查看所有曝光的bvar,[/vars/VARNAME](http://brpc.baidu.com:8765/vars/rpc_socket_count)可查阅某个bvar,在rpc中的具体使用方法请查看[这里](vars.md)。baidu-rpc大量使用了bvar提供统计数值,当你需要在多线程环境中计数并展现时,应该第一时间想到bvar。但bvar不能代替所有的计数器,它的本质是把写时的竞争转移到了读:读得合并所有写过的线程中的数据,而不可避免地变慢了。当你读写都很频繁并得基于数值做一些逻辑判断时,你不应该用bvar。
[public/bvar](http://icode.baidu.com/repo/baidu/opensource/brpc/files/master/blob/src/)是多线程环境下的计数器类库,方便记录和查看用户程序中的各类数值,它利用了thread local存储避免了cache bouncing,相比UbMonitor几乎不会给程序增加性能开销,也快于竞争频繁的原子操作。brpc集成了bvar,[/vars](http://brpc.baidu.com:8765/vars)可查看所有曝光的bvar,[/vars/VARNAME](http://brpc.baidu.com:8765/vars/rpc_socket_count)可查阅某个bvar,在rpc中的具体使用方法请查看[这里](vars.md)。brpc大量使用了bvar提供统计数值,当你需要在多线程环境中计数并展现时,应该第一时间想到bvar。但bvar不能代替所有的计数器,它的本质是把写时的竞争转移到了读:读得合并所有写过的线程中的数据,而不可避免地变慢了。当你读写都很频繁并得基于数值做一些逻辑判断时,你不应该用bvar。
# 2.什么是cache bouncing?
......
......@@ -145,11 +145,11 @@ class Variable {
static int dump_exposed(Dumper* dumper, const DumpOptions* options);
};
```
最常见的导出需求是通过HTTP接口查询和写入本地文件。前者在baidu-rpc中通过[/vars](vars.md)服务提供,后者则已实现在bvar中,由用户选择开启。该功能由5个gflags控制,你的程序需要使用[gflags](flags.md)
最常见的导出需求是通过HTTP接口查询和写入本地文件。前者在brpc中通过[/vars](vars.md)服务提供,后者则已实现在bvar中,由用户选择开启。该功能由5个gflags控制,你的程序需要使用[gflags](flags.md)
![img](../images/bvar_dump_flags.png)
用户可在程序启动前加上对应的gflags,在baidu-rpc中也可通过[/flags](flags.md)服务在启动后动态修改某个gflag。
用户可在程序启动前加上对应的gflags,在brpc中也可通过[/flags](flags.md)服务在启动后动态修改某个gflag。
当bvar_dump_file不为空时,程序会启动一个后台导出线程以bvar_dump_interval指定的间隔更新bvar_dump_file,其中包含了被bvar_dump_include匹配且不被bvar_dump_exclude匹配的所有bvar。
......@@ -171,7 +171,7 @@ rpc_server_8002_uptime_ms : 14740954
像”`iobuf_block_count : 8`”被bvar_dump_include过滤了,“`rpc_server_8002_error : 0`”则被bvar_dump_exclude排除了。
如果你的程序没有使用baidu-rpc,仍需要动态修改gflag(一般不需要),可以调用google::SetCommandLineOption(),如下所示:
如果你的程序没有使用brpc,仍需要动态修改gflag(一般不需要),可以调用google::SetCommandLineOption(),如下所示:
```c++
#include <gflags/gflags.h>
...
......
# 背景
baidu-dsp是联盟基于Ad Exchange和RTB模式的需求方平台,服务大客户、代理的投放产品体系。我们改造了多个模块,均取得了显著的效果。本文只介绍其中关于super-nova-as的改动。super-nova-as是的baidu-dsp的AS,之前使用ub-aserver编写,为了尽量减少改动,我们没有改造整个as,而只是把super-nova-as连接下游(ctr-server、cvr-server、super-nova-bs)的client从ubrpc升级为baidu-rpc。
baidu-dsp是联盟基于Ad Exchange和RTB模式的需求方平台,服务大客户、代理的投放产品体系。我们改造了多个模块,均取得了显著的效果。本文只介绍其中关于super-nova-as的改动。super-nova-as是的baidu-dsp的AS,之前使用ub-aserver编写,为了尽量减少改动,我们没有改造整个as,而只是把super-nova-as连接下游(ctr-server、cvr-server、super-nova-bs)的client从ubrpc升级为brpc。
# 结论
......@@ -10,11 +10,11 @@ baidu-dsp是联盟基于Ad Exchange和RTB模式的需求方平台,服务大客
# 测试过程
1. 环境:1个as,1个bs,1个ctr,1个cvr;部署情况为:bs单机部署,as+ctr+cvr混布;ctr和cvr为baidu-rpc版本
1. 环境:1个as,1个bs,1个ctr,1个cvr;部署情况为:bs单机部署,as+ctr+cvr混布;ctr和cvr为brpc版本
2. 分别采用1000,1500压力对ubrpc版本的as进行压测,发现1500压力下,as对bs有大量的超时,as到达瓶颈;
3. 分别采用2000,2500压力对baidu-rpc版本的as进行压测,发现2500压力下,as机器的cpu_idle低于30%,as到达瓶颈。baidu-rpc对资源利用充分。
3. 分别采用2000,2500压力对brpc版本的as进行压测,发现2500压力下,as机器的cpu_idle低于30%,as到达瓶颈。brpc对资源利用充分。
| | ubrpc | baidu-rpc |
| | ubrpc | brpc |
| -------- | ---------------------------------------- | ---------------------------------------- |
| 流量 | ![img](../images/baidu_dsp_compare_1.png) | ![img](../images/baidu_dsp_compare_2.png) |
| bs成功率 | ![img](../images/baidu_dsp_compare_3.png) | ![img](../images/baidu_dsp_compare_4.png) |
......
# 背景
ELF(Essential/Extreme/Excellent Learning Framework) 框架为公司内外的大数据应用提供学习/挖掘算法开发支持。 平台主要包括数据迭代处理的框架支持,并行计算过程中的通信支持和用于存储大规模参数的分布式、快速、高可用参数服务器。应用于fcr-model,公有云bml,大数据实验室,语音技术部门等等。之前是基于[zeromq](http://zeromq.org/)封装的rpc,这次改用baidu-rpc。
ELF(Essential/Extreme/Excellent Learning Framework) 框架为公司内外的大数据应用提供学习/挖掘算法开发支持。 平台主要包括数据迭代处理的框架支持,并行计算过程中的通信支持和用于存储大规模参数的分布式、快速、高可用参数服务器。应用于fcr-model,公有云bml,大数据实验室,语音技术部门等等。之前是基于[zeromq](http://zeromq.org/)封装的rpc,这次改用brpc。
# 结论
......
# 背景
云平台部把使用ubrpc的模块改造为使用baidu-rpc。由于使用了mcpack2pb的转换功能,这个模块既能被老的ubrpc client访问,也可以通过protobuf类的协议访问(标准协议,sofa-pbrpc协议等等)。
云平台部把使用ubrpc的模块改造为使用brpc。由于使用了mcpack2pb的转换功能,这个模块既能被老的ubrpc client访问,也可以通过protobuf类的协议访问(标准协议,sofa-pbrpc协议等等)。
下面是原报告。
原有使用43台机器(对ubrpc也有富余),baidu-rpc使用3台机器即可(此时访问redis的io达到瓶颈)。当前流量4w qps,支持流量增长,考虑跨机房冗余,避免redis和vip瓶颈,baidu-rpc实际使用8台机器提供服务。
原有使用43台机器(对ubrpc也有富余),brpc使用3台机器即可(此时访问redis的io达到瓶颈)。当前流量4w qps,支持流量增长,考虑跨机房冗余,避免redis和vip瓶颈,brpc实际使用8台机器提供服务。
baidu-rpc改造后的connecter收益明显,可以用较少的机器提供更优质的服务。收益分3个方面:
brpc改造后的connecter收益明显,可以用较少的机器提供更优质的服务。收益分3个方面:
# 相同配置的机器qps和latency的比较
......@@ -18,7 +18,7 @@ baidu-rpc改造后的connecter收益明显,可以用较少的机器提供更
混布情况:同机部署了逻辑层2.0/3.0和C逻辑层,均有流量
图中可以看到随着压力的增大:
* baidu-rpc的延时,增加微乎其微,提供了较为一致的延时体验
* brpc的延时,增加微乎其微,提供了较为一致的延时体验
* ubrpc的延时,快速增大,到了6000~8000qps的时候,出现*queue full*,服务不可用。
# 不同配置机器qps和延时的比较
......@@ -27,13 +27,13 @@ qps固定为6500,观察延时。
| --------- | ---------------------------------------- | ---------------------------------------- |
| cpu | 24 Intel(R) Xeon(R) CPU E5645 @ 2.40GHz | 24 Intel(R) Xeon(R) CPU E5-2620 0 @ 2.00GHz |
| ubrpc | 8363.46(us) | 12649.5(us) |
| baidu-rpc | 3364.66(us) | 3382.15(us) |
| brpc | 3364.66(us) | 3382.15(us) |
有此可见:
* ubrpc在不同配置下性能表现差异大,在配置较低的机器下表现较差。
* baidu-rpc表现的比ubrpc好,在较低配置的机器上也能有好的表现,因机器不同带来的差异不大。
* brpc表现的比ubrpc好,在较低配置的机器上也能有好的表现,因机器不同带来的差异不大。
# 相同配置机器idle分布的比较
......@@ -44,4 +44,4 @@ qps固定为6500,观察延时。
在线上缩容 不断增大压力过程中:
* ubrpc cpu idle分布在35%~60%,在55%最集中,最低30%;
* baidu-rpc cpu idle分布在60%~85%,在75%最集中,最低50%; baidu-rpc比ubrpc对cpu的消耗低。
* brpc cpu idle分布在60%~85%,在75%最集中,最低50%; brpc比ubrpc对cpu的消耗低。
This diff is collapsed.
......@@ -15,11 +15,11 @@ ParallelChannel (“pchan”)同时访问其包含的sub channel,并合并它
- 可以取消。
- 支持超时。
示例代码见[example/parallel_echo_c++](http://icode.baidu.com/repo/baidu/opensource/baidu-rpc/files/master/tree/example/parallel_echo_c++/)
示例代码见[example/parallel_echo_c++](http://icode.baidu.com/repo/baidu/opensource/brpc/files/master/tree/example/parallel_echo_c++/)
任何brpc::ChannelBase的子类都可以加入ParallelChannel,包括ParallelChannel和其他组合Channel。用户可以设置ParallelChannelOptions.fail_limit来控制访问的最大失败次数(r31803前是ParallelChannel::set_fail_limit),当失败的访问达到这个数目时,RPC call会立刻结束而不等待超时。
当baidu-rpc >= 1.0.155.31351时,一个sub channel可多次加入同一个ParallelChannel。当你需要对同一个服务发起多次异步访问并等待它们完成的话,这很有用。
当brpc >= 1.0.155.31351时,一个sub channel可多次加入同一个ParallelChannel。当你需要对同一个服务发起多次异步访问并等待它们完成的话,这很有用。
ParallelChannel的内部结构大致如下:
......@@ -36,13 +36,13 @@ int AddChannel(brpc::ChannelBase* sub_channel,
ResponseMerger* response_merger);
```
当ownership为brpc::OWNS_CHANNEL时,sub_channel会在ParallelChannel析构时被删除。当baidu-rpc >= 1.0.155.31351时,由于一个sub channel可能会多次加入一个ParallelChannel,只要其中一个指明了ownership为brpc::OWNS_CHANNEL,那个sub channel就会在ParallelChannel析构时被删除(一次)。
当ownership为brpc::OWNS_CHANNEL时,sub_channel会在ParallelChannel析构时被删除。当brpc >= 1.0.155.31351时,由于一个sub channel可能会多次加入一个ParallelChannel,只要其中一个指明了ownership为brpc::OWNS_CHANNEL,那个sub channel就会在ParallelChannel析构时被删除(一次)。
访问ParallelChannel时调用AddChannel是线程**不安全**的。
## CallMapper
用于把对ParallelChannel的调用转化为对sub channel的调用。如果call_mapper是NULL,sub channel的请求就是ParallelChannel的请求,而response则New()自ParallelChannel的response。如果call_mapper不为NULL,则会在ParallelChannel析构时被删除。当baidu-rpc >= 1.0.105.30846时,call_mapper内含引用计数,一个call_mapper可与多个sub channel关联。
用于把对ParallelChannel的调用转化为对sub channel的调用。如果call_mapper是NULL,sub channel的请求就是ParallelChannel的请求,而response则New()自ParallelChannel的response。如果call_mapper不为NULL,则会在ParallelChannel析构时被删除。当brpc >= 1.0.105.30846时,call_mapper内含引用计数,一个call_mapper可与多个sub channel关联。
```c++
class CallMapper {
......@@ -148,14 +148,14 @@ const Controller* sub(int index) const;
# SelectiveChannel
[SelectiveChannel](http://icode.baidu.com/repo/baidu/opensource/baidu-rpc/files/master/blob/src/brpc/selective_channel.h) (“schan”)按负载均衡算法访问其包含的一个Channel,相比普通Channel它更加高层:把流量分给sub channel,而不是具体的Server。SelectiveChannel主要用来支持机器组之间的负载均衡,它具备Channel的主要属性:
[SelectiveChannel](http://icode.baidu.com/repo/baidu/opensource/brpc/files/master/blob/src/brpc/selective_channel.h) (“schan”)按负载均衡算法访问其包含的一个Channel,相比普通Channel它更加高层:把流量分给sub channel,而不是具体的Server。SelectiveChannel主要用来支持机器组之间的负载均衡,它具备Channel的主要属性:
- 支持同步和异步访问。
- 发起异步操作后可以立刻删除。
- 可以取消。
- 支持超时。
示例代码见[example/selective_echo_c++](http://icode.baidu.com/repo/baidu/opensource/baidu-rpc/files/master/tree/example/selective_echo_c++/)
示例代码见[example/selective_echo_c++](http://icode.baidu.com/repo/baidu/opensource/brpc/files/master/tree/example/selective_echo_c++/)
任何brpc::ChannelBase的子类都可加入SelectiveChannel,包括SelectiveChannel和其他组合Channel。
......@@ -240,9 +240,9 @@ stub.FooMethod(&cntl, &request, &response, NULL);
# PartitionChannel
[PartitionChannel](http://icode.baidu.com/repo/baidu/opensource/baidu-rpc/files/master/blob/src/brpc/partition_channel.h)是特殊的ParallelChannel,它会根据名字服务中的tag自动建立对应分库的sub channel。这样用户就可以把所有的分库机器挂在一个名字服务内,通过tag来指定哪台机器对应哪个分库。示例代码见[example/partition_echo_c++](http://icode.baidu.com/repo/baidu/opensource/baidu-rpc/files/master/tree/example/partition_echo_c++/)
[PartitionChannel](http://icode.baidu.com/repo/baidu/opensource/brpc/files/master/blob/src/brpc/partition_channel.h)是特殊的ParallelChannel,它会根据名字服务中的tag自动建立对应分库的sub channel。这样用户就可以把所有的分库机器挂在一个名字服务内,通过tag来指定哪台机器对应哪个分库。示例代码见[example/partition_echo_c++](http://icode.baidu.com/repo/baidu/opensource/brpc/files/master/tree/example/partition_echo_c++/)
ParititonChannel只能处理一种分库方法,当用户需要多种分库方法共存,或从一个分库方法平滑地切换为另一种分库方法时,可以使用DynamicPartitionChannel,它会根据不同的分库方式动态地建立对应的sub PartitionChannel,并根据容量把请求分配给不同的分库。示例代码见[example/dynamic_partition_echo_c++](http://icode.baidu.com/repo/baidu/opensource/baidu-rpc/files/master/tree/example/dynamic_partition_echo_c++/)
ParititonChannel只能处理一种分库方法,当用户需要多种分库方法共存,或从一个分库方法平滑地切换为另一种分库方法时,可以使用DynamicPartitionChannel,它会根据不同的分库方式动态地建立对应的sub PartitionChannel,并根据容量把请求分配给不同的分库。示例代码见[example/dynamic_partition_echo_c++](http://icode.baidu.com/repo/baidu/opensource/brpc/files/master/tree/example/dynamic_partition_echo_c++/)
如果分库在不同的名字服务内,那么用户得自行用ParallelChannel组装,即每个sub channel对应一个分库(使用不同的名字服务)。ParellelChannel的使用方法请见上一节。
......
baidu-rpc可以分析花在等待锁上的时间及发生等待的函数。
brpc可以分析花在等待锁上的时间及发生等待的函数。
# 开启方法
按需开启。无需配置,不依赖tcmalloc,不需要链接frame pointer或libunwind。如果只是baidu-rpc client或没有使用baidu-rpc,看[这里](dummy_server.md)
按需开启。无需配置,不依赖tcmalloc,不需要链接frame pointer或libunwind。如果只是brpc client或没有使用brpc,看[这里](dummy_server.md)
# 图示
当很多线程争抢同一把锁时,一些线程无法立刻获得锁,而必须睡眠直到某个线程退出临界区。这个争抢过程我们称之为**contention**。在多核机器上,当多个线程需要操作同一个资源却被一把锁挡住时,便无法充分发挥多个核心的并发能力。现代OS通过提供比锁更底层的同步原语,使得无竞争锁完全不需要系统调用,只是一两条wait-free,耗时10-20ns的原子操作,非常快。而锁一旦发生竞争,一些线程就要陷入睡眠,再次醒来触发了OS的调度代码,代价至少为3-5us。所以让锁尽量无竞争,让所有线程“一起飞”是需要高性能的server的永恒话题。
r31906后baidu-rpc支持contention profiler,可以分析在等待锁上花费了多少时间。等待过程中线程是睡着的不会占用CPU,所以contention profiler中的时间并不是cpu时间,也不会出现在[cpu profiler](cpu_profiler.md)中。cpu profiler可以抓到特别频繁的锁(以至于花费了很多cpu),但耗时真正巨大的临界区往往不是那么频繁,而无法被cpu profiler发现。**contention profiler和cpu profiler好似互补关系,前者分析等待时间(被动),后者分析忙碌时间。**还有一类由用户基于condition或sleep发起的主动等待时间,无需分析。
r31906后brpc支持contention profiler,可以分析在等待锁上花费了多少时间。等待过程中线程是睡着的不会占用CPU,所以contention profiler中的时间并不是cpu时间,也不会出现在[cpu profiler](cpu_profiler.md)中。cpu profiler可以抓到特别频繁的锁(以至于花费了很多cpu),但耗时真正巨大的临界区往往不是那么频繁,而无法被cpu profiler发现。**contention profiler和cpu profiler好似互补关系,前者分析等待时间(被动),后者分析忙碌时间。**还有一类由用户基于condition或sleep发起的主动等待时间,无需分析。
目前contention profiler支持pthread_mutex_t(非递归)和bthread_mutex_t,开启后每秒最多采集1000个竞争锁,这个数字由参数-bvar_collector_expected_per_second控制(同时影响rpc_dump)。
......
baidu-rpc可以分析程序中的热点函数。
brpc可以分析程序中的热点函数。
# 开启方法
......@@ -6,7 +6,7 @@ baidu-rpc可以分析程序中的热点函数。
1. 这么写也开启了tcmalloc,不建议单独链接cpu profiler而不链接tcmalloc,可能越界访问导致[crash](https://github.com/gperftools/gperftools/blob/master/README#L226)**。**可能由于tcmalloc不及时归还内存,越界访问不会crash。
2. 如果tcmalloc使用frame pointer而不是libunwind回溯栈,请确保在CXXFLAGS或CFLAGS中加上`-fno-omit-frame-pointer`,否则函数间的调用关系会丢失,最后产生的图片中都是彼此独立的函数方框。
2. 定义宏BRPC_ENABLE_CPU_PROFILER。在COMAKE中加入`CXXFLAGS('-DBRPC_ENABLE_CPU_PROFILER')`
3. 如果只是baidu-rpc client或没有使用baidu-rpc,看[这里](dummy_server.md)
3. 如果只是brpc client或没有使用brpc,看[这里](dummy_server.md)
注意要关闭Server端的认证,否则可能会看到这个:
......@@ -38,7 +38,7 @@ cpu profiler的原理是在定期被调用的SIGPROF handler中采样所在线
![img](../images/echo_cpu_profiling.png)
你也可以使用[pprof](http://icode.baidu.com/repo/baidu/opensource/baidu-rpc/files/master/blob/tools/pprof)或gperftools中的pprof进行profiling。
你也可以使用[pprof](http://icode.baidu.com/repo/baidu/opensource/brpc/files/master/blob/tools/pprof)或gperftools中的pprof进行profiling。
比如`pprof --text localhost:9002 --seconds=5`的意思是统计运行在本机9002端口的server的cpu情况,时长5秒。一次运行的例子如下:
......
如果你的程序只使用了baidu-rpc的client或根本没有使用baidu-rpc,但你也想使用baidu-rpc的内置服务,只要在程序中启动一个空的server就行了,这种server我们称为**dummy server**
如果你的程序只使用了brpc的client或根本没有使用brpc,但你也想使用brpc的内置服务,只要在程序中启动一个空的server就行了,这种server我们称为**dummy server**
# 使用了baidu-rpc的client
# 使用了brpc的client
只要在程序运行目录建立dummy_server.port文件,填入一个端口号(比如8888),程序会马上在这个端口上启动一个dummy server。在浏览器中访问它的内置服务,便可看到同进程内的所有bvar。
![img](../images/dummy_server_1.png) ![img](../images/dummy_server_2.png)
![img](../images/dummy_server_3.png)
# 没有使用baidu-rpc
# 没有使用brpc
你必须手动加入dummy server。你得先查看[Getting Started](getting_started.md)如何下载和编译baidu-rpc,然后在程序入口处加入如下代码片段:
你必须手动加入dummy server。你得先查看[Getting Started](getting_started.md)如何下载和编译brpc,然后在程序入口处加入如下代码片段:
```c++
#include <brpc/server.h>
......
baidu-rpc使用[brpc::Controller](http://icode.baidu.com/repo/baidu/opensource/baidu-rpc/files/master/blob/src/brpc/controller.h)设置一次RPC的参数和获取一次RPC的结果,ErrorCode()和ErrorText()是Controller的两个方法,分别是该次RPC的错误码和错误描述,只在RPC结束后才能访问,否则结果未定义。ErrorText()由Controller的基类google::protobuf::RpcController定义,ErrorCode()则是brpc::Controller定义的。Controller还有个Failed()方法告知该次RPC是否失败,这三者的关系是:
brpc使用[brpc::Controller](http://icode.baidu.com/repo/baidu/opensource/brpc/files/master/blob/src/brpc/controller.h)设置一次RPC的参数和获取一次RPC的结果,ErrorCode()和ErrorText()是Controller的两个方法,分别是该次RPC的错误码和错误描述,只在RPC结束后才能访问,否则结果未定义。ErrorText()由Controller的基类google::protobuf::RpcController定义,ErrorCode()则是brpc::Controller定义的。Controller还有个Failed()方法告知该次RPC是否失败,这三者的关系是:
- 当Failed()为true时,ErrorCode()一定不为0,ErrorText()是非空的错误描述
- 当Failed()为false时,ErrorCode()一定为0,ErrorText()是未定义的(目前在baidu-rpc中会为空,但你最好不要依赖这个事实)
- 当Failed()为false时,ErrorCode()一定为0,ErrorText()是未定义的(目前在brpc中会为空,但你最好不要依赖这个事实)
# 标记RPC为错误
baidu-rpc的client端和server端都有Controller,都可以通过SetFailed()修改其中的ErrorCode和ErrorText。当多次调用一个Controller的SetFailed时,ErrorCode会被覆盖,ErrorText则是**添加**而不是覆盖,在client端,框架会额外加上第几次重试,在server端,框架会额外加上server的地址信息。
brpc的client端和server端都有Controller,都可以通过SetFailed()修改其中的ErrorCode和ErrorText。当多次调用一个Controller的SetFailed时,ErrorCode会被覆盖,ErrorText则是**添加**而不是覆盖,在client端,框架会额外加上第几次重试,在server端,框架会额外加上server的地址信息。
client端Controller的SetFailed()常由框架调用,比如发送request失败,接收到的response不符合要求等等。只有在进行较复杂的访问操作时用户才可能需要设置client端的错误,比如在访问后端前做额外的请求检查,发现有错误时需要把RPC设置为失败。
server端Controller的SetFailed()常由用户在服务回调中调用。当处理过程发生错误时,一般调用SetFailed()并释放资源后就return了。框架会把错误码和错误信息按交互协议填入response,client端的框架收到后会填入它那边的Controller中,从而让用户在RPC结束后取到。需要注意的是,**server端在SetFailed()时一般不需要再打条日志。**打日志是比较慢的,在繁忙的线上磁盘上,很容易出现巨大的lag。一个错误频发的client容易减慢整个server的速度而影响到其他的client,理论上来说这甚至能成为一种攻击手段。对于希望在server端看到错误信息的场景,可以打开**-log_error_text**开关(已上线服务可访问/flags/log_error_text?setvalue=true动态打开),server会在每次失败的RPC后把对应Controller的ErrorText()打印出来。
# baidu-rpc的错误码
# brpc的错误码
baidu-rpc使用的所有ErrorCode都定义在[errno.proto](http://icode.baidu.com/repo/baidu/opensource/baidu-rpc/files/master/blob/src/brpc/errno.proto)中,*SYS_*开头的来自linux系统,与/usr/include/errno.h中定义的精确一致,定义在proto中是为了跨语言。其余的是baidu-rpc自有的。
brpc使用的所有ErrorCode都定义在[errno.proto](http://icode.baidu.com/repo/baidu/opensource/brpc/files/master/blob/src/brpc/errno.proto)中,*SYS_*开头的来自linux系统,与/usr/include/errno.h中定义的精确一致,定义在proto中是为了跨语言。其余的是brpc自有的。
[berror(error_code)](http://icode.baidu.com/repo/baidu/opensource/baidu-rpc/files/master/blob/src/base/errno.h)可获得error_code的描述,berror()可获得[system errno](http://www.cplusplus.com/reference/cerrno/errno/)的描述。**ErrorText() != berror(****ErrorCode())**,ErrorText()会包含更具体的错误信息。baidu-rpc默认包含berror,你可以直接使用。
[berror(error_code)](http://icode.baidu.com/repo/baidu/opensource/brpc/files/master/blob/src/base/errno.h)可获得error_code的描述,berror()可获得[system errno](http://www.cplusplus.com/reference/cerrno/errno/)的描述。**ErrorText() != berror(****ErrorCode())**,ErrorText()会包含更具体的错误信息。brpc默认包含berror,你可以直接使用。
baidu-rpc中常见错误的打印内容列表如下:
brpc中常见错误的打印内容列表如下:
......
# 概述
类似于kylin的ExecMan, [ExecutionQueue](http://icode.baidu.com/repo/baidu/opensource/baidu-rpc/files/master/blob/src/bthread/execution_queue.h)提供了异步串行执行的功能。ExecutionQueue的相关技术最早使用在RPC中实现[多线程向同一个fd写数据](io.md#发消息). 在r31345之后加入到bthread。 ExecutionQueue 提供了如下基本功能:
类似于kylin的ExecMan, [ExecutionQueue](http://icode.baidu.com/repo/baidu/opensource/brpc/files/master/blob/src/bthread/execution_queue.h)提供了异步串行执行的功能。ExecutionQueue的相关技术最早使用在RPC中实现[多线程向同一个fd写数据](io.md#发消息). 在r31345之后加入到bthread。 ExecutionQueue 提供了如下基本功能:
- 异步有序执行: 任务在另外一个单独的线程中执行, 并且执行顺序严格和提交顺序一致.
- Multi Producer: 多个线程可以同时向一个ExecutionQueue提交任务
......
baidu-rpc使用gflags管理配置。如果你的程序也使用gflags,那么你应该已经可以修改和baidu-rpc相关的flags,你可以浏览[flags服务](http://brpc.baidu.com:8765/flags)了解每个flag的具体功能。如果你的程序还没有使用gflags,我们建议你使用,原因如下:
brpc使用gflags管理配置。如果你的程序也使用gflags,那么你应该已经可以修改和brpc相关的flags,你可以浏览[flags服务](http://brpc.baidu.com:8765/flags)了解每个flag的具体功能。如果你的程序还没有使用gflags,我们建议你使用,原因如下:
- 命令行和文件均可传入,前者方便做测试,后者适合线上运维。放在文件中的gflags可以reload。而configure只支持从文件读取配置。
- 你可以在浏览器中查看baidu-rpc服务器中所有gflags,并对其动态修改(如果允许的话)。configure不可能做到这点。
- 你可以在浏览器中查看brpc服务器中所有gflags,并对其动态修改(如果允许的话)。configure不可能做到这点。
- gflags分散在和其作用紧密关联的文件中,更好管理。而使用configure需要聚集到一个庞大的读取函数中。
如果你依赖了baidu-rpc,那么你已经依赖了third-64/gflags,如果你需要依赖某个特定版本的话,在COMAKE中加入CONFIGS('third-64/gflags@<specific-version>')。
如果你依赖了brpc,那么你已经依赖了third-64/gflags,如果你需要依赖某个特定版本的话,在COMAKE中加入CONFIGS('third-64/gflags@<specific-version>')。
# Usage of gflags
......
# BUILD
baidu-rpc prefers static linking if possible, so that deps don't have to be installed on every
brpc prefers static linking if possible, so that deps don't have to be installed on every
machine running the code.
## Ubuntu/LinuxMint/WSL
......@@ -60,7 +60,7 @@ no known issues.
Be compatible with pb 3.0 and pb 2.x with the same file:
Don't use new types in proto3 and start the proto file with `syntax="proto2";`
[tools/add_syntax_equal_proto2_to_all.sh](http://icode.baidu.com/repo/baidu/opensource/baidu-rpc/files/master/blob/tools/add_syntax_equal_proto2_to_all.sh)can add `syntax="proto2"` to all proto files without it.
[tools/add_syntax_equal_proto2_to_all.sh](http://icode.baidu.com/repo/baidu/opensource/brpc/files/master/blob/tools/add_syntax_equal_proto2_to_all.sh)can add `syntax="proto2"` to all proto files without it.
protobuf 3.3-3.4 is not tested yet.
## gflags: 2.0-2.21
......@@ -73,9 +73,9 @@ required by https.
## tcmalloc: 1.7-2.5
baidu-rpc does **not** link [tcmalloc](http://goog-perftools.sourceforge.net/doc/tcmalloc.html) by default. Users link tcmalloc on-demand.
brpc does **not** link [tcmalloc](http://goog-perftools.sourceforge.net/doc/tcmalloc.html) by default. Users link tcmalloc on-demand.
Comparing to ptmalloc embedded in glibc, tcmalloc often improves performance. However different versions of tcmalloc may behave really differently. For example, tcmalloc 2.1 may make multi-threaded examples in baidu-rpc perform significantly worse(due to a spinlock in tcmalloc) than the one using tcmalloc 1.7 and 2.5. Even different minor versions may differ. When you program behave unexpectedly, remove tcmalloc or try another version.
Comparing to ptmalloc embedded in glibc, tcmalloc often improves performance. However different versions of tcmalloc may behave really differently. For example, tcmalloc 2.1 may make multi-threaded examples in brpc perform significantly worse(due to a spinlock in tcmalloc) than the one using tcmalloc 1.7 and 2.5. Even different minor versions may differ. When you program behave unexpectedly, remove tcmalloc or try another version.
Code compiled with gcc 4.8.2 when linking to a tcmalloc compiled with earlier GCC may crash or deadlock before main(), E.g:
......@@ -91,8 +91,8 @@ When you remove tcmalloc, not only remove the linking with tcmalloc but also the
## valgrind: 3.8+
baidu-rpc detects valgrind automatically (and registers stacks of bthread). Older valgrind (say 3.2) is not supported.
brpc detects valgrind automatically (and registers stacks of bthread). Older valgrind (say 3.2) is not supported.
# Track instances
We provide a program to help you to track and monitor all baidu-rpc instances. Just run [trackme_server](http://icode.baidu.com/repo/baidu/opensource/baidu-rpc/files/master/tree/tools/trackme_server/) somewhere and launch need-to-be-tracked instances with -trackme_server=SERVER. The trackme_server will receive pings from instances periodically and print logs when it does. You can aggregate instance addresses from the log and call builtin services of the instances for further information.
We provide a program to help you to track and monitor all brpc instances. Just run [trackme_server](http://icode.baidu.com/repo/baidu/opensource/brpc/files/master/tree/tools/trackme_server/) somewhere and launch need-to-be-tracked instances with -trackme_server=SERVER. The trackme_server will receive pings from instances periodically and print logs when it does. You can aggregate instance addresses from the log and call builtin services of the instances for further information.
baidu-rpc可以分析内存是被哪些函数占据的。heap profiler的原理是每分配满一些内存就采样调用处的栈,“一些”由环境变量TCMALLOC_SAMPLE_PARAMETER控制,默认524288,即512K字节。根据栈表现出的函数调用关系汇总为我们看到的结果图。在实践中heap profiler对原程序的影响不明显。
brpc可以分析内存是被哪些函数占据的。heap profiler的原理是每分配满一些内存就采样调用处的栈,“一些”由环境变量TCMALLOC_SAMPLE_PARAMETER控制,默认524288,即512K字节。根据栈表现出的函数调用关系汇总为我们看到的结果图。在实践中heap profiler对原程序的影响不明显。
# 开启方法
......@@ -18,7 +18,7 @@ baidu-rpc可以分析内存是被哪些函数占据的。heap profiler的原理
/home/gejun/pprof/echo_server.1419559063.localhost.pprof.heap: header size >= 2**16
```
4. 如果只是baidu-rpc client或没有使用baidu-rpc,看[这里](dummy_server.md)。
4. 如果只是brpc client或没有使用brpc,看[这里](dummy_server.md)。
注意要关闭Server端的认证,否则可能会看到这个:
......@@ -54,7 +54,7 @@ WARNING: 12-26 10:01:25: * 0 [src/brpc/input_messenger.cpp:132][4294969345] Au
![img](../images/heap_profiler_3.gif)
你也可以使用pprof脚本(public/baidu-rpc/tools/pprof)在命令行中查看文本格式结果:
你也可以使用pprof脚本(public/brpc/tools/pprof)在命令行中查看文本格式结果:
```
$ tools/pprof --text db-rpc-dev00.db01:8765/pprof/heap
......@@ -101,7 +101,7 @@ Total: 38.9 MB
0.0 0.0% 100.0% 3.5 9.0% std::string::_Rep::_S_create
```
baidu-rpc还提供一个类似的growth profiler分析内存的分配去向(不考虑释放)。
brpc还提供一个类似的growth profiler分析内存的分配去向(不考虑释放)。
![img](../images/growth_profiler.png)
http client的例子见[example/http_c++](http://icode.baidu.com/repo/baidu/opensource/baidu-rpc/files/master/blob/example/http_c++/http_client.cpp)
http client的例子见[example/http_c++](http://icode.baidu.com/repo/baidu/opensource/brpc/files/master/blob/example/http_c++/http_client.cpp)
# 创建Channel
......@@ -31,7 +31,7 @@ HTTP和protobuf无关,所以除了Controller和done,CallMethod的其他参
# POST
默认的HTTP Method为GET,如果需要做POST,则需要设置。待POST的数据应置入request_attachment(),它([base::IOBuf](http://icode.baidu.com/repo/baidu/opensource/baidu-rpc/files/master/blob/src/base/iobuf.h))可以直接append std::string或char*
默认的HTTP Method为GET,如果需要做POST,则需要设置。待POST的数据应置入request_attachment(),它([base::IOBuf](http://icode.baidu.com/repo/baidu/opensource/brpc/files/master/blob/src/base/iobuf.h))可以直接append std::string或char*
```c++
brpc::Controller cntl;
......@@ -153,7 +153,7 @@ Notes on http header:
# 解压response body
出于通用性考虑且解压代码不复杂,baidu-rpc不会自动解压response body,用户可以自己做,方法如下:
出于通用性考虑且解压代码不复杂,brpc不会自动解压response body,用户可以自己做,方法如下:
```c++
#include <brpc/policy/gzip_compress.h>
......@@ -172,9 +172,9 @@ if (encoding != NULL && *encoding == "gzip") {
# 持续下载
r33796前baidu-rpc client在下载一个超长的body时,需要一直等待直到body完整才会视作RPC结束,这个过程中超长body都会存在内存中,如果body是无限长的(比如直播用的flv文件),那么内存会持续增长,直到超时。换句话说,r33796前的baidu-rpc client不适合下载大文件。
r33796前brpc client在下载一个超长的body时,需要一直等待直到body完整才会视作RPC结束,这个过程中超长body都会存在内存中,如果body是无限长的(比如直播用的flv文件),那么内存会持续增长,直到超时。换句话说,r33796前的brpc client不适合下载大文件。
r33796后baidu-rpc client支持在读取完body前就结束RPC,让用户在RPC结束后再读取持续增长的body。注意这个功能不等同于“支持http chunked mode”,baidu-rpc的http实现一直支持解析chunked mode,这里的问题是如何让用户处理超长或无限长的body,和body是否以chunked mode传输无关。
r33796后brpc client支持在读取完body前就结束RPC,让用户在RPC结束后再读取持续增长的body。注意这个功能不等同于“支持http chunked mode”,brpc的http实现一直支持解析chunked mode,这里的问题是如何让用户处理超长或无限长的body,和body是否以chunked mode传输无关。
使用方法如下:
......@@ -204,7 +204,7 @@ r33796后baidu-rpc client支持在读取完body前就结束RPC,让用户在RPC
OnReadOnePart在每读到一段数据时被调用,OnEndOfMessage在数据结束或连接断开时调用,实现前仔细阅读注释。
2. 发起RPC前设置`cntl.response_will_be_read_progressively();`
这告诉baidu-rpc在读取http response时只要读完header部分RPC就可以结束了。
这告诉brpc在读取http response时只要读完header部分RPC就可以结束了。
3. RPC结束后调用`cntl.ReadProgressiveAttachmentBy(new MyProgressiveReader);`
MyProgressiveReader就是用户实现ProgressiveReader的实例。用户可以在这个实例的OnEndOfMessage接口中删除这个实例。
......
这里特指“纯粹"的HTTP service,而不是可通过HTTP访问的pb服务。虽然用不到pb消息,但“纯粹”的HTTP Service也必须定义在.proto文件中,只是request和response都是空的结构体。这么做是确保所有的服务声明集中在proto文件中,而不是散列在.proto、程序、配置等多个地方。示例代码见[http_server.cpp](http://icode.baidu.com/repo/baidu/opensource/baidu-rpc/files/master/blob/example/http_c++/http_server.cpp)
这里特指“纯粹"的HTTP service,而不是可通过HTTP访问的pb服务。虽然用不到pb消息,但“纯粹”的HTTP Service也必须定义在.proto文件中,只是request和response都是空的结构体。这么做是确保所有的服务声明集中在proto文件中,而不是散列在.proto、程序、配置等多个地方。示例代码见[http_server.cpp](http://icode.baidu.com/repo/baidu/opensource/brpc/files/master/blob/example/http_c++/http_server.cpp)
# URL前缀为/ServiceName/MethodName
......@@ -106,7 +106,7 @@ public:
# Restful URL
r32097后,baidu-rpc支持为service中的每个方法指定一个URL。接口如下:
r32097后,brpc支持为service中的每个方法指定一个URL。接口如下:
```c++
// 如果restful_mappings不为空, service中的方法可通过指定的URL被HTTP协议访问,而不是/ServiceName/MethodName.
......@@ -154,7 +154,7 @@ if (server.AddService(&queue_svc,
}
```
上面代码中AddService的第三个参数分了三行,但实际上是一个字符串。这个字符串包含以逗号(,)分隔的三个映射关系,每个映射告诉baidu-rpc:在遇到箭头左侧的URL时调用右侧的方法。"/v1/queue/stats/*"中的星号可匹配任意字串。在r33521前星号只能加在URL最后。
上面代码中AddService的第三个参数分了三行,但实际上是一个字符串。这个字符串包含以逗号(,)分隔的三个映射关系,每个映射告诉brpc:在遇到箭头左侧的URL时调用右侧的方法。"/v1/queue/stats/*"中的星号可匹配任意字串。在r33521前星号只能加在URL最后。
关于映射规则:
......@@ -226,7 +226,7 @@ cntl->http_response().set_content_type("text/html");
## Status Code
status code是http response特有的字段,标记http请求的完成情况。请使用定义在[http_status_code.h](http://icode.baidu.com/repo/baidu/opensource/baidu-rpc/files/master/blob/src/brpc/http_status_code.h)中的enum,遵守HTTP协议。
status code是http response特有的字段,标记http请求的完成情况。请使用定义在[http_status_code.h](http://icode.baidu.com/repo/baidu/opensource/brpc/files/master/blob/src/brpc/http_status_code.h)中的enum,遵守HTTP协议。
```c++
// Get Status Code
......@@ -250,7 +250,7 @@ cntl->http_response().SetHeader("Location", "http://bj.bs.bae.baidu.com/family/i
## Query String
如上面的[HTTP headers](http_service.md#http-headers)中提到的那样,我们按约定成俗的方式来理解query string,即key1=value1&key2=value2&...。只有key而没有value也是可以的,仍然会被GetQuery查询到,只是值为空字符串,这常被用做bool型的开关。接口定义在[uri.h](http://icode.baidu.com/repo/baidu/opensource/baidu-rpc/files/master/blob/src/brpc/uri.h)
如上面的[HTTP headers](http_service.md#http-headers)中提到的那样,我们按约定成俗的方式来理解query string,即key1=value1&key2=value2&...。只有key而没有value也是可以的,仍然会被GetQuery查询到,只是值为空字符串,这常被用做bool型的开关。接口定义在[uri.h](http://icode.baidu.com/repo/baidu/opensource/brpc/files/master/blob/src/brpc/uri.h)
```c++
const std::string* time_value = cntl->http_request().uri().GetQuery("time");
......@@ -274,7 +274,7 @@ http服务常对http body进行压缩,对于文本网页可以有效减少传
# 解压request body
出于通用性考虑且解压代码不复杂,baidu-rpc不会自动解压request body,用户可以自己做,方法如下:
出于通用性考虑且解压代码不复杂,brpc不会自动解压request body,用户可以自己做,方法如下:
```c++
#include <brpc/policy/gzip_compress.h>
......@@ -340,14 +340,14 @@ bool Controller::is_ssl() const;
没有极端性能要求的产品线都有使用HTTP协议的倾向,特别是移动端产品线,所以我们很重视HTTP的实现质量,具体来说:
- 使用了node.js的[http parser](http://icode.baidu.com/repo/baidu/opensource/baidu-rpc/files/master/blob/src/brpc/details/http_parser.h)(部分来自nginx)解析http消息,这是一个轻量、优秀的实现。
- 使用了node.js的[http parser](http://icode.baidu.com/repo/baidu/opensource/brpc/files/master/blob/src/brpc/details/http_parser.h)(部分来自nginx)解析http消息,这是一个轻量、优秀的实现。
- 使用[rapidjson](https://github.com/miloyip/rapidjson)解析json,这是一个主打性能的json库,由一位腾讯专家开发。
- 在最差情况下解析http请求的时间复杂度也是O(N),其中N是请求的字节数。反过来说,如果解析代码要求http请求是完整的,那么它可能会花费O(N^2)的时间。HTTP请求普遍较大,这一点意义还是比较大的。
- 来自不同client的http消息是高度并发的,即使相当复杂的http消息也不会影响对其他客户端的响应。其他rpc和[基于单线程reactor](threading_overview.md#单线程reactor)的各类http server往往难以做到这一点。
# 持续发送
r33796前baidu-rpc server不适合发送超大或无限长的body。r33796后baidu-rpc server支持。方法如下:
r33796前brpc server不适合发送超大或无限长的body。r33796后brpc server支持。方法如下:
1. 调用Controller::CreateProgressiveAttachment()创建可持续发送的body。
`boost::intrusive_ptr<brpc::ProgressiveAttachment> pa(cntl->CreateProgressiveAttachment());`
......@@ -358,17 +358,17 @@ r33796前baidu-rpc server不适合发送超大或无限长的body。r33796后bai
# 持续接收
目前baidu-rpc server不支持在接受完http请求的header部分就调用用户的服务回调,即baidu-rpc server不适合接收超长或无限长的body。
目前brpc server不支持在接受完http请求的header部分就调用用户的服务回调,即brpc server不适合接收超长或无限长的body。
# FAQ
### Q: baidu-rpc前的nginx报了final fail (ff)
### Q: brpc前的nginx报了final fail (ff)
baidu-rpc server同端口支持多种协议,当它遇到非法HTTP请求并解析失败后,无法说这个请求一定是HTTP。在r31355之后,server会对query-string及之后出现解析错误的请求返回HTTP 400错误并关闭连接(因为有很大概率是HTTP请求),但如果是HTTP method错误,诸如出现GET、POST、HEAD等标准方法之外的东西或严重的格式错误(可能由HTTP client有bug导致),server仍会直接断开连接,导致nginx的ff。
brpc server同端口支持多种协议,当它遇到非法HTTP请求并解析失败后,无法说这个请求一定是HTTP。在r31355之后,server会对query-string及之后出现解析错误的请求返回HTTP 400错误并关闭连接(因为有很大概率是HTTP请求),但如果是HTTP method错误,诸如出现GET、POST、HEAD等标准方法之外的东西或严重的格式错误(可能由HTTP client有bug导致),server仍会直接断开连接,导致nginx的ff。
解决方案: 在使用Nginx转发流量时,可以对$HTTP_method做一下过滤,只放行允许的方法。或者干脆在proxy时设置proxy_method为指定方法,来避免ff。
### Q: baidu-rpc支持http chunked方式传输吗
### Q: brpc支持http chunked方式传输吗
支持。
......
This diff is collapsed.
baidu-rpc使用[base::IOBuf](http://icode.baidu.com/repo/baidu/opensource/baidu-rpc/files/master/blob/src/base/iobuf.h)作为存储附件或http body的数据结构,它是一种非连续零拷贝缓冲,在其他项目中得到了验证并有出色的性能。IOBuf的接口和std::string类似,但不相同。
brpc使用[base::IOBuf](http://icode.baidu.com/repo/baidu/opensource/brpc/files/master/blob/src/base/iobuf.h)作为存储附件或http body的数据结构,它是一种非连续零拷贝缓冲,在其他项目中得到了验证并有出色的性能。IOBuf的接口和std::string类似,但不相同。
如果你之前使用Kylin中的BufHandle,你将更能感受到IOBuf的便利性:前者几乎没有实现完整,直接暴露了内部结构,用户得小心翼翼地处理引用计数,极易出错。BufHandle是很多bug的诱因。
......
# 概述
LALB全称Locality-aware load balancing,是一个能把请求及时、自动地送到延时最低的下游的负载均衡算法,特别适合混合部署环境。该算法产生自DP系统,现已加入baidu-rpc!
LALB全称Locality-aware load balancing,是一个能把请求及时、自动地送到延时最低的下游的负载均衡算法,特别适合混合部署环境。该算法产生自DP系统,现已加入brpc!
LALB可以解决的问题:
......@@ -101,7 +101,7 @@ LoadBalancer是一个读远多于写的数据结构:大部分时候,所有
- 不同的读之间没有竞争,高度并发。
- 如果没有写,读总是能无竞争地获取和释放thread-local锁,一般小于25ns,对延时基本无影响。如果有写,由于其临界区极小(拿到立刻释放),读在大部分时候仍能快速地获得锁,少数时候释放锁时可能有唤醒写线程的代价。由于写本身就是少数情况,读整体上几乎不会碰到竞争锁。
完成这些功能的数据结构是[DoublyBufferedData<>](http://icode.baidu.com/repo/baidu/opensource/baidu-rpc/files/master/blob/src/base/containers/doubly_buffered_data.h),我们常简称为DBD。baidu-rpc中的所有load balancer都使用了这个数据结构,使不同线程在分流时几乎不会互斥。而其他rpc实现往往使用了全局锁,这使得它们无法写出复杂的分流算法:否则分流代码将会成为竞争热点。
完成这些功能的数据结构是[DoublyBufferedData<>](http://icode.baidu.com/repo/baidu/opensource/brpc/files/master/blob/src/base/containers/doubly_buffered_data.h),我们常简称为DBD。brpc中的所有load balancer都使用了这个数据结构,使不同线程在分流时几乎不会互斥。而其他rpc实现往往使用了全局锁,这使得它们无法写出复杂的分流算法:否则分流代码将会成为竞争热点。
这个结构有广泛的应用场景:
......@@ -112,7 +112,7 @@ LoadBalancer是一个读远多于写的数据结构:大部分时候,所有
LALB的查找过程是按权值分流,O(N)方法如下:获得所有权值的和total,产生一个间于[0, total-1]的随机数R,逐个遍历权值,直到当前权值之和不大于R,而下一个权值之和大于R。
这个方法可以工作,也好理解,但当N达到几百时性能已经很差,这儿的主要因素是cache一致性:LALB是一个基于反馈的算法,RPC结束时信息会被反馈入LALB,被遍历的数据结构也一直在被修改。这意味着前台的O(N)读必须刷新每一行cacheline。当N达到数百时,一次查找过程可能会耗时百微秒,更别提更大的N了,LALB(将)作为baidu-rpc的默认分流算法,这个性能开销是无法接受的。
这个方法可以工作,也好理解,但当N达到几百时性能已经很差,这儿的主要因素是cache一致性:LALB是一个基于反馈的算法,RPC结束时信息会被反馈入LALB,被遍历的数据结构也一直在被修改。这意味着前台的O(N)读必须刷新每一行cacheline。当N达到数百时,一次查找过程可能会耗时百微秒,更别提更大的N了,LALB(将)作为brpc的默认分流算法,这个性能开销是无法接受的。
另一个办法是用完全二叉树。每个节点记录了左子树的权值之和,这样我们就能在O(logN)时间内完成查找。当N为1024时,我们最多跳转10次内存,总耗时可控制在1微秒内,这个性能是可接受的。这个方法的难点是如何和DoublyBufferedData结合。
......
......@@ -2,10 +2,10 @@
# 名字服务
在baidu-rpc中,[NamingService](http://icode.baidu.com/repo/baidu/opensource/baidu-rpc/files/master/blob/src/brpc/naming_service.h)用于获得服务名对应的所有节点。一个直观的做法是定期调用一个函数以获取最新的节点列表。但这会带来一定的延时(定期调用的周期一般在若干秒左右),作为通用接口不太合适。特别当名字服务提供事件通知时(比如zk),这个特性没有被利用。所以我们反转了控制权:不是我们调用用户函数,而是用户在获得列表后调用我们的接口,对应[NamingServiceActions](http://icode.baidu.com/repo/baidu/opensource/baidu-rpc/files/master/blob/src/brpc/naming_service.h)。当然我们还是得启动进行这一过程的函数,对应NamingService::RunNamingService。下面以三个实现解释这套方式:
在brpc中,[NamingService](http://icode.baidu.com/repo/baidu/opensource/brpc/files/master/blob/src/brpc/naming_service.h)用于获得服务名对应的所有节点。一个直观的做法是定期调用一个函数以获取最新的节点列表。但这会带来一定的延时(定期调用的周期一般在若干秒左右),作为通用接口不太合适。特别当名字服务提供事件通知时(比如zk),这个特性没有被利用。所以我们反转了控制权:不是我们调用用户函数,而是用户在获得列表后调用我们的接口,对应[NamingServiceActions](http://icode.baidu.com/repo/baidu/opensource/brpc/files/master/blob/src/brpc/naming_service.h)。当然我们还是得启动进行这一过程的函数,对应NamingService::RunNamingService。下面以三个实现解释这套方式:
- bns:没有事件通知,所以我们只能定期去获得最新列表,默认间隔是[5秒](http://brpc.baidu.com:8765/flags/ns_access_interval)。为了简化这类定期获取的逻辑,baidu-rpc提供了[PeriodicNamingService](http://icode.baidu.com/repo/baidu/opensource/baidu-rpc/files/master/blob/src/brpc/periodic_naming_service.h) 供用户继承,用户只需要实现单次如何获取(GetServers)。获取后调用NamingServiceActions::ResetServers告诉框架。框架会对列表去重,和之前的列表比较,通知对列表有兴趣的观察者(NamingServiceWatcher)。这套逻辑会运行在独立的bthread中,即NamingServiceThread。一个NamingServiceThread可能被多个Channel共享,通过intrusive_ptr管理ownership。
- file:列表即文件。合理的方式是在文件更新后重新读取。[该实现](http://icode.baidu.com/repo/baidu/opensource/baidu-rpc/files/master/blob/src/brpc/policy/file_naming_service.cpp)使用[FileWatcher](http://icode.baidu.com/repo/baidu/opensource/baidu-rpc/files/master/blob/src/base/files/file_watcher.h)关注文件的修改时间,当文件修改后,读取并调用NamingServiceActions::ResetServers告诉框架。
- bns:没有事件通知,所以我们只能定期去获得最新列表,默认间隔是[5秒](http://brpc.baidu.com:8765/flags/ns_access_interval)。为了简化这类定期获取的逻辑,brpc提供了[PeriodicNamingService](http://icode.baidu.com/repo/baidu/opensource/brpc/files/master/blob/src/brpc/periodic_naming_service.h) 供用户继承,用户只需要实现单次如何获取(GetServers)。获取后调用NamingServiceActions::ResetServers告诉框架。框架会对列表去重,和之前的列表比较,通知对列表有兴趣的观察者(NamingServiceWatcher)。这套逻辑会运行在独立的bthread中,即NamingServiceThread。一个NamingServiceThread可能被多个Channel共享,通过intrusive_ptr管理ownership。
- file:列表即文件。合理的方式是在文件更新后重新读取。[该实现](http://icode.baidu.com/repo/baidu/opensource/brpc/files/master/blob/src/brpc/policy/file_naming_service.cpp)使用[FileWatcher](http://icode.baidu.com/repo/baidu/opensource/brpc/files/master/blob/src/base/files/file_watcher.h)关注文件的修改时间,当文件修改后,读取并调用NamingServiceActions::ResetServers告诉框架。
- list:列表就在服务名里(逗号分隔)。在读取完一次并调用NamingServiceActions::ResetServers后就退出了,因为列表再不会改变了。
如果用户需要建立这些对象仍然是不够方便的,因为总是需要一些工厂代码根据配置项建立不同的对象,鉴于此,我们把工厂类做进了框架,并且是非常方便的形式:
......@@ -20,7 +20,7 @@ list://addr1,addr2,... # use the addresses separated by comma
http://<url> # Domain Naming Service, aka DNS.
```
这套方式是可扩展的,实现了新的NamingService后在[global.cpp](http://icode.baidu.com/repo/baidu/opensource/baidu-rpc/files/master/blob/src/brpc/global.cpp)中依葫芦画瓢注册下就行了,如下图所示:
这套方式是可扩展的,实现了新的NamingService后在[global.cpp](http://icode.baidu.com/repo/baidu/opensource/brpc/files/master/blob/src/brpc/global.cpp)中依葫芦画瓢注册下就行了,如下图所示:
![img](../images/register_ns.png)
......@@ -28,7 +28,7 @@ http://<url> # Domain Naming Service, aka DNS.
# 负载均衡
baidu-rpc中[LoadBalancer](http://icode.baidu.com/repo/baidu/opensource/baidu-rpc/files/master/blob/src/brpc/load_balancer.h)从多个服务节点中选择一个节点,目前的实现见[负载均衡](client.md#负载均衡)
brpc中[LoadBalancer](http://icode.baidu.com/repo/baidu/opensource/brpc/files/master/blob/src/brpc/load_balancer.h)从多个服务节点中选择一个节点,目前的实现见[负载均衡](client.md#负载均衡)
Load balancer最重要的是如何让不同线程中的负载均衡不互斥,解决这个问题的技术是[DoublyBufferedData](lalb.md#doublybuffereddata)
......@@ -38,9 +38,9 @@ Load balancer最重要的是如何让不同线程中的负载均衡不互斥,
# 健康检查
对于那些无法连接却仍在NamingService的节点,baidu-rpc会定期连接它们,成功后对应的Socket将被”复活“,并可能被LoadBalancer选择上,这个过程就是健康检查。注意:被健康检查或在LoadBalancer中的节点一定在NamingService中。换句话说,只要一个节点不从NamingService删除,它要么是正常的(会被LoadBalancer选上),要么在做健康检查。
对于那些无法连接却仍在NamingService的节点,brpc会定期连接它们,成功后对应的Socket将被”复活“,并可能被LoadBalancer选择上,这个过程就是健康检查。注意:被健康检查或在LoadBalancer中的节点一定在NamingService中。换句话说,只要一个节点不从NamingService删除,它要么是正常的(会被LoadBalancer选上),要么在做健康检查。
传统的做法是使用一个线程做所有连接的健康检查,baidu-rpc简化了这个过程:为需要的连接动态创建一个bthread专门做健康检查(Socket::HealthCheckThread)。这个线程的生命周期被对应连接管理。具体来说,当Socket被SetFailed后,健康检查线程就可能启动(如果SocketOptions.health_check_interval为正数的话):
传统的做法是使用一个线程做所有连接的健康检查,brpc简化了这个过程:为需要的连接动态创建一个bthread专门做健康检查(Socket::HealthCheckThread)。这个线程的生命周期被对应连接管理。具体来说,当Socket被SetFailed后,健康检查线程就可能启动(如果SocketOptions.health_check_interval为正数的话):
- 健康检查线程先在确保没有其他人在使用Socket了后关闭连接。目前是通过对Socket的引用计数判断的。这个方法之所以有效在于Socket被SetFailed后就不能被Address了,所以引用计数只减不增。
- 定期连接直到远端机器被连接上,在这个过程中,如果Socket析构了,那么该线程也就随之退出了。
......
[memcached](http://memcached.org/)是常用的缓存服务,为了使用户更快捷地访问memcached并充分利用bthread的并发能力,baidu-rpc直接支持memcache协议。示例程序:[example/memcache_c++](http://icode.baidu.com/repo/baidu/opensource/baidu-rpc/files/master/tree/example/memcache_c++/)
[memcached](http://memcached.org/)是常用的缓存服务,为了使用户更快捷地访问memcached并充分利用bthread的并发能力,brpc直接支持memcache协议。示例程序:[example/memcache_c++](http://icode.baidu.com/repo/baidu/opensource/brpc/files/master/tree/example/memcache_c++/)
**注意**:baidu-rpc只支持memcache的二进制协议。memcached在1.3前只有文本协议,但在当前看来支持的意义甚微。如果你的memcached早于1.3,升级版本。
**注意**:brpc只支持memcache的二进制协议。memcached在1.3前只有文本协议,但在当前看来支持的意义甚微。如果你的memcached早于1.3,升级版本。
相比使用[libmemcached](http://libmemcached.org/libMemcached.html)(官方client)的优势有:
......@@ -9,7 +9,7 @@
- 有明确的request和response。而libmemcached是没有的,收到的消息不能直接和发出的消息对应上,用户需要自己做维护工作。
- 支持多种[连接方式](client.md#连接方式)。支持超时、backup request、取消、tracing、内置服务等一系列RPC基本福利。
当前实现充分利用了RPC的并发机制并尽量避免了拷贝。一个client可以轻松地把一个同机memcached实例(版本1.4.15)压到极限:单连接9万,多连接33万。在大部分情况下,baidu-rpc应该能充分发挥memcached的性能。
当前实现充分利用了RPC的并发机制并尽量避免了拷贝。一个client可以轻松地把一个同机memcached实例(版本1.4.15)压到极限:单连接9万,多连接33万。在大部分情况下,brpc应该能充分发挥memcached的性能。
# 访问单台memcached
......
......@@ -7,7 +7,7 @@
- 大多数结构是等长的。
这个属性可以大幅简化内存分配的过程,获得比通用malloc更稳定、快速的性能。baidu-rpc中的ResourcePool<T>和ObjectPool<T>即提供这类分配。
这个属性可以大幅简化内存分配的过程,获得比通用malloc更稳定、快速的性能。brpc中的ResourcePool<T>和ObjectPool<T>即提供这类分配。
> 这篇文章不鼓励用户使用ResourcePool<T>或ObjectPool<T>,事实上我们反对用户在程序中使用这两个类。因为”等长“的副作用是某个类型独占了一部分内存,这些内存无法再被其他类型使用,如果不加控制的滥用,反而会在程序中产生大量彼此隔离的内存分配体系,既浪费内存也不见得会有更好的性能。
......@@ -25,17 +25,17 @@
# ObjectPool<T>
这是ResourcePool<T>的变种,不返回偏移量,而直接返回对象指针。内部结构和ResourcePool类似,一些代码更加简单。对于用户来说,这就是一个多线程下的对象池,baidu-rpc里也是这么用的。比如Socket::Write中把每个待写出的请求包装为WriteRequest,这个对象就是用ObjectPool<WriteRequest>分配的。
这是ResourcePool<T>的变种,不返回偏移量,而直接返回对象指针。内部结构和ResourcePool类似,一些代码更加简单。对于用户来说,这就是一个多线程下的对象池,brpc里也是这么用的。比如Socket::Write中把每个待写出的请求包装为WriteRequest,这个对象就是用ObjectPool<WriteRequest>分配的。
# 生成bthread_t
用户期望通过创建bthread获得更高的并发度,所以创建bthread必须很快。 在目前的实现中创建一个bthread的平均耗时小于200ns。如果每次都要从头创建,是不可能这么快的。创建过程更像是从一个bthread池子中取一个实例,我们又同时需要一个id来指代一个bthread,所以这儿正是ResourcePool的用武之地。bthread在代码中被称作Task,其结构被称为TaskMeta,定义在[task_meta.h](http://icode.baidu.com/repo/baidu/opensource/baidu-rpc/files/master/blob/src/bthread/task_meta.h)中,所有的TaskMeta由ResourcePool<TaskMeta>分配。
用户期望通过创建bthread获得更高的并发度,所以创建bthread必须很快。 在目前的实现中创建一个bthread的平均耗时小于200ns。如果每次都要从头创建,是不可能这么快的。创建过程更像是从一个bthread池子中取一个实例,我们又同时需要一个id来指代一个bthread,所以这儿正是ResourcePool的用武之地。bthread在代码中被称作Task,其结构被称为TaskMeta,定义在[task_meta.h](http://icode.baidu.com/repo/baidu/opensource/brpc/files/master/blob/src/bthread/task_meta.h)中,所有的TaskMeta由ResourcePool<TaskMeta>分配。
bthread的大部分函数都需要在O(1)时间内通过bthread_t访问到TaskMeta,并且当bthread_t失效后,访问应返回NULL以让函数做出返回错误。解决方法是:bthread_t由32位的版本和32位的偏移量组成。版本解决[ABA问题](http://en.wikipedia.org/wiki/ABA_problem),偏移量由ResourcePool<TaskMeta>分配。查找时先通过偏移量获得TaskMeta,再检查版本,如果版本不匹配,说明bthread失效了。注意:这只是大概的说法,在多线程环境下,即使版本相等,bthread仍可能随时失效,在不同的bthread函数中处理方法都是不同的,有些函数会加锁,有些则能忍受版本不相等。
![img](../images/resource_pool.png)
这种id生成方式在baidu-rpc中应用广泛,baidu-rpc中的SocketId,bthread_id_t也是用类似的方法分配的。
这种id生成方式在brpc中应用广泛,brpc中的SocketId,bthread_id_t也是用类似的方法分配的。
# 栈
......
# server端多协议
baidu-rpc server在同端口支持所有的协议,大部分时候这对部署和运维更加方便。由于不同协议的格式大相径庭,严格地来说,同端口很难无二义地支持所有协议。出于解耦和可扩展性的考虑,也不太可能集中式地构建一个针对所有协议的分类器。我们的做法就是把协议归三类后逐个尝试:
brpc server在同端口支持所有的协议,大部分时候这对部署和运维更加方便。由于不同协议的格式大相径庭,严格地来说,同端口很难无二义地支持所有协议。出于解耦和可扩展性的考虑,也不太可能集中式地构建一个针对所有协议的分类器。我们的做法就是把协议归三类后逐个尝试:
- 第一类协议:标记或特殊字符在最前面,比如[标准协议](http://gollum.baidu.com/ProtobufRPC)[hulu协议](http://wiki.babel.baidu.com/twiki/bin/view/Com/Main/Hulu_rpc_protocols)的前4个字符分别分别是PRPC和HULU,解析代码只需要检查前4个字节就可以知道协议是否匹配,最先尝试这类协议。这些协议在同一个连接上也可以共存。
- 第二类协议:有较为复杂的语法,没有固定的协议标记或特殊字符,可能在解析一段输入后才能判断是否匹配,目前此类协议只有http。
......@@ -14,13 +14,13 @@ baidu-rpc server在同端口支持所有的协议,大部分时候这对部署
# 支持新协议
baidu-rpc就是设计为可随时扩展新协议的,步骤如下:
brpc就是设计为可随时扩展新协议的,步骤如下:
> 以nshead开头的协议有统一支持,看[这里](nshead_service.md)。
## 增加ProtocolType
[options.proto](http://icode.baidu.com/repo/baidu/opensource/baidu-rpc/files/master/blob/src/brpc/options.proto)的ProtocolType中增加新协议类型,如果你需要的话可以联系我们增加,以确保不会和其他人的需求重合。
[options.proto](http://icode.baidu.com/repo/baidu/opensource/brpc/files/master/blob/src/brpc/options.proto)的ProtocolType中增加新协议类型,如果你需要的话可以联系我们增加,以确保不会和其他人的需求重合。
目前的ProtocolType(16年底):
```c++
......@@ -34,7 +34,7 @@ enum ProtocolType {
PROTOCOL_HTTP = 6;
PROTOCOL_PUBLIC_PBRPC = 7;
PROTOCOL_NOVA_PBRPC = 8;
PROTOCOL_NSHEAD_CLIENT = 9; // implemented in baidu-rpc-ub
PROTOCOL_NSHEAD_CLIENT = 9; // implemented in brpc-ub
PROTOCOL_NSHEAD = 10;
PROTOCOL_HADOOP_RPC = 11;
PROTOCOL_HADOOP_SERVER_RPC = 12;
......@@ -52,7 +52,7 @@ enum ProtocolType {
```
## 实现回调
均定义在struct Protocol中,该结构定义在[protocol.h](http://icode.baidu.com/repo/baidu/opensource/baidu-rpc/files/master/blob/src/brpc/protocol.h)。其中的parse必须实现,除此之外server端至少要实现process_request,client端至少要实现serialize_request,pack_request,process_response;
均定义在struct Protocol中,该结构定义在[protocol.h](http://icode.baidu.com/repo/baidu/opensource/brpc/files/master/blob/src/brpc/protocol.h)。其中的parse必须实现,除此之外server端至少要实现process_request,client端至少要实现serialize_request,pack_request,process_response;
实现协议回调还是比较困难的,这块的代码不会像供普通用户使用的那样,有较好的提示和保护,你得先靠自己搞清楚其他协议中的类似代码,然后再动手,最后发给我们做code review。
......@@ -137,7 +137,7 @@ typedef const std::string& (*GetMethodName)(const google::protobuf::MethodDescri
## 注册到全局
实现好的协议要调用RegisterProtocol[注册到全局](http://icode.baidu.com/repo/baidu/opensource/baidu-rpc/files/master/blob/src/brpc/global.cpp),以便baidu-rpc发现。就像这样:
实现好的协议要调用RegisterProtocol[注册到全局](http://icode.baidu.com/repo/baidu/opensource/brpc/files/master/blob/src/brpc/global.cpp),以便brpc发现。就像这样:
```c++
Protocol http_protocol = { ParseHttpMessage,
SerializeHttpRequest, PackHttpRequest,
......@@ -155,7 +155,7 @@ if (RegisterProtocol(PROTOCOL_HTTP, http_protocol) != 0) {
为了进一步简化protocol的实现逻辑,r34386是一个不兼容改动,主要集中在下面几点:
- ProcessXXX必须在处理结束时调用msg_base->Destroy()。在之前的版本中,这是由框架完成的。这个改动帮助我们隐藏处理EOF的代码(很晦涩),还可以在未来支持更异步的处理(退出ProcessXXX不意味着处理结束)。为了确保所有的退出分支都会调用msg_base->Destroy(),可以使用定义在[destroying_ptr.h](http://icode.baidu.com/repo/baidu/opensource/baidu-rpc/files/master/blob/src/brpc/destroying_ptr.h)中的DestroyingPtr<>,可能像这样:
- ProcessXXX必须在处理结束时调用msg_base->Destroy()。在之前的版本中,这是由框架完成的。这个改动帮助我们隐藏处理EOF的代码(很晦涩),还可以在未来支持更异步的处理(退出ProcessXXX不意味着处理结束)。为了确保所有的退出分支都会调用msg_base->Destroy(),可以使用定义在[destroying_ptr.h](http://icode.baidu.com/repo/baidu/opensource/brpc/files/master/blob/src/brpc/destroying_ptr.h)中的DestroyingPtr<>,可能像这样:
```c++
void ProcessXXXRequest(InputMessageBase* msg_base) {
DestroyingPtr<MostCommonMessage> msg(static_cast<MostCommonMessage*>(msg_base));
......@@ -163,7 +163,7 @@ void ProcessXXXRequest(InputMessageBase* msg_base) {
}
```
- 具体请参考[其他协议](http://icode.baidu.com/repo/baidu/opensource/baidu-rpc/files/master/blob/src/brpc/policy/baidu_rpc_protocol.cpp)的实现。
- 具体请参考[其他协议](http://icode.baidu.com/repo/baidu/opensource/brpc/files/master/blob/src/brpc/policy/baidu_rpc_protocol.cpp)的实现。
- InputMessageBase::socket_id()被移除,而通过socket()可以直接访问到对应Socket的指针。ProcessXXX函数中Address Socket的代码可以移除。
ProcessXXXRequest开头的修改一般是这样:
```c++
......
ub是百度内广泛使用的老RPC框架,在迁移ub服务时不可避免地需要[访问ub-server](ub_client.md)或被ub-client访问。ub使用的协议种类很多,但都以nshead作为二进制包的头部,这类服务在baidu-rpc中统称为**“nshead service”**
ub是百度内广泛使用的老RPC框架,在迁移ub服务时不可避免地需要[访问ub-server](ub_client.md)或被ub-client访问。ub使用的协议种类很多,但都以nshead作为二进制包的头部,这类服务在brpc中统称为**“nshead service”**
nshead后大都使用mcpack/compack作为序列化格式,注意这不是“协议”。"协议"除了序列化格式,还涉及到各种特殊字段的定义,一种序列化格式可能会衍生出很多协议。ub没有定义标准协议,所以即使都使用mcpack/compack,产品线的通信协议也是五花八门,无法互通。鉴于此,我们提供了一套接口,让用户能够灵活的处理自己产品线的协议,同时享受baidu-rpc提供的builtin services等一系列框架福利。
nshead后大都使用mcpack/compack作为序列化格式,注意这不是“协议”。"协议"除了序列化格式,还涉及到各种特殊字段的定义,一种序列化格式可能会衍生出很多协议。ub没有定义标准协议,所以即使都使用mcpack/compack,产品线的通信协议也是五花八门,无法互通。鉴于此,我们提供了一套接口,让用户能够灵活的处理自己产品线的协议,同时享受brpc提供的builtin services等一系列框架福利。
# 使用ubrpc的服务
ubrpc协议的基本形式是nshead+compack或mcpack2,但compack或mcpack2中包含一些RPC过程需要的特殊字段。
在baidu-rpc r31687之后,用protobuf写的服务可以通过mcpack2pb被ubrpc client访问,步骤如下:
在brpc r31687之后,用protobuf写的服务可以通过mcpack2pb被ubrpc client访问,步骤如下:
## 把idl文件转化为proto文件
使用脚本[idl2proto](http://icode.baidu.com/repo/baidu/opensource/baidu-rpc/files/master/blob/tools/idl2proto)把idl文件自动转化为proto文件,下面是转化后的proto文件。
使用脚本[idl2proto](http://icode.baidu.com/repo/baidu/opensource/brpc/files/master/blob/tools/idl2proto)把idl文件自动转化为proto文件,下面是转化后的proto文件。
```protobuf
// Converted from echo.idl by public/mcpack2pb/idl2proto
......@@ -124,11 +124,11 @@ brpc::ServerOptions option;
option.nshead_service = new brpc::policy::UbrpcCompackAdaptor; // mcpack2用UbrpcMcpack2Adaptor
```
例子见[example/echo_c++_ubrpc_compack](http://icode.baidu.com/repo/baidu/opensource/baidu-rpc/files/master/blob/example/echo_c++_ubrpc_compack/)
例子见[example/echo_c++_ubrpc_compack](http://icode.baidu.com/repo/baidu/opensource/brpc/files/master/blob/example/echo_c++_ubrpc_compack/)
# 使用nshead+blob的服务
[NsheadService](http://icode.baidu.com/repo/baidu/opensource/baidu-rpc/files/master/blob/src/brpc/nshead_service.h)是baidu-rpc中所有处理nshead打头协议的基类,实现好的NsheadService实例得赋值给ServerOptions.nshead_service才能发挥作用。不赋值的话,默认是NULL,代表不支持任何nshead开头的协议,这个server被nshead开头的数据包访问时会报错。明显地,**一个Server只能处理一种以nshead开头的协议。**
[NsheadService](http://icode.baidu.com/repo/baidu/opensource/brpc/files/master/blob/src/brpc/nshead_service.h)是brpc中所有处理nshead打头协议的基类,实现好的NsheadService实例得赋值给ServerOptions.nshead_service才能发挥作用。不赋值的话,默认是NULL,代表不支持任何nshead开头的协议,这个server被nshead开头的数据包访问时会报错。明显地,**一个Server只能处理一种以nshead开头的协议。**
NsheadService的接口如下,基本上用户只需要实现`ProcessNsheadRequest`这个函数。
......@@ -139,7 +139,7 @@ struct NsheadMessage {
base::IOBuf body;
};
// 实现这个类并复制给ServerOptions.nshead_service来让baidu-rpc处理nshead请求。
// 实现这个类并复制给ServerOptions.nshead_service来让brpc处理nshead请求。
class NsheadService : public Describable {
public:
NsheadService();
......@@ -164,11 +164,11 @@ public:
};
```
完整的example在[example/nshead_extension_c++](http://icode.baidu.com/repo/baidu/opensource/baidu-rpc/files/master/tree/example/nshead_extension_c++/)
完整的example在[example/nshead_extension_c++](http://icode.baidu.com/repo/baidu/opensource/brpc/files/master/tree/example/nshead_extension_c++/)
# 使用nshead+mcpack/compack/idl的服务
idl是mcpack/compack的前端,用户只要在idl文件中描述schema,就可以生成一些C++结构体,这些结构体可以打包为mcpack/compack。如果你的服务仍在大量地使用idl生成的结构体,且短期内难以修改,同时想要使用baidu-rpc提升性能和开发效率的话,可以实现[NsheadService](http://icode.baidu.com/repo/baidu/opensource/baidu-rpc/files/master/blob/src/brpc/nshead_service.h),其接口接受nshead + 二进制包为request,用户填写自己的处理逻辑,最后的response也是nshead+二进制包。流程与protobuf方法保持一致,但过程中不涉及任何protobuf的序列化和反序列化,用户可以自由地理解nshead后的二进制包,包括用idl加载mcpack/compack数据包。
idl是mcpack/compack的前端,用户只要在idl文件中描述schema,就可以生成一些C++结构体,这些结构体可以打包为mcpack/compack。如果你的服务仍在大量地使用idl生成的结构体,且短期内难以修改,同时想要使用brpc提升性能和开发效率的话,可以实现[NsheadService](http://icode.baidu.com/repo/baidu/opensource/brpc/files/master/blob/src/brpc/nshead_service.h),其接口接受nshead + 二进制包为request,用户填写自己的处理逻辑,最后的response也是nshead+二进制包。流程与protobuf方法保持一致,但过程中不涉及任何protobuf的序列化和反序列化,用户可以自由地理解nshead后的二进制包,包括用idl加载mcpack/compack数据包。
不过,你应当充分意识到这么改造的坏处:
......@@ -178,7 +178,7 @@ idl是mcpack/compack的前端,用户只要在idl文件中描述schema,就可
# 使用nshead+protobuf的服务
如果你的协议已经使用了nshead + protobuf,或者你想把你的协议适配为protobuf格式,那可以使用另一种模式:实现[NsheadPbServiceAdaptor](http://icode.baidu.com/repo/baidu/opensource/baidu-rpc/files/master/blob/src/brpc/nshead_pb_service_adaptor.h)(NsheadService的子类)。
如果你的协议已经使用了nshead + protobuf,或者你想把你的协议适配为protobuf格式,那可以使用另一种模式:实现[NsheadPbServiceAdaptor](http://icode.baidu.com/repo/baidu/opensource/brpc/files/master/blob/src/brpc/nshead_pb_service_adaptor.h)(NsheadService的子类)。
工作步骤:
......@@ -186,7 +186,7 @@ idl是mcpack/compack的前端,用户只要在idl文件中描述schema,就可
- Call ParseRequestFromIOBuf() to convert the body after nshead header to pb request, then call the pb method.
- When user calls server's done to end the RPC, SerializeResponseToIOBuf() is called to convert pb response to binary data that will be appended after nshead header and sent back to client.
这样做的好处是,这个服务还可以被其他使用protobuf的协议访问,比如标准协议,hulu协议,sofa协议等等。NsheadPbServiceAdaptor的主要接口如下。完整的example在[这里](http://icode.baidu.com/repo/baidu/opensource/baidu-rpc/files/master/tree/example/nshead_pb_extension_c++/)
这样做的好处是,这个服务还可以被其他使用protobuf的协议访问,比如标准协议,hulu协议,sofa协议等等。NsheadPbServiceAdaptor的主要接口如下。完整的example在[这里](http://icode.baidu.com/repo/baidu/opensource/brpc/files/master/tree/example/nshead_pb_extension_c++/)
```c++
class NsheadPbServiceAdaptor : public NsheadService {
......
[redis](http://redis.io/)是最近几年比较火的缓存服务,相比memcached在server端提供了更多的数据结构和操作方法,简化了用户的开发工作,在百度内有比较广泛的应用。为了使用户更快捷地访问redis并充分利用bthread的并发能力,baidu-rpc直接支持redis协议。示例程序:[example/redis_c++](http://icode.baidu.com/repo/baidu/opensource/baidu-rpc/files/master/tree/example/redis_c++/)
[redis](http://redis.io/)是最近几年比较火的缓存服务,相比memcached在server端提供了更多的数据结构和操作方法,简化了用户的开发工作,在百度内有比较广泛的应用。为了使用户更快捷地访问redis并充分利用bthread的并发能力,brpc直接支持redis协议。示例程序:[example/redis_c++](http://icode.baidu.com/repo/baidu/opensource/brpc/files/master/tree/example/redis_c++/)
相比使用[hiredis](https://github.com/redis/hiredis)(官方client)的优势有:
......@@ -7,7 +7,7 @@
- 支持多种[连接方式](client.md#连接方式)。支持超时、backup request、取消、tracing、内置服务等一系列RPC基本福利。
- 一个进程和一个redis-server只有一个连接。多个线程同时访问一个redis-server时更高效(见[性能](#性能))。无论reply的组成多复杂,内存都会连续成块地分配,并支持短串优化(SSO)。
像http一样,baidu-rpc保证在最差情况下解析redis reply的时间复杂度也是O(N),N是reply的字节数,而不是O(N^2)。当reply是个较大的数组时,这是比较重要的。
像http一样,brpc保证在最差情况下解析redis reply的时间复杂度也是O(N),N是reply的字节数,而不是O(N^2)。当reply是个较大的数组时,这是比较重要的。
r32037后加上[-redis_verbose](#查看发出的请求和收到的回复)后会在stderr上打印出所有的redis request和response供调试。
......@@ -103,7 +103,7 @@ CHECK_EQ(-10, response.reply(3).integer());
# RedisRequest
一个[RedisRequest](http://icode.baidu.com/repo/baidu/opensource/baidu-rpc/files/master/blob/src/brpc/redis.h)可包含多个Command,调用AddCommand*增加命令,成功返回true,失败返回false并会打印调用处的栈。
一个[RedisRequest](http://icode.baidu.com/repo/baidu/opensource/brpc/files/master/blob/src/brpc/redis.h)可包含多个Command,调用AddCommand*增加命令,成功返回true,失败返回false并会打印调用处的栈。
```c++
bool AddCommand(const char* fmt, ...);
......@@ -123,7 +123,7 @@ command_size()可获得(成功)加入的命令个数。
# RedisResponse
[RedisResponse](http://icode.baidu.com/repo/baidu/opensource/baidu-rpc/files/master/blob/src/brpc/redis.h)可能包含一个或多个[RedisReply](http://icode.baidu.com/repo/baidu/opensource/baidu-rpc/files/master/blob/src/brpc/redis_reply.h),reply_size()可获得reply的个数,reply(i)可获得第i个reply的引用(从0计数)。注意在hiredis中,如果请求包含了N个command,获取结果也要调用N次redisGetReply。但在baidu-rpc中这是不必要的,RedisResponse已经包含了N个reply,通过reply(i)获取就行了。只要RPC成功,response.reply_size()应与request.command_size()相等,除非redis-server有bug(redis-server工作的基本前提就是response和request按序一一对应)
[RedisResponse](http://icode.baidu.com/repo/baidu/opensource/brpc/files/master/blob/src/brpc/redis.h)可能包含一个或多个[RedisReply](http://icode.baidu.com/repo/baidu/opensource/brpc/files/master/blob/src/brpc/redis_reply.h),reply_size()可获得reply的个数,reply(i)可获得第i个reply的引用(从0计数)。注意在hiredis中,如果请求包含了N个command,获取结果也要调用N次redisGetReply。但在brpc中这是不必要的,RedisResponse已经包含了N个reply,通过reply(i)获取就行了。只要RPC成功,response.reply_size()应与request.command_size()相等,除非redis-server有bug(redis-server工作的基本前提就是response和request按序一一对应)
每个reply可能是:
......@@ -142,7 +142,7 @@ response中的所有reply的ownership属于response。当response析构时,rep
# 访问redis集群
暂时请沿用常见的[twemproxy](https://github.com/twitter/twemproxy)方案,像访问单点一样访问proxy。如果你之前用hiredis访问BDRP(使用了twemproxy),那把client更换成baidu-rpc就行了。通过client(一致性哈希)直接访问redis集群虽然能降低延时,但同时也(可能)意味着无法直接利用BDRP的托管服务,这一块还不是很确定。
暂时请沿用常见的[twemproxy](https://github.com/twitter/twemproxy)方案,像访问单点一样访问proxy。如果你之前用hiredis访问BDRP(使用了twemproxy),那把client更换成brpc就行了。通过client(一致性哈希)直接访问redis集群虽然能降低延时,但同时也(可能)意味着无法直接利用BDRP的托管服务,这一块还不是很确定。
如果你自己维护了redis集群,和memcache类似,应该是可以用一致性哈希访问的。但每个RedisRequest应只包含一个command或确保所有的command始终落在同一台server。如果request包含了多个command,在当前实现下总会送向同一个server。比方说一个request中包含了多个Get,而对应的key分布在多个server上,那么结果就肯定不对了,这个情况下你必须把一个request分开为多个。
......@@ -180,7 +180,7 @@ TRACE: 02-13 19:43:49: * 0 client.cpp:180] Accessing redis server at qps=41167
TRACE: 02-13 19:43:50: * 0 client.cpp:180] Accessing redis server at qps=412583 latency=482
```
200个线程后qps基本到极限了。这里的极限qps比hiredis高很多,原因在于baidu-rpc默认以单链接访问redis-server,多个线程在写出时会[以wait-free的方式合并](io.md#发消息),从而让redis-server就像被批量访问一样,每次都能从那个连接中读出一批请求,从而获得远高于非批量时的qps。下面通过连接池访问redis-server时qps的大幅回落是另外一个证明。
200个线程后qps基本到极限了。这里的极限qps比hiredis高很多,原因在于brpc默认以单链接访问redis-server,多个线程在写出时会[以wait-free的方式合并](io.md#发消息),从而让redis-server就像被批量访问一样,每次都能从那个连接中读出一批请求,从而获得远高于非批量时的qps。下面通过连接池访问redis-server时qps的大幅回落是另外一个证明。
分别使用1,50,200个bthread一次发送10个同步压测同机redis-server,延时单位均为微秒。
......@@ -225,7 +225,7 @@ TRACE: 02-13 18:07:42: * 0 client.cpp:180] Accessing redis server at qps=75238
# Command Line Interface
example/redis_c++/redis_cli是一个类似于官方CLI的命令行工具,以展示baidu-rpc对redis协议的处理能力。当使用baidu-rpc访问redis-server出现不符合预期的行为时,也可以使用这个CLI进行交互式的调试。
example/redis_c++/redis_cli是一个类似于官方CLI的命令行工具,以展示brpc对redis协议的处理能力。当使用brpc访问redis-server出现不符合预期的行为时,也可以使用这个CLI进行交互式的调试。
```
$ ./redis_cli
......@@ -236,7 +236,7 @@ $ ./redis_cli
/_.___/\__,_/_/\__,_/\__,_/ /_/ / .___/\___/
/_/
This command-line tool mimics the look-n-feel of official redis-cli, as a
demostration of baidu-rpc's capability of talking to redis server. The
demostration of brpc's capability of talking to redis server. The
output and behavior is not exactly same with the official one.
redis 127.0.0.1:6379> mset key1 foo key2 bar key3 17
......@@ -245,10 +245,10 @@ redis 127.0.0.1:6379> mget key1 key2 key3
["foo", "bar", "17"]
redis 127.0.0.1:6379> incrby key3 10
(integer) 27
redis 127.0.0.1:6379> client setname baidu-rpc-cli
redis 127.0.0.1:6379> client setname brpc-cli
OK
redis 127.0.0.1:6379> client getname
"baidu-rpc-cli"
"brpc-cli"
```
和官方CLI类似,redis_cli <command>也可以直接运行命令,-server参数可以指定redis-server的地址。
......@@ -8,7 +8,7 @@ rpc_press无需写代码就压测各种rpc server,目前支持的协议有:
# 获取工具
先按照[Getting Started](getting_started.md)编译好baidu-rpc,再去tools/rpc_press编译。
先按照[Getting Started](getting_started.md)编译好brpc,再去tools/rpc_press编译。
在CentOS 6.3上如果出现找不到libssl.so.4的错误,可执行`ln -s /usr/lib64/libssl.so.6 libssl.so.4临时解决`
......@@ -34,7 +34,7 @@ json也可以写在文件中,假如./input.json包含了上述两个请求,-
- -inc: 包含被import的proto文件的路径。rpc_press默认支持import目录下的其他proto文件,但如果proto文件在其他目录,就要通过这个参数指定,多个路径用分号(;)分隔。
- -lb_policy: 指定负载均衡算法,默认为空,可选项为: rr random la c_murmurhash c_md5,具体见[负载均衡](client.md#负载均衡)
- -timeout_ms: 设定超时,单位是毫秒(milliseconds),默认是1000(1秒)
- -max_retry: 最大的重试次数,默认是3, 一般无需修改. baidu-rpc的重试行为具体请见[这里](client.md#重试).
- -max_retry: 最大的重试次数,默认是3, 一般无需修改. brpc的重试行为具体请见[这里](client.md#重试).
- -protocol: 连接server使用的协议,可选项见[协议](client.md#协议), 默认是baidu_std(标准协议)
- -connection_type: 连接方式,可选项为: single pooled short,具体见[连接方式](client.md#连接方式)。默认会根据协议自动选择,无需指定.
- -output: 如果不为空,response会转为json并写入这个文件,默认为空。
......@@ -60,7 +60,7 @@ json也可以写在文件中,假如./input.json包含了上述两个请求,-
rpc_press启动后会默认在8888端口启动一个dummy server,用于观察rpc_press本身的运行情况:
```
./rpc_press -proto=latest_baidu_rpc/public/baidu-rpc/example/multi_threaded_echo_c++/echo.proto -service=example.EchoService -method=Echo -server=0.0.0.0:8002 -input=./input.json -duration=0 -qps=100
./rpc_press -proto=echo.proto -service=example.EchoService -method=Echo -server=0.0.0.0:8002 -input=./input.json -duration=0 -qps=100
TRACE: 01-30 16:10:04: * 0 src/brpc/server.cpp:733] Server[dummy_servers] is serving on port=8888.
TRACE: 01-30 16:10:04: * 0 src/brpc/server.cpp:742] Check out http://db-rpc-dev00.db01.baidu.com:8888 in your web browser.</code>
```
......
r31658后,baidu-rpc能随机地把一部分请求写入一些文件中,并通过rpc_replay工具回放。目前支持的协议有:baidu_std, hulu_pbrpc, sofa_pbrpc。
r31658后,brpc能随机地把一部分请求写入一些文件中,并通过rpc_replay工具回放。目前支持的协议有:baidu_std, hulu_pbrpc, sofa_pbrpc。
# 获取工具
先按照[Getting Started](getting_started.md)编译好baidu-rpc,再去tools/rpc_replay编译。
先按照[Getting Started](getting_started.md)编译好brpc,再去tools/rpc_replay编译。
在CentOS 6.3上如果出现找不到libssl.so.4的错误,可执行`ln -s /usr/lib64/libssl.so.6 libssl.so.4临时解决`
# 采样
baidu-rpc通过如下flags打开和控制如何保存请求,包含(R)后缀的flag都可以动态设置。
brpc通过如下flags打开和控制如何保存请求,包含(R)后缀的flag都可以动态设置。
![img](../images/rpc_replay_1.png)
......@@ -16,12 +16,12 @@ baidu-rpc通过如下flags打开和控制如何保存请求,包含(R)后缀的
参数说明:
- -rpc_dump是主开关,关闭时其他以rpc_dump开头的flag都无效。当打开-rpc_dump后,baidu-rpc会以一定概率采集请求,如果服务的qps很高,baidu-rpc会调节采样比例,使得每秒钟采样的请求个数不超过-bvar_collector_expected_per_second对应的值。这个值在目前同样影响rpcz和contention profiler,一般不用改动,以后会对不同的应用独立开来。
- -rpc_dump是主开关,关闭时其他以rpc_dump开头的flag都无效。当打开-rpc_dump后,brpc会以一定概率采集请求,如果服务的qps很高,brpc会调节采样比例,使得每秒钟采样的请求个数不超过-bvar_collector_expected_per_second对应的值。这个值在目前同样影响rpcz和contention profiler,一般不用改动,以后会对不同的应用独立开来。
- -rpc_dump_dir:设置存放被dump请求的目录
- -rpc_dump_max_files: 设置目录下的最大文件数,当超过限制时,老文件会被删除以腾出空间。
- -rpc_dump_max_requests_in_one_file:一个文件内的最大请求数,超过后写新文件。
baidu-rpc通过一个[bvar::Collector](http://icode.baidu.com/repo/baidu/opensource/baidu-rpc/files/master/blob/src/bvar/collector.h)来汇总来自不同线程的被采样请求,不同线程之间没有竞争,开销很小。
brpc通过一个[bvar::Collector](http://icode.baidu.com/repo/baidu/opensource/brpc/files/master/blob/src/bvar/collector.h)来汇总来自不同线程的被采样请求,不同线程之间没有竞争,开销很小。
写出的内容依次存放在rpc_dump_dir目录下的多个文件内,这个目录默认在./rpc_dump_<app>,其中<app>是程序名。不同程序在同一个目录下同时采样时会写入不同的目录。如果程序启动时rpc_dump_dir已经存在了,目录将被清空。目录中的每个文件以requests.yyyymmdd_hhmmss_uuuuus命名,以保证按时间有序方便查找,比如:
......@@ -43,7 +43,7 @@ serialized request (body_size - meta_size bytes, including attachment)
> 一个文件可能包含多种协议的请求,如果server被多种协议访问的话。回放时被请求的server也将收到不同协议的请求。
baidu-rpc提供了[SampleIterator](http://icode.baidu.com/repo/baidu/opensource/baidu-rpc/files/master/blob/src/brpc/rpc_dump.h)从一个采样目录下的所有文件中依次读取所有的被采样请求,用户可根据需求把serialized request反序列化为protobuf请求,做一些二次开发。
brpc提供了[SampleIterator](http://icode.baidu.com/repo/baidu/opensource/brpc/files/master/blob/src/brpc/rpc_dump.h)从一个采样目录下的所有文件中依次读取所有的被采样请求,用户可根据需求把serialized request反序列化为protobuf请求,做一些二次开发。
```c++
#include <brpc/rpc_dump.h>
......@@ -59,7 +59,7 @@ for (SampleRequest* req = it->Next(); req != NULL; req = it->Next()) {
# 回放
baidu-rpc在[tools/rpc_replay](http://icode.baidu.com/repo/baidu/opensource/baidu-rpc/files/master/tree/tools/rpc_replay/)提供了默认的回放工具。运行方式如下:
brpc在[tools/rpc_replay](http://icode.baidu.com/repo/baidu/opensource/brpc/files/master/tree/tools/rpc_replay/)提供了默认的回放工具。运行方式如下:
![img](../images/rpc_replay_4.png)
......
......@@ -2,7 +2,7 @@ rpc_view可以查看端口不在8000-8999的server的内置服务。之前如果
# 获取工具
先按照[Getting Started](getting_started.md)编译好baidu-rpc,再去tools/rpc_view编译。
先按照[Getting Started](getting_started.md)编译好brpc,再去tools/rpc_view编译。
在CentOS 6.3上如果出现找不到libssl.so.4的错误,可执行`ln -s /usr/lib64/libssl.so.6 libssl.so.4临时解决`
......
用户能通过/rpcz看到最近请求的详细信息,并可以插入注释(annotation),不同于tracing system(如[dapper](http://static.googleusercontent.com/media/research.google.com/en//pubs/archive/36356.pdf))以全局视角看到整体系统的延时分布,rpcz更多是一个调试工具,虽然角色有所不同,但在baidu-rpc中rpcz和tracing的数据来源是一样的。当每秒请求数小于1万时,rpcz会记录所有的请求,超过1万时,rpcz会随机忽略一些请求把采样数控制在1万左右。rpcz可以淘汰时间窗口之前的数据,通过-span_keeping_seconds选项设置,默认1小时。[一个长期运行的例子](http://brpc.baidu.com:8765/rpcz)
用户能通过/rpcz看到最近请求的详细信息,并可以插入注释(annotation),不同于tracing system(如[dapper](http://static.googleusercontent.com/media/research.google.com/en//pubs/archive/36356.pdf))以全局视角看到整体系统的延时分布,rpcz更多是一个调试工具,虽然角色有所不同,但在brpc中rpcz和tracing的数据来源是一样的。当每秒请求数小于1万时,rpcz会记录所有的请求,超过1万时,rpcz会随机忽略一些请求把采样数控制在1万左右。rpcz可以淘汰时间窗口之前的数据,通过-span_keeping_seconds选项设置,默认1小时。[一个长期运行的例子](http://brpc.baidu.com:8765/rpcz)
关于开销:我们的实现完全规避了线程竞争,开销极小,在qps 30万的测试场景中,观察不到明显的性能变化,对大部分应用而言应该是“free”的。即使采集了几千万条请求,rpcz也不会增加很多内存,一般在50兆以内。rpcz会占用一些磁盘空间(就像日志一样),如果设定为存一个小时的数据,一般在几百兆左右。
......@@ -20,7 +20,7 @@
![img](../images/rpcz_5.png)
如果只是baidu-rpc client或没有使用baidu-rpc,看[这里](dummy_server.md)
如果只是brpc client或没有使用brpc,看[这里](dummy_server.md)
## 数据展现
......@@ -48,7 +48,7 @@
## Annotation
只要你使用了baidu-rpc,就可以使用[TRACEPRINTF](http://icode.baidu.com/repo/baidu/opensource/baidu-rpc/files/master/blob/src/brpc/traceprintf.h)打印内容到事件流中,比如:
只要你使用了brpc,就可以使用[TRACEPRINTF](http://icode.baidu.com/repo/baidu/opensource/brpc/files/master/blob/src/brpc/traceprintf.h)打印内容到事件流中,比如:
```c++
TRACEPRINTF("Hello rpcz %d", 123);
......
This diff is collapsed.
......@@ -158,6 +158,6 @@ void search() {
你可以在子函数中继续这个过程,增加更多bvar,并比对不同的分布,最后定位来源。
### 只使用了baidu-rpc client
### 只使用了brpc client
得打开dummy server提供内置服务,方法见[这里](dummy_server.md)
......@@ -18,7 +18,7 @@
- **processing**: 正在处理的请求个数。如果持续不为0(特别是在压力归0后),应考虑程序是否有bug。
用户可通过让对应Service实现[brpc::Describable](http://icode.baidu.com/repo/baidu/opensource/baidu-rpc/files/master/blob/src/brpc/describable.h)自定义在/status页面上的描述.
用户可通过让对应Service实现[brpc::Describable](http://icode.baidu.com/repo/baidu/opensource/brpc/files/master/blob/src/brpc/describable.h)自定义在/status页面上的描述.
```c++
class MyService : public XXXService, public brpc::Describable {
......
......@@ -321,4 +321,4 @@ TEST_F(StreamingLogTest, log_at) {
定义在base/comlog_sink.h中,把日志打印入comlog,主要用于线上系统,用法见[SYNOPSIS](#SYNOPSIS)一段。
使用ComlogSink的streaming log可以和com_writelog, ul_writelog混用。你并不需要把程序中所有日志都换成streaming log。
\ No newline at end of file
使用ComlogSink的streaming log可以和com_writelog, ul_writelog混用。你并不需要把程序中所有日志都换成streaming log。
......@@ -17,7 +17,7 @@ Streaming RPC保证:
目前的实现还没有自动切割过大的消息,同一个tcp连接上的多个Stream之间可能有[Head-of-line blocking](https://en.wikipedia.org/wiki/Head-of-line_blocking)问题,请尽量避免过大的单个消息,实现自动切割后我们会告知并更新文档。
例子见[example/streaming_echo_c++](http://icode.baidu.com/repo/baidu/opensource/baidu-rpc/files/master/tree/example/streaming_echo_c++/)
例子见[example/streaming_echo_c++](http://icode.baidu.com/repo/baidu/opensource/brpc/files/master/tree/example/streaming_echo_c++/)
# 建立Stream
......
......@@ -62,4 +62,4 @@ Use *p ... - still the errno of original pthread, undefined b
`__const__`定义为空对程序其他部分的影响几乎为0。另外如果你没有**直接**使用errno(即你的项目中没有出现errno),或使用的是gcc
3.4,即使没有定义`-D__const__=`,程序的正确性也不会受影响,但为了防止未来可能的问题,我们强烈建议加上。
需要说明的是,和errno类似,pthread_self也有类似的问题,不过一般pthread_self除了打日志没有其他用途,影响面较小,在`-D__const__=`后pthread_self也会正常。
\ No newline at end of file
需要说明的是,和errno类似,pthread_self也有类似的问题,不过一般pthread_self除了打日志没有其他用途,影响面较小,在`-D__const__=`后pthread_self也会正常。
......@@ -22,7 +22,7 @@
另外,更并发的数据结构也难以奏效,感兴趣的同学可以去搜索"concurrent priority queue"或"concurrent skip list",这些数据结构一般假设插入的数值较为散开,所以可以同时修改结构内的不同部分。但这在RPC场景中也不成立,相互竞争的线程设定的时间往往聚集在同一个区域,因为程序的超时大都是一个值,加上当前时间后都差不多。
这些因素让TimerThread的设计相当棘手。由于大部分用户的qps较低,不足以明显暴露这个扩展性问题,在r31791前我们一直沿用“用一把锁保护的TimerThread”。TimerThread是baidu-rpc在默认配置下唯一的高频竞争点,这个问题是我们一直清楚的技术债。随着baidu-rpc在高qps系统中应用越来越多,是时候解决这个问题了。r31791后的TimerThread解决了上述三个难点,timer操作几乎对RPC性能没有影响,我们先看下性能差异。
这些因素让TimerThread的设计相当棘手。由于大部分用户的qps较低,不足以明显暴露这个扩展性问题,在r31791前我们一直沿用“用一把锁保护的TimerThread”。TimerThread是brpc在默认配置下唯一的高频竞争点,这个问题是我们一直清楚的技术债。随着brpc在高qps系统中应用越来越多,是时候解决这个问题了。r31791后的TimerThread解决了上述三个难点,timer操作几乎对RPC性能没有影响,我们先看下性能差异。
> 在示例程序example/mutli_threaded_echo_c++中,r31791后TimerThread相比老TimerThread在24核E5-2620上(超线程),以50个bthread同步发送时,节省4%cpu(差不多1个核),qps提升10%左右;在400个bthread同步发送时,qps从30万上升到60万。新TimerThread的表现和完全关闭超时时接近。
......@@ -34,7 +34,7 @@
- 删除时通过id直接定位到timer内存结构,修改一个标志,timer结构总是由TimerThread释放。
- TimerThread被唤醒后首先把全局nearest_run_time设置为几乎无限大(max of int64),然后取出所有Bucket内的链表,并把Bucket的nearest_run_time设置为几乎无限大(max of int64)。TimerThread把未删除的timer插入小顶堆中维护,这个堆就它一个线程用。在每次运行回调或准备睡眠前都会检查全局nearest_run_time, 如果全局更早,说明有更早的时间加入了,重复这个过程。
这里勾勒了TimerThread的大致工作原理,工程实现中还有不少细节问题,具体请阅读[timer_thread.h](http://icode.baidu.com/repo/baidu/opensource/baidu-rpc/files/master/blob/src/bthread/timer_thread.h)[timer_thread.cpp](http://icode.baidu.com/repo/baidu/opensource/baidu-rpc/files/master/blob/src/bthread/timer_thread.cpp)
这里勾勒了TimerThread的大致工作原理,工程实现中还有不少细节问题,具体请阅读[timer_thread.h](http://icode.baidu.com/repo/baidu/opensource/brpc/files/master/blob/src/bthread/timer_thread.h)[timer_thread.cpp](http://icode.baidu.com/repo/baidu/opensource/brpc/files/master/blob/src/bthread/timer_thread.cpp)
这个方法之所以有效:
......@@ -46,10 +46,10 @@
- TimerThread自己维护小顶堆,没有任何cache bouncing,效率很高。
- TimerThread醒来的频率大约是RPC超时的倒数,比如超时=100ms,TimerThread一秒内大约醒10次,已经最优。
至此baidu-rpc在默认配置下不再有全局竞争点,在400个线程同时运行时,profiling也显示几乎没有对锁的等待。
至此brpc在默认配置下不再有全局竞争点,在400个线程同时运行时,profiling也显示几乎没有对锁的等待。
下面是一些和linux下时间管理相关的知识:
- epoll_wait的超时精度是毫秒,较差。pthread_cond_timedwait的超时使用timespec,精度到纳秒,一般是60微秒左右的延时。
- 出于性能考虑,TimerThread使用wall-time,而不是单调时间,可能受到系统时间调整的影响。具体来说,如果在测试中把系统时间往前或往后调一个小时,程序行为将完全undefined。未来可能会让用户选择单调时间。
- 在cpu支持nonstop_tsc和constant_tsc的机器上,baidu-rpc和bthread会优先使用基于rdtsc的cpuwide_time_us。那两个flag表示rdtsc可作为wall-time使用,不支持的机器上会转而使用较慢的内核时间。我们的机器(Intel Xeon系列)大都有那两个flag。rdtsc作为wall-time使用时是否会受到系统调整时间的影响,未测试不清楚。
- 在cpu支持nonstop_tsc和constant_tsc的机器上,brpc和bthread会优先使用基于rdtsc的cpuwide_time_us。那两个flag表示rdtsc可作为wall-time使用,不支持的机器上会转而使用较慢的内核时间。我们的机器(Intel Xeon系列)大都有那两个flag。rdtsc作为wall-time使用时是否会受到系统调整时间的影响,未测试不清楚。
baidu-rpc可通过多种方式访问用ub搭建的服务。
brpc可通过多种方式访问用ub搭建的服务。
# ubrpc (by protobuf)
r31687后,baidu-rpc支持通过protobuf访问ubrpc,不需要baidu-rpc-ub,也不依赖idl-compiler。(也可以让protobuf服务被ubrpc client访问,方法见[使用ubrpc的服务](nshead_service.md#使用ubrpc的服务))。
r31687后,brpc支持通过protobuf访问ubrpc,不需要baidu-rpc-ub,也不依赖idl-compiler。(也可以让protobuf服务被ubrpc client访问,方法见[使用ubrpc的服务](nshead_service.md#使用ubrpc的服务))。
**步骤:**
1.[idl2proto](http://icode.baidu.com/repo/baidu/opensource/baidu-rpc/files/master/blob/tools/idl2proto)把idl文件转化为proto文件,老版本idl2proto不会转化idl中的service,需要手动转化。
1.[idl2proto](http://icode.baidu.com/repo/baidu/opensource/brpc/files/master/blob/tools/idl2proto)把idl文件转化为proto文件,老版本idl2proto不会转化idl中的service,需要手动转化。
```protobuf
// Converted from echo.idl by public/mcpack2pb/idl2proto
......@@ -146,7 +146,7 @@ r31687后,baidu-rpc支持通过protobuf访问ubrpc,不需要baidu-rpc-ub,
// cntl.idl_result();
```
例子详见[example/echo_c++_ubrpc_compack](http://icode.baidu.com/repo/baidu/opensource/baidu-rpc/files/master/blob/example/echo_c++_ubrpc_compack/)。
例子详见[example/echo_c++_ubrpc_compack](http://icode.baidu.com/repo/baidu/opensource/brpc/files/master/blob/example/echo_c++_ubrpc_compack/)。
# ubrpc (by baidu-rpc-ub)
......@@ -154,7 +154,7 @@ server端由public/ubrpc搭建,request/response使用idl文件描述字段,
**步骤:**
1. 依赖[public/baidu-rpc-ub](http://icode.baidu.com/repo/baidu/opensource/baidu-rpc/files/master/blob)模块,在COMAKE中增加依赖:`CONFIGS('public/baidu-rpc-ub@ci-base')。`这个模块是baidu-rpc的扩展,不需要的用户不会依赖idl/mcpack/compack等模块。baidu-rpc-ub只包含扩展代码,baidu-rpc中的新特性会自动体现在这个模块中。
1. 依赖[public/baidu-rpc-ub](http://icode.baidu.com/repo/baidu/opensource/brpc/files/master/blob)模块,在COMAKE中增加依赖:`CONFIGS('public/baidu-rpc-ub@ci-base')。`这个模块是brpc的扩展,不需要的用户不会依赖idl/mcpack/compack等模块。baidu-rpc-ub只包含扩展代码,brpc中的新特性会自动体现在这个模块中。
2. 编写一个proto文件,其中定义了service,名字和idl中的相同,但请求类型必须是baidu.rpc.UBRequest,回复类型必须是baidu.rpc.UBResponse。这两个类型定义在brpc/ub.proto中,使用时得import
......@@ -219,7 +219,7 @@ server端由public/ubrpc搭建,request/response使用idl文件描述字段,
...
```
具体example代码可以参考[echo_c++_compack_ubrpc](http://icode.baidu.com/repo/baidu/opensource/baidu-rpc/files/master/tree/example/echo_c++_compack_ubrpc/),类似的还有[echo_c++_mcpack_ubrpc](http://icode.baidu.com/repo/baidu/opensource/baidu-rpc/files/master/tree/example/echo_c++_mcpack_ubrpc/)。
具体example代码可以参考[echo_c++_compack_ubrpc](http://icode.baidu.com/repo/baidu/opensource/brpc/files/master/tree/example/echo_c++_compack_ubrpc/),类似的还有[echo_c++_mcpack_ubrpc](http://icode.baidu.com/repo/baidu/opensource/brpc/files/master/tree/example/echo_c++_mcpack_ubrpc/)。
# nshead+idl
......@@ -258,7 +258,7 @@ channel.CallMethod(NULL, &cntl, &request, &response, NULL); // 假设channel
response.message();
```
具体example代码可以参考[echo_c++_mcpack_ub](http://icode.baidu.com/repo/baidu/opensource/baidu-rpc/files/master/blob/example/echo_c++_mcpack_ub/),compack情况类似,不再赘述
具体example代码可以参考[echo_c++_mcpack_ub](http://icode.baidu.com/repo/baidu/opensource/brpc/files/master/blob/example/echo_c++_mcpack_ub/),compack情况类似,不再赘述
# nshead+mcpack(非idl产生的)
......@@ -305,11 +305,11 @@ const mc_pack_t* res_pack = response.McpackHandle();
mc_pack_get_str(res_pack, "mystr");
```
具体example代码可以参考[echo_c++_raw_mcpack](http://icode.baidu.com/repo/baidu/opensource/baidu-rpc/files/master/blob/example/echo_c++_raw_mcpack/)。
具体example代码可以参考[echo_c++_raw_mcpack](http://icode.baidu.com/repo/baidu/opensource/brpc/files/master/blob/example/echo_c++_raw_mcpack/)。
# nshead+blob
r32897后baidu-rpc直接支持用nshead+blob访问老server(而不用依赖baidu-rpc-ub)。example代码可以参考[nshead_extension_c++](http://icode.baidu.com/repo/baidu/opensource/baidu-rpc/files/master/blob/example/nshead_extension_c++/client.cpp)。
r32897后brpc直接支持用nshead+blob访问老server(而不用依赖baidu-rpc-ub)。example代码可以参考[nshead_extension_c++](http://icode.baidu.com/repo/baidu/opensource/brpc/files/master/blob/example/nshead_extension_c++/client.cpp)。
```c++
#include <brpc/nshead_message.h>
......@@ -342,7 +342,7 @@ if (cntl.Failed()) {
// response.head and response.body contains nshead_t and blob respectively.
```
或者用户也可以使用baidu-rpc-ub中的UBRawBufferRequest和UBRawBufferResponse来访问。example代码可以参考[echo_c++_raw_buffer](http://icode.baidu.com/repo/baidu/opensource/baidu-rpc/files/master/blob/example/echo_c++_raw_buffer/)。
或者用户也可以使用baidu-rpc-ub中的UBRawBufferRequest和UBRawBufferResponse来访问。example代码可以参考[echo_c++_raw_buffer](http://icode.baidu.com/repo/baidu/opensource/brpc/files/master/blob/example/echo_c++_raw_buffer/)。
```c++
brpc::Channel channel;
......
[public/bvar](http://icode.baidu.com/repo/baidu/opensource/baidu-rpc/files/master/tree/src/bvar/)是多线程环境下的计数器类库,方便记录和查看用户程序中的各类数值,它利用了thread local存储避免了cache bouncing,相比UbMonitor几乎不会给程序增加性能开销,也快于竞争频繁的原子操作。baidu-rpc集成了bvar,[/vars](http://brpc.baidu.com:8765/vars)可查看所有曝光的bvar,[/vars/VARNAME](http://brpc.baidu.com:8765/vars/rpc_socket_count)可查阅某个bvar,增加计数器的方法请查看[bvar](bvar.md)。baidu-rpc大量使用了bvar提供统计数值,当你需要在多线程环境中计数并展现时,应该第一时间想到bvar。但bvar不能代替所有的计数器,它的本质是把写时的竞争转移到了读:读得合并所有写过的线程中的数据,而不可避免地变慢了。当你读写都很频繁并得基于数值做一些逻辑判断时,你不应该用bvar。
[public/bvar](http://icode.baidu.com/repo/baidu/opensource/brpc/files/master/tree/src/bvar/)是多线程环境下的计数器类库,方便记录和查看用户程序中的各类数值,它利用了thread local存储避免了cache bouncing,相比UbMonitor几乎不会给程序增加性能开销,也快于竞争频繁的原子操作。brpc集成了bvar,[/vars](http://brpc.baidu.com:8765/vars)可查看所有曝光的bvar,[/vars/VARNAME](http://brpc.baidu.com:8765/vars/rpc_socket_count)可查阅某个bvar,增加计数器的方法请查看[bvar](bvar.md)。brpc大量使用了bvar提供统计数值,当你需要在多线程环境中计数并展现时,应该第一时间想到bvar。但bvar不能代替所有的计数器,它的本质是把写时的竞争转移到了读:读得合并所有写过的线程中的数据,而不可避免地变慢了。当你读写都很频繁并得基于数值做一些逻辑判断时,你不应该用bvar。
## 查询方法
......@@ -65,11 +65,11 @@ x%分位值(percentile)是指把一段时间内的N个统计值排序,排
上图是按时间变化曲线。包含了4条曲线,横轴是时间,纵轴从上到下分别对应99.9%,99%,90%,50%分位值。颜色从上到下也越来越浅(从橘红到土黄)。滑动鼠标可以阅读对应数据点的值,上图中显示是”39秒种前的99%分位值是330微秒”。这幅图中不包含99.99%的曲线,因为99.99%分位值常明显大于99.9%及以下的分位值,画在一起的话会使得其他曲线变得很”矮“,难以辨认。你可以点击以"_latency_9999"结尾的bvar独立查看99.99%曲线,当然,你也可以独立查看50%,90%,99%,99.9%等曲线。按时间变化曲线可以看到分位值的变化趋势,对分析系统的性能变化很实用。
baidu-rpc的服务都会自动统计延时分布,用户不用自己加了。如下图所示:
brpc的服务都会自动统计延时分布,用户不用自己加了。如下图所示:
![img](../images/vars_6.png)
你可以用bvar::LatencyRecorder统计非baidu-rpc服务的延时,这么做(更具体的使用方法请查看[bvar-c++](bvar_c++.md)):
你可以用bvar::LatencyRecorder统计非brpc服务的延时,这么做(更具体的使用方法请查看[bvar-c++](bvar_c++.md)):
```c++
#include <bvar/bvar.h>
......@@ -84,10 +84,10 @@ void foo() {
}
```
如果这个程序使用了baidu-rpc server,那么你应该已经可以在/vars看到client_latency, client_latency_cdf等变量,点击便可查看动态曲线。如下图所示:
如果这个程序使用了brpc server,那么你应该已经可以在/vars看到client_latency, client_latency_cdf等变量,点击便可查看动态曲线。如下图所示:
![img](../images/vars_7.png)
## 非baidu-rpc server
## 非brpc server
如果这个程序只是一个baidu-rpc client或根本没有使用baidu-rpc,并且你也想看到动态曲线,看[这里](dummy_server.md)
如果这个程序只是一个brpc client或根本没有使用brpc,并且你也想看到动态曲线,看[这里](dummy_server.md)
......@@ -75,7 +75,7 @@ int main(int argc, char* argv[]) {
std::cerr << cntl.ErrorText() << std::endl;
return -1;
}
// If -http_verbose is on, baidu-rpc already prints the response to stderr.
// If -http_verbose is on, brpc already prints the response to stderr.
if (!brpc::FLAGS_http_verbose) {
std::cout << cntl.response_attachment() << std::endl;
}
......
......@@ -24,7 +24,7 @@ DEFINE_int32(idle_timeout_s, -1, "Connection will be closed if there is no "
"read/write operations during the last `idle_timeout_s'");
DEFINE_int32(max_concurrency, 0, "Limit of request processing in parallel");
// Adapt your own nshead-based protocol to use baidu-rpc
// Adapt your own nshead-based protocol to use brpc
class MyNsheadProtocol : public brpc::NsheadService {
public:
void ProcessNsheadRequest(const brpc::Server&,
......
......@@ -111,7 +111,7 @@ int main(int argc, char* argv[]) {
// channels are disabled in ParallelChannel.
if (FLAGS_same_channel) {
// For baidu-rpc >= 1.0.155.31351, a sub channel can be added into
// For brpc >= 1.0.155.31351, a sub channel can be added into
// a ParallelChannel more than once.
brpc::Channel* sub_channel = new brpc::Channel;
// Initialize the channel, NULL means using default options.
......
......@@ -12,7 +12,7 @@
// See the License for the specific language governing permissions and
// limitations under the License.
// A baidu-rpc based command-line interface to talk with redis-server
// A brpc based command-line interface to talk with redis-server
#include <signal.h>
#include <stdio.h>
......@@ -102,7 +102,7 @@ int main(int argc, char* argv[]) {
// Print welcome information.
printf("%s\n", brpc::logo());
printf("This command-line tool mimics the look-n-feel of official "
"redis-cli, as a demostration of baidu-rpc's capability of"
"redis-cli, as a demostration of brpc's capability of"
" talking to redis-server. The output and behavior is "
"not exactly same with the official one.\n\n");
......@@ -129,7 +129,7 @@ int main(int argc, char* argv[]) {
add_history(command.get());
if (!strcmp(command.get(), "help")) {
printf("This is a redis CLI written in baidu-rpc.\n");
printf("This is a redis CLI written in brpc.\n");
continue;
}
if (!strcmp(command.get(), "quit")) {
......
Notice that the following BSD-style license applies to the Valgrind header
files used by baidu-rpc (valgrind.h). However, the rest of Valgrind is
files used by brpc (valgrind.h). However, the rest of Valgrind is
licensed under the terms of the GNU General Public License, version 2,
unless otherwise indicated.
......
......@@ -17,7 +17,7 @@
#ifndef BRPC_ADAPTIVE_CONNECTION_TYPE_H
#define BRPC_ADAPTIVE_CONNECTION_TYPE_H
// To baidu-rpc developers: This is a header included by user, don't depend
// To brpc developers: This is a header included by user, don't depend
// on internal structures, use opaque pointers instead.
#include "base/strings/string_piece.h"
......
......@@ -15,7 +15,7 @@
#ifndef BRPC_ADAPTIVE_PROTOCOL_TYPE_H
#define BRPC_ADAPTIVE_PROTOCOL_TYPE_H
// To baidu-rpc developers: This is a header included by user, don't depend
// To brpc developers: This is a header included by user, don't depend
// on internal structures, use opaque pointers instead.
#include "base/strings/string_piece.h"
......
......@@ -27,7 +27,7 @@ namespace brpc {
class Controller;
// These static strings are referenced more than once in baidu-rpc.
// These static strings are referenced more than once in brpc.
// Don't turn them to std::strings whose constructing sequences are undefined.
const char* const UNKNOWN_METHOD_STR = "unknown_method";
const char* const TRACE_ID_STR = "trace";
......
......@@ -18,7 +18,7 @@
#ifndef BRPC_CHANNEL_H
#define BRPC_CHANNEL_H
// To baidu-rpc developers: This is a header included by user, don't depend
// To brpc developers: This is a header included by user, don't depend
// on internal structures, use opaque pointers instead.
#include <ostream> // std::ostream
......
......@@ -23,13 +23,13 @@
#include <google/protobuf/service.h> // google::protobuf::RpcChannel
#include "brpc/describable.h"
// To baidu-rpc developers: This is a header included by user, don't depend
// To brpc developers: This is a header included by user, don't depend
// on internal structures, use opaque pointers instead.
namespace brpc {
// Base of all baidu-rpc channels.
// Base of all brpc channels.
class ChannelBase : public google::protobuf::RpcChannel/*non-copyable*/,
public Describable {
public:
......
......@@ -17,7 +17,7 @@
#ifndef BRPC_CONTROLLER_H
#define BRPC_CONTROLLER_H
// To baidu-rpc developers: This is a header included by user, don't depend
// To brpc developers: This is a header included by user, don't depend
// on internal structures, use opaque pointers instead.
#include <gflags/gflags.h> // Users often need gflags
......
......@@ -17,7 +17,7 @@
#ifndef BRPC_DATA_FACTORY_H
#define BRPC_DATA_FACTORY_H
// To baidu-rpc developers: This is a header included by user, don't depend
// To brpc developers: This is a header included by user, don't depend
// on internal structures, use opaque pointers instead.
namespace brpc {
......
......@@ -130,7 +130,7 @@ private:
};
// Inherit this class to intercept Controller::IssueRPC. This is an internal
// utility only useable by baidu-rpc developers.
// utility only useable by brpc developers.
class RPCSender {
public:
virtual ~RPCSender() {}
......
......@@ -579,7 +579,7 @@ void SerializeHttpRequest(base::IOBuf* request,
}
// The fake "curl" user-agent may let servers return plain-text results.
if (h->GetHeader("User-Agent") == NULL) {
os << "User-Agent: baidu-rpc/1.0 curl/7.0" BRPC_CRLF;
os << "User-Agent: brpc/1.0 curl/7.0" BRPC_CRLF;
}
const std::string& user_info = h->uri().user_info();
if (!user_info.empty() && h->GetHeader("Authorization") == NULL) {
......
......@@ -81,7 +81,7 @@ struct NsheadServiceOptions {
size_t additional_space;
};
// Inherit this class to let baidu-rpc server understands nshead requests.
// Inherit this class to let brpc server understands nshead requests.
class NsheadService : public Describable {
public:
NsheadService();
......
......@@ -17,7 +17,7 @@
#ifndef BRPC_PARALLEL_CHANNEL_H
#define BRPC_PARALLEL_CHANNEL_H
// To baidu-rpc developers: This is a header included by user, don't depend
// To brpc developers: This is a header included by user, don't depend
// on internal structures, use opaque pointers instead.
#include <vector>
......
......@@ -17,7 +17,7 @@
#ifndef BRPC_PARTITION_CHANNEL_H
#define BRPC_PARTITION_CHANNEL_H
// To baidu-rpc developers: This is a header included by user, don't depend
// To brpc developers: This is a header included by user, don't depend
// on internal structures, use opaque pointers instead.
#include "brpc/parallel_channel.h"
......
......@@ -58,7 +58,7 @@ DEFINE_int32(http_body_compress_threshold, 512, "Not compress http body when "
DEFINE_string(http_header_of_user_ip, "", "http requests sent by proxies may "
"set the client ip in http headers. When this flag is non-empty, "
"baidu-rpc will read ip:port from the specified header for "
"brpc will read ip:port from the specified header for "
"authorization and set Controller::remote_side()");
DEFINE_bool(pb_enum_as_number, false, "[Not recommended] Convert enums in "
......@@ -100,7 +100,7 @@ CommonStrings::CommonStrings()
: ACCEPT("accept")
, DEFAULT_ACCEPT("*/*")
, USER_AGENT("user-agent")
, DEFAULT_USER_AGENT("baidu-rpc/1.0 curl/7.0")
, DEFAULT_USER_AGENT("brpc/1.0 curl/7.0")
, CONTENT_TYPE("content-type")
, CONTENT_TYPE_TEXT("text/plain")
, CONTENT_TYPE_JSON("application/json")
......
......@@ -48,7 +48,7 @@ DEFINE_bool(log_error_text, false,
BRPC_VALIDATE_GFLAG(log_error_text, PassValidate);
// Not using ProtocolType_MAX as the boundary because others may define new
// protocols outside baidu-rpc.
// protocols outside brpc.
const size_t MAX_PROTOCOL_SIZE = 128;
struct ProtocolEntry {
base::atomic<bool> valid;
......
......@@ -18,7 +18,7 @@
#ifndef BRPC_PROTOCOL_H
#define BRPC_PROTOCOL_H
// To baidu-rpc developers: This is a header included by user, don't depend
// To brpc developers: This is a header included by user, don't depend
// on internal structures, use opaque pointers instead.
#include <vector> // std::vector
......
......@@ -17,13 +17,13 @@
#ifndef BRPC_RELOADABLE_FLAGS_H
#define BRPC_RELOADABLE_FLAGS_H
// To baidu-rpc developers: This is a header included by user, don't depend
// To brpc developers: This is a header included by user, don't depend
// on internal structures, use opaque pointers instead.
#include <stdint.h>
// Register an always-true valiator to a gflag so that the gflag is treated as
// reloadable by baidu-rpc. If a validator exists, abort the program.
// reloadable by brpc. If a validator exists, abort the program.
// You should call this macro within global scope. for example:
//
// DEFINE_int32(foo, 0, "blah blah");
......
......@@ -68,7 +68,7 @@ public:
// don't forget the const modifier
};
// Get the RetryPolicy used by baidu-rpc.
// Get the RetryPolicy used by brpc.
const RetryPolicy* DefaultRetryPolicy();
} // namespace brpc
......
......@@ -17,7 +17,7 @@
#ifndef BRPC_SELECTIVE_CHANNEL_H
#define BRPC_SELECTIVE_CHANNEL_H
// To baidu-rpc developers: This is a header included by user, don't depend
// To brpc developers: This is a header included by user, don't depend
// on internal structures, use opaque pointers instead.
#include "brpc/socket_id.h"
......
......@@ -18,7 +18,7 @@
#ifndef BRPC_SERVER_H
#define BRPC_SERVER_H
// To baidu-rpc developers: This is a header included by user, don't depend
// To brpc developers: This is a header included by user, don't depend
// on internal structures, use opaque pointers instead.
#include "bthread/errno.h" // Redefine errno
......@@ -157,7 +157,7 @@ struct ServerOptions {
// you can't assume that the server uses exactly so many pthreads because
// pthread workers are shared by all servers and channels inside a
// process. And there're no "io-thread" and "worker-thread" anymore,
// baidu-rpc automatically schedules "io" and "worker" code for better
// brpc automatically schedules "io" and "worker" code for better
// parallelism and less context switches.
// If this option <= 0, number of pthread workers is not changed.
// Default: #cpu-cores
......@@ -167,7 +167,7 @@ struct ServerOptions {
// concurrency of a method, use server.MaxConcurrencyOf("xxx") instead.
//
// In a traditional server, number of pthread workers also limits
// concurrency. However baidu-rpc runs requests in bthreads which are
// concurrency. However brpc runs requests in bthreads which are
// mapped to pthread workers, when a bthread context switches, it gives
// the pthread worker to another bthread, yielding a higher concurrency
// than number of pthreads. In some situation, higher concurrency may
......@@ -326,7 +326,7 @@ struct ServiceOptions {
// when the pb schema is non-empty in http servings. The body must be
// valid json or protobuf(wire-format) otherwise the request is rejected.
// This option does not affect pure-http services (pb schema is empty).
// Services that use older versions of baidu-rpc may need to turn this
// Services that use older versions of brpc may need to turn this
// conversion off and handle http requests by their own to keep compatible
// with existing clients.
// Default: true
......
......@@ -17,7 +17,7 @@
#ifndef BRPC_SERVER_ID_H
#define BRPC_SERVER_ID_H
// To baidu-rpc developers: This is a header included by user, don't depend
// To brpc developers: This is a header included by user, don't depend
// on internal structures, use opaque pointers instead.
#include "base/containers/hash_tables.h" // hash
......
......@@ -17,7 +17,7 @@
#ifndef BRPC_SOCKET_ID_H
#define BRPC_SOCKET_ID_H
// To baidu-rpc developers: This is a header included by user, don't depend
// To brpc developers: This is a header included by user, don't depend
// on internal structures, use opaque pointers instead.
#include <stdint.h> // uint64_t
......
......@@ -19,7 +19,7 @@
#include "base/macros.h"
// To baidu-rpc developers: This is a header included by user, don't depend
// To brpc developers: This is a header included by user, don't depend
// on internal structures, use opaque pointers instead.
......
......@@ -155,11 +155,11 @@ static void HandleTrackMeResponse(Controller* cntl, TrackMeResponse* res) {
case TrackMeOK:
break;
case TrackMeFatal:
LOG(ERROR) << "Your baidu-rpc (r" << g_rpc_version
LOG(ERROR) << "Your brpc (r" << g_rpc_version
<< ") is affected by: " << res->error_text();
break;
case TrackMeWarning:
LOG(WARNING) << "Your baidu-rpc (r" << g_rpc_version
LOG(WARNING) << "Your brpc (r" << g_rpc_version
<< ") is affected by: " << res->error_text();
break;
default:
......
......@@ -23,7 +23,7 @@
#include "base/status.h"
#include "base/string_splitter.h"
// To baidu-rpc developers: This is a class exposed to end-user. DON'T put impl.
// To brpc developers: This is a class exposed to end-user. DON'T put impl.
// details in this header, use opaque pointers instead.
......
......@@ -283,11 +283,11 @@ extern int bthread_key_delete(bthread_key_t key) __THROW;
// bthread_setspecific() is callable from within destructor. If the application
// does so, destructors will be repeatedly called for at most
// PTHREAD_DESTRUCTOR_ITERATIONS times to clear the slots.
// NOTE: If the thread is not created by baidu-rpc server and lifetime is
// NOTE: If the thread is not created by brpc server and lifetime is
// very short(doing a little thing and exit), avoid using bthread-local. The
// reason is that bthread-local always allocate keytable on first call to
// bthread_setspecific, the overhead is negligible in long-lived threads,
// but noticeable in shortly-lived threads. Threads in baidu-rpc server
// but noticeable in shortly-lived threads. Threads in brpc server
// are special since they reuse keytables from a bthread_keytable_pool_t
// in the server.
// Returns 0 on success, error code otherwise.
......
......@@ -55,7 +55,7 @@ namespace bthread {
// }
// This container is NOT thread-safe right now, and shouldn't be
// an issue in current usages throughout baidu-rpc.
// an issue in current usages throughout brpc.
template <typename Id, typename IdTraits>
class ListOfABAFreeId {
public:
......
......@@ -83,7 +83,7 @@ inline void TaskGroup::push_rq(bthread_t tid) {
// into other TaskGroup does not help.
// * Insertions into other TaskGroups perform worse when all workers
// are busy at creating bthreads (proved by test_input_messenger in
// baidu-rpc)
// brpc)
flush_nosignal_tasks();
LOG_EVERY_SECOND(ERROR) << "_rq is full, capacity=" << _rq.capacity();
// TODO(gejun): May cause deadlock when all workers are spinning here.
......
......@@ -797,7 +797,7 @@ static bool validate_bvar_dump(const char*, bool enabled) {
const bool ALLOW_UNUSED dummy_bvar_dump = ::google::RegisterFlagValidator(
&FLAGS_bvar_dump, validate_bvar_dump);
// validators (to make these gflags reloadable in baidu-rpc)
// validators (to make these gflags reloadable in brpc)
static bool validate_bvar_dump_interval(const char*, int32_t v) {
// FIXME: -bvar_dump_interval is actually unreloadable but we need to
// check validity of it, so we still add this validator. In practice
......
......@@ -26,7 +26,7 @@
#include "mcpack2pb/field_type.h"
// CAUTION: Methods in this header is not intended to be public to users of
// baidu-rpc, and subject to change at any future time.
// brpc, and subject to change at any future time.
namespace mcpack2pb {
......
......@@ -26,7 +26,7 @@
#include "mcpack2pb/field_type.h"
// CAUTION: Methods in this header is not intended to be public to users of
// baidu-rpc, and subject to change at any future time.
// brpc, and subject to change at any future time.
namespace mcpack2pb {
......
......@@ -334,22 +334,22 @@ TEST(HttpMessageTest, serialize_http_request) {
base::IOBuf content;
content.append("data");
SerializeHttpRequest(&request, &header, ep, &content);
ASSERT_EQ("POST / HTTP/1.1\r\nContent-Length: 4\r\nHost: 127.0.0.1:1234\r\nFoo: Bar\r\nAccept: */*\r\nUser-Agent: baidu-rpc/1.0 curl/7.0\r\n\r\ndata", request);
ASSERT_EQ("POST / HTTP/1.1\r\nContent-Length: 4\r\nHost: 127.0.0.1:1234\r\nFoo: Bar\r\nAccept: */*\r\nUser-Agent: brpc/1.0 curl/7.0\r\n\r\ndata", request);
// user-set content-length is ignored.
header.SetHeader("Content-Length", "100");
SerializeHttpRequest(&request, &header, ep, &content);
ASSERT_EQ("POST / HTTP/1.1\r\nContent-Length: 4\r\nHost: 127.0.0.1:1234\r\nFoo: Bar\r\nAccept: */*\r\nUser-Agent: baidu-rpc/1.0 curl/7.0\r\n\r\ndata", request);
ASSERT_EQ("POST / HTTP/1.1\r\nContent-Length: 4\r\nHost: 127.0.0.1:1234\r\nFoo: Bar\r\nAccept: */*\r\nUser-Agent: brpc/1.0 curl/7.0\r\n\r\ndata", request);
// user-host overwrites passed-in remote_side
header.SetHeader("Host", "MyHost: 4321");
SerializeHttpRequest(&request, &header, ep, &content);
ASSERT_EQ("POST / HTTP/1.1\r\nContent-Length: 4\r\nFoo: Bar\r\nHost: MyHost: 4321\r\nAccept: */*\r\nUser-Agent: baidu-rpc/1.0 curl/7.0\r\n\r\ndata", request);
ASSERT_EQ("POST / HTTP/1.1\r\nContent-Length: 4\r\nFoo: Bar\r\nHost: MyHost: 4321\r\nAccept: */*\r\nUser-Agent: brpc/1.0 curl/7.0\r\n\r\ndata", request);
// user-set accept
header.SetHeader("accePT"/*intended uppercase*/, "blahblah");
SerializeHttpRequest(&request, &header, ep, &content);
ASSERT_EQ("POST / HTTP/1.1\r\nContent-Length: 4\r\naccePT: blahblah\r\nFoo: Bar\r\nHost: MyHost: 4321\r\nUser-Agent: baidu-rpc/1.0 curl/7.0\r\n\r\ndata", request);
ASSERT_EQ("POST / HTTP/1.1\r\nContent-Length: 4\r\naccePT: blahblah\r\nFoo: Bar\r\nHost: MyHost: 4321\r\nUser-Agent: brpc/1.0 curl/7.0\r\n\r\ndata", request);
// user-set UA
header.SetHeader("user-AGENT", "myUA");
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment