Skip to content
Projects
Groups
Snippets
Help
Loading...
Sign in / Register
Toggle navigation
B
brpc
Project
Project
Details
Activity
Cycle Analytics
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Charts
Issues
0
Issues
0
List
Board
Labels
Milestones
Merge Requests
0
Merge Requests
0
CI / CD
CI / CD
Pipelines
Jobs
Schedules
Charts
Packages
Packages
Wiki
Wiki
Snippets
Snippets
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Charts
Create a new issue
Jobs
Commits
Issue Boards
Open sidebar
submodule
brpc
Commits
ed52e0b9
Commit
ed52e0b9
authored
Aug 24, 2017
by
gejun
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
add overview.md and getting_started.md
parent
72a31f94
Show whitespace changes
Inline
Side-by-side
Showing
2 changed files
with
490 additions
and
0 deletions
+490
-0
getting_started.md
docs/getting_started.md
+360
-0
overview.md
docs/overview.md
+130
-0
No files found.
docs/getting_started.md
0 → 100644
View file @
ed52e0b9
# 运行示例程序
在命令行中运行如下命令即可在~/my_baidu_rpc/public/baidu-rpc中下载源代码编译并运行echo示例程序:
> mkdir -p ~/my_baidu_rpc/public && cd ~/my_baidu_rpc/public && svn co
> https://svn.baidu.com/public/trunk/baidu-rpc && cd baidu-rpc && comake2 -UB -J8 -j8 && comake2 -P
> && make -sj8 && cd example/echo_c++ && comake2 -UB -J8 -j8 && comake2 -P && make -sj8 && (
> ./echo_server & ) && ./echo_client && pkill echo_server
# 通过COMAKE依赖baidu-rpc
如果你的模块还没有建立,你可能得去
[
work.baidu.com
](
http://work.baidu.com/
)
上申请新的模块。确保你使用了
[
comake2
](
http://wiki.babel.baidu.com/twiki/bin/view/Com/Main/Comake2
)
。
在COMAKE文件中增加:
> CONFIGS('public/baidu-rpc@ci-base')
这依赖了baidu-rpc的最新发布版本。模板可参考
[
echo的COMAKE文件
](
https://svn.baidu.com/public/trunk/baidu-rpc/example/echo_c++/COMAKE
)
。然后运行:
```
$ comake2 -UB # 下载所有的依赖模块
$ comake2 -P # 生成或更新Makefile
$ make -sj8 # 编译
```
你也可以在
[
agile上
](
http://agile.baidu.com/#/builds/public/baidu-rpc@trunk
)
或
[
scm.baidu.com
](
http://scm.baidu.com/
)
上查询baidu-rpc的已发布版本,并在COMAKE中依赖对应的静态版本。当comake2
-UB时baidu-rpc的版本会固定在你选定的版本上。注意:静态版本指的是我们发布的tag,而不是trunk上的revision。请勿依赖trunk的某个revision。
### gcc4下的errno问题
务必在直接或间接使用baidu-rpc的C/C++项目COMAKE中添加
**CPPFLAGS('-D__const__=')**
,防止
[
gcc4下的errno问题
](
http://wiki.baidu.com/display/RPC/thread-local#thread-local-gcc4下的errno问题
)
。
## comake出错
老版本comake2(比如2.1.3.2304)不支持ci-base版本,如果comake2报如下的错误,说明版本太老:
```
... [status:1][err:https://svn.baidu.com/public/tags/xxx/ci-base: (Not a valid URL)
```
你可以运行如下命令更新comake2版本后再试,确保你有修改的权限。
```
$ cd $(dirname $(readlink -f $(which comake2)))/libcomake2
$ ./auto_update.py
```
## 更新依赖后编译失败
一般是baidu-rpc和依赖的模块版本不匹配导致的。确保以下模块没有被指定版本,让baidu-rpc自己选择:
[
public/common
](
http://agile.baidu.com/agile/pipeline#/builds/public/common@trunk
)
[
public/bthread
](
http://agile.baidu.com/agile/pipeline#/builds/public/bthread@trunk
)
[
public/bvar
](
http://agile.baidu.com/agile/pipeline#/builds/public/bvar@trunk
)
[
public/murmurhash
](
http://agile.baidu.com/agile/pipeline#/builds/public/murmurhash@trunk
)
[
public/mcpack2pb
](
http://agile.baidu.com/agile/pipeline#/builds/public/mcpack2pb@trunk
)
[
public/iobuf
](
http://agile.baidu.com/agile/pipeline#/builds/public/iobuf@trunk
)
[
public/protobuf-json
](
http://agile.baidu.com/agile/pipeline#/builds/public/protobuf-json@trunk
)
问题的根源在于百度的代码库不是单根的,所以每个模块都有很多独立版本, 产生非常多的组合,
有点类似于以前windows上的dll hell问题, 以baidu-rpc依赖的public/common为例:
-
情况1: 产品线依赖了public/common@某tag 和 public/baidu-rpc@ci-base, 所以baidu-rpc总是会更新到最新,
而public/common则不会, 如果baidu-rpc依赖了public/common的新接口, baidu-rpc的编译就挂了.
-
情况2: 产品线依赖了public/common@ci-base 和 public/baidu-rpc@某tag,
所以public/common总是会更新到最新, 但baidu-rpc不变, 如果public/common中删除了老接口,
或一些接口有调整, baidu-rpc的编译也挂了
如果去掉了public/common的依赖,
每次更新时COMAKE或BCLOUD会自动选择baidu-rpc对应版本被发布时使用的public/common版本,
比如baidu-rpc@某tag会选择该tag发布时对应的public/common版本, 一般总是可编译过的.
当然COMAKE或BCLOUD可能会对依赖打平, 如果产品线依赖的其他模块里依赖了public/common某版本且无法更改,
同时有编译问题, 只是在自己模块中去掉对public/common的依赖是不够的,
因为COMAKE或BCLOUD还是会选到那个指定的版本,
这种情况下可以在自己的COMAKE或BCLOUD中显式地依赖baidu-rpc@ci-base和public/common@ci-base.
基本原则就是都依赖ci-base一般总是对的, 不用过于担心稳定性问题.
# 支持的软件栈
## GCC
| 版本 | 支持程度 |
| ---- | ----------- |
| 3.4 | 一直支持 |
| 4.4 | 15年9月后的版本支持 |
| 4.8 | 一直支持 |
| 5.4 | r34255后支持 |
| 7.1 | r35109后支持 |
使用其他版本的gcc可能会有warning而编译失败 (baidu-rpc把warning也视作error), 请联系我们修复。
r32023后支持C++11编译。
## GDB
如果你使用的是opt/compiler下的gcc(4.8),那么gdb应该也应该用opt/compiler下的。
如果gdb对多线程的支持有问题,可以用这个版本,在目标机器上运行如下命令可获得gdb79可执行文件:
`wget "http://hetu.baidu.com:80/api/tool/getFile?toolId=1357&fileId=1201" -O "gdb79" && chmod +x
./gdb79`
## OS
RHEL4,centos 6.5,centos 4.3,ubuntu 14.04 (linuxmint 17.3)
ubuntu (linuxmint)下找不到openssl/ssl.h的话请安装libssl-dev。
对于自己的机器,安装好svn和
[
comake2
](
http://wiki.babel.baidu.com/twiki/bin/view/Com/Main/Comake2#安装升级
)
(公司环境中才能运行auto_udpate.py)后就可以按照第一节的方法下载和运行baidu-rpc了。
## protobuf
一直支持2.4。公司内请使用
[
2.4.1.1100
](
http://scm.baidu.com/fourversion/index.action?threeversion.id=346350&fourversion.id=668155
)
,这个版本中为google::protobuf::NewCallback增加了重载形式,可以支持超过2个参数。
r31361后支持2.6。公司内请使用
[
2.6.1.400
](
http://scm.baidu.com/fourversion/index.action?threeversion.id=644886&fourversion.id=1139940
)
,这个版本中为google::protobuf::NewCallback增加了重载形式,可以支持超过2个参数。
r32035后支持3.1.x,server端的arena分配暂不支持。不要求开启C++11,但要求gcc 4.8。
r35000后支持3.2.x。
### 更换protobuf版本
baidu-rpc默认的依赖2.4,你可以在你项目的COMAKE或BCLOUD中指定不同版本的protobuf,依赖会被打平。如果baidu-rpc依赖的一些模块报告protobuf版本不匹配,可以去对应的模块clean后重编。
### 关于NewCallback
由于protobuf
3把NewCallback设置为私有,r32035后baidu-rpc把NewCallback独立于
[
src/baidu/rpc/callback.h
](
https://svn.baidu.com/public/trunk/baidu-rpc/src/baidu/rpc/callback.h
)
,如果你的程序出现NewCallback相关的编译错误(不论protobuf的版本),把google::protobuf::NewCallback替换为baidu::rpc::NewCallback就行了。
### 编译.proto
COMAKE
[
支持proto文件
](
http://wiki.babel.baidu.com/twiki/bin/view/Com/Main/Comake2#如何编译idl或proto文件
)
作为源文件,但需要通过
**PROTOC**
指明protobuf
compiler的位置,在COMAKE文件中加入:
**COMAKE**
你也可以使用用如下命令手动生成代码,详见
[
公司内的pb文档
](
http://wiki.babel.baidu.com/twiki/bin/view/Com/Main/Protobuf
)
:
```
$ protoc --cpp_out=DEST_PATH -I=PROTO_PATH your.proto
```
更多protobuf问题请先阅读
[
google官方文档
](
https://developers.google.com/protocol-buffers/docs/reference/overview
)
,再阅读
[
公司内文档
](
http://wiki.babel.baidu.com/twiki/bin/view/Com/Main/Protobuf
)
,确保你已经了解protobuf的概念和基本使用方式。不确定的接口可以查看生成的.pb.h文件。
### 同时兼容pb 3.0和pb 2.x
勿使用proto3的新类型,proto文件开头要加上syntax="proto2";,
[
tools/add_syntax_equal_proto2_to_all.sh
](
https://svn.baidu.com/public/trunk/baidu-rpc/tools/add_syntax_equal_proto2_to_all.sh
)
可以给目录以下的所有没有加的proto文件加上syntax="proto2"。
## boost
要求1.56以上。r34354后开启C++11时不依赖boost。
**intrusive_ptr**
r34233后改为依赖public/common中的base::intrusive_ptr,不再依赖boost中的。
**context**
r34213后改为依赖public/bthread中的context,不再依赖boost中的。
r34213前如果在编译过程中出现相关的错误,可以尝试在COMAKE中显式地声明依赖:
**COMAKE**
**atomic**
r34354后改为依赖public/common中的base::atomic,不再(直接)依赖boost中的。当开启C++11时,base::atomic基于std::atomic,否则仍基于boost::atomic。换句话说,此版本以后的baidu-rpc当开启C++11时不再依赖boost。
## tcmalloc
baidu-rpc默认
**不链接**
[
tcmalloc
](
http://goog-perftools.sourceforge.net/doc/tcmalloc.html
)
,如果需要可自行依赖,在COMAKE中增加:
**COMAKE**
tcmalloc相比默认的ptmalloc常可提升整体性能,建议尝试。但不同的tcmalloc版本可能有巨大的性能差异。tcmalloc
2.
1.0.100会使baidu-rpc示例程序的性能显著地低于使用tcmalloc 1.7.0.200和2.5.0.5977的版本。甚至使用
1.
7.0.100的性能也比1.7.0.200低一些,当你的程序出现性能问题时,去掉tcmalloc或更换版本看看。
使用gcc4.8.2编译的模块, 如果依赖的是third-64/tcmalloc(gcc3.4.5编译的产出),
可能会遇到main函数前malloc线程不安全的情况,具体表现为程序在main函数之前crash或者死锁. 例如
![
img
](
http://wiki.baidu.com/download/attachments/71337200/image2017-8-22%2017%3A32%3A28.png?version=1&modificationDate=1503394348000&api=v2
)
如果你的程序在gcc4.8编译的产出中遇到这个问题,建议升级tcmalloc为源码编译.
tcmalloc的另一个常见问题是它不像默认的ptmalloc那样及时的归还系统内存,所以在出现非法内存访问时,可能不会立刻crash,而最终crash在不相关的地方,甚至不crash。当你的程序出现诡异的内存问题时,也记得去掉tcmalloc看看。
如果要使用
[
cpu profiler
](
http://wiki.baidu.com/display/RPC/cpu+profiler
)
或
[
heap
profiler](http://wiki.baidu.com/display/RPC/heap+profiler),请链接tcmalloc,这两个profiler是基于tcmalloc开发的。
[
contention
profiler](http://wiki.baidu.com/display/RPC/contention+profiler)不要求tcmalloc。
不想链接tcmalloc时请注意:不仅要去掉对tcmalloc模块的依赖,还得检查下是否删除了-DBAIDU_RPC_ENABLE_CPU_PROFILER
或 -DBAIDU_RPC_ENABLE_HEAP_PROFILER等baidu-rpc的宏。
## gflags
经验证支持2.0至
[
2.21
](
https://github.com/gflags/gflags/tree/v2.2.1
)
## valgrind
在r30539后,在valgrind中运行程序时加上
**-has_valgrind**
(确保你的程序使用了
[
gflags
](
http://wiki.baidu.com/pages/viewpage.action?pageId=71698818
)
)
> 例如valgrind ./my_program
> -has_valgrind。如果没有开启的话,valgrind无法识别bthread的栈从而可能crash。
>
> 老版本的valgrind(比如3.2)似乎不支持,请使用新一点的版本(比如3.8),可以用[jumbo](http://jumbo.baidu.com/)安装。
在r34944后,-has_valgrind被移除,程序会自动判断是否在valgrind中。
## openssl
r35109后支持1.1
# 新特性
对用户有意义的新特性,以方便用户调研使用。
| 版本 | 功能 | 描述 |
| --------- | --------------------------- | ---------------------------------------- |
|
`r33446 `
| 开启-usercode_in_pthread无死锁风险 | 之前有。 |
|
`r33424 `
| 增加开关-log_hostname |
开启后会在每条日志后加上本机名。对于汇总的日志查询工具有用。 |
|
`r33323 `
| 默认发布工具 | 编译baidu-rpc时rpc_press, rpc_view, rpc_replay,
parallel_http也会一并编译,并能在产品库中获得。要注意的是,产品库默认以gcc
3.
4编译,在新机器上可能无法直接运行,需要对一些so做软链。 |
|
`r33306 `
| 增加工具parallel_http | 可同时访问数万个http url,
远快于curl(即使批量后台运行) |
|
`r32844 `
| 支持http-flv | 另一种广泛用于直播的流媒体协议
|
|
`r32803 `
| 支持同时发起大量异步访问 |
重构了bthread_id_list_t,从静态容量变为了动态容量。 |
|
`r32668 `
| 支持RTMP | 一种广泛用于直播的流媒体协议(仍在完善中)
|
|
`r32560 `
| 支持NamingServiceFilter | 用于过滤名字服务返回的节点列表
|
|
`r32536 `
| 初始化bthread |
`ServerOptions增加了bthread_init_fn等参数用于在server启动前初始化一些bthread。`
|
|
`r32420 `
| 支持nshead_mcpack | 可用protobuf处理nshead+mcpack的协议 |
|
`r32401 `
| 受控的日志打印 |
`LOG_ONCE:只打印一次 LOG_EVERY_N:每过N次打印一次
LOG_EVERY_SECOND:每秒打印一次`
|
|
`r32399 `
| 不可修改的flags | 加上-immutable_flags程序的/flags页面就无法被修改了
|
|
`r32328 `
| 获取RPC延时 |
Controller.latency_us()会返回对应的RPC延时,同步异步都支持。 |
|
`r32301 `
| 显示RTT |
[
/connections
](
http://brpc.baidu.com:8765/connections
)
页面会显示内核统计的smooth RTT了。 |
|
`r32279 `
| 支持凤巢ITP协议 |
详见
[
ITP
](
http://wiki.baidu.com/pages/viewpage.action?pageId=184259578
)
|
|
`r32097`
| 支持Restful开发 |
用户可定制访问每个方法的URL,详见
[
RestfulURL
](
http://wiki.baidu.com/pages/viewpage.action?pageId=213828736#id-实现HTTPService-RestfulURL
)
|
|
`r32034 `
| 支持protobuf 3.0 |
`Server端的Arena分配仍不支持。mcpack2pb,
protobuf-json等周边工具仍待迁移。`
|
|
`r32015 `
| 访问redis-server |
[
访问Redis
](
http://wiki.baidu.com/pages/viewpage.action?pageId=213828705
)
|
|
`r32009`
| RetryPolicy |
可定制重试策略,详见
[
重试
](
http://wiki.baidu.com/pages/viewpage.action?pageId=213828685#id-创建和访问Client-错误值得重试
)
|
|
`r32009 `
| rpc_view |
可在浏览器中查看端口不在
[
8000-8999
]
的内置服务,详见
[
rpc_view
](
http://wiki.baidu.com/pages/viewpage.action?pageId=167651918
)
|
|
`r31986 `
| rpc_press |
代替了pbrpcpress,详见
[
rpc_press
](
http://wiki.baidu.com/pages/viewpage.action?pageId=97645422
)
|
|
`r31901 `
| contention profiler | 可分析在锁上的等待时间,详见
[
contention
profiler](http://wiki.baidu.com/pages/viewpage.action?pageId=165876314) |
|
`r31658 `
| rpc dump & replay |
详见
[
rpc_replay
](
http://wiki.baidu.com/pages/viewpage.action?pageId=158707916
)
|
# FAQ
### Q: baidu-rpc会不会发布稳定版本
本项目是主干开发,最新的改动在
[
trunk
](
https://svn.baidu.com/public/trunk/baidu-rpc/
)
,发布在
[
agile上
](
http://agile.baidu.com/#/builds/public/baidu-rpc@trunk
)
。我们会尽量保持已有接口不变,升级新版本一般不会break代码。由于开发节奏快,我们没有发布Releasing
Branch (RB)的计划。
使用ci-base是更安全的选择。老版本的使用者更稀疏一些,bug会更加隐秘,发现得更晚。这种注意不到的bug会真正影响到策略的判断和迭代。而ci-base你在用,其他产品线也在用,问题很快能被发现和纠正。
如果正在运行的baidu-rpc版本被发现了用户有感的bug,我们会给每个baidu-rpc
server实例推送对应的bug信息,以提示产品线尽快升级,提示内容如下所示:
FATAL: 01-23 11:11:43: adx
*
14542 src/baidu/rpc/trackme.cpp:141] Your baidu-rpc (r34140) is
affected by:
[
r34123-r34140
]
base::IOBuf::append(Movable) may leak memory;
### Q: SSL相关的链接问题
在1.0.171.31586之
**前**
的版本中,可能出现如下的core,请在COMAKE的LDFLAGS中去掉-lssl
-lcrypto,加上-ldl -lz。原因在于baidu-rpc链接的third-64/openssl和系统提供的冲突。
在1.0.171.31586之
**后**
的版本中,为了提高对不同平台的兼容性,baidu-rpc不再静态链接openssl,可能在链接时出现undefined
reference,请在COMAKE的LDFLAGS中加上-lssl -lcrypto。
```
#0 0x000000302af6f950 in strcmp () from /lib64/tls/libc.so.6
#1 0x000000302cc992a3 in OBJ_NAME_new_index () from /lib64/libcrypto.so.4
#2 0x000000302cc959a0 in lh_free () from /lib64/libcrypto.so.4
#3 0x000000302cc95cc2 in lh_insert () from /lib64/libcrypto.so.4
#4 0x000000302cc99469 in OBJ_NAME_add () from /lib64/libcrypto.so.4
#5 0x000000000078a8a5 in SSL_library_init () at webfoot_item.cpp:21
#6 0x0000000000613b0d in RegisterAllExtensionsOrDieImpl () at
src/baidu/rpc/policy/register_all_extensions.cpp:97
#7 0x000000302b809a10 in pthread_once () from /lib64/tls/libpthread.so.0
#8 0x0000000000613a31 in baidu::rpc::policy::RegisterAllExtensionsOrDie () at
src/baidu/rpc/policy/register_all_extensions.cpp:238
#9 0x00000000005ccaac in Channel (this=0xb69c40) at src/baidu/rpc/channel.cpp:68
#10 0x000000000059e0f8 in __static_initialization_and_destruction_0 (__initialize_p=1,
__priority=65535) at test_ei_srv_client.cpp:8
#11 0x000000000059e159 in global constructors keyed to main () at test_ei_srv_client.cpp:19
#12 0x0000000000903696 in __do_global_ctors_aux () at ./src/base/spinlock.h:49
#13 0x000000000059b028 in _init ()
#14 0x0000000000903610 in __libc_csu_init () at ./src/base/spinlock.h:49
#15 0x00000000009035d1 in __libc_csu_init () at ./src/base/spinlock.h:49
#16 0x000000302af1c45f in __libc_start_main () from /lib64/tls/libc.so.6
#17 0x000000000059deaa in _start ()
#18 0x0000007fbffff1a8 in ?? ()
#19 0x000000000000001c in ?? ()
```
### Q: 为什么C++ client/server 能够互相通信, 和其他语言的client/server 通信会报序列化失败的错误
检查一下C++ 版本是否开启了压缩 (Controller::set_compress_type), 目前 python/JAVA
版的rpc框架还没有实现压缩,互相返回会出现问题。
### Q: 两个产品线都使用protobuf,为什么不能互相访问
协议 !=
protobuf。protobuf负责打包,协议负责定字段。打包格式相同不意味着字段可以互通。协议中可能会包含多个protobuf包,以及额外的长度、校验码、magic
number等等。协议的互通是通过在RPC框架内转化为统一的编程接口完成的,而不是在protobuf层面。从广义上来说,protobuf也可以作为打包框架使用,生成其他序列化格式的包,像
[
idl
<
=
>
protobuf](http://wiki.baidu.com/pages/viewpage.action?pageId=144820547)就是通过protobuf生成了解析idl的代码。
### Q: protobuf打印了UTF-8相关的错误日志
```
// protobuf 2.4
google/protobuf/wire_format.cc:1059] Encountered string containing invalid UTF-8 data while parsing
protocol buffer. Strings must contain only UTF-8; use the 'bytes' type for raw bytes.
// protobuf 2.6
google/protobuf/wire_format.cc:1091] String field 'key' contains invalid UTF-8 data when serializing
a protocol buffer. Use the 'bytes' type if you intend to send raw bytes
```
原因:pb中的string必须是utf-8编码,非utf-8编码的字符串必须用bytes存储。
解决方式:
1.
[
推荐
]
把proto中对应字段的类型从string改为bytes。string和bytes的二进制格式是一样的,所以这个改动不会造成新老消息的不兼容。这两个类型生成的函数也是一样的,用户代码不需要修改。
2.
定义宏NDEBUG。这个检查会被跳过。
注意:pb 2.4不会打印出问题的字段名,pb 2.6会,如果你需要快速定位出问题的字段,用pb 2.6
docs/overview.md
0 → 100644
View file @
ed52e0b9
P/IP协议](http://en.wikipedia.org/wiki/Internet_protocol_suite)相互访问,但TCP/IP只是往远端发送了一段二进制数据,相比日常需求还有很多问题需要抽象:
-
数据以什么格式传输?不同机器间,网络间可能是不同的字节序,把C++
struct直接作为数据传输显然是不合适的;随着业务变化,数据字段往往要增加或删减,怎么兼容前后不同版本的格式?
-
一个TCP连接只能传输一份数据么?能复用吗?
-
如何连接集群?如何从集群中选出一台机器发送数据?
-
连接断开时应该干什么?万一server不发送回复怎么办?
-
...
[
RPC
](
http://en.wikipedia.org/wiki/Remote_procedure_call
)
可以解决这些问题,它把网络交互类比为“client访问server上的函数”:client向server发送request后开始等待,直到server收到、处理、回复client后,client又再度恢复并根据response做出反应。
![
img
](
http://wiki.baidu.com/download/thumbnails/71337070/Client-server.jpg?version=1&modificationDate=1427941174000&api=v2
)
我们来看看上面的一些问题是如何解决的:
-
RPC需要序列化,目前公司内定为protobuf。用户填写protobuf::Message类型的request,RPC结束后,从同为protobuf::Message类型的response中取出结果。protobuf有较好的前后兼容性,方便业务调整字段。我们强烈建议使用protobuf描述业务数据。
-
用户不需要关心连接是如何建立的,但用户可以选择不同的连接方式:短连接,连接池,单连接。
-
用户一般通过名字服务发现一个集群中的所有机器,常用的有
[
BNS
](
http://devops.baidu.com/new/bns/index.md
)
,以前有
[
galileo
](
http://wiki.babel.baidu.com/twiki/bin/view/Com/Gm/ResourceRouterHome
)
。用户可以指定负载均衡算法,让RPC每次选出一台机器发送请求。
-
连接断开时按用户的配置进行重试。如果server没有在给定时间内返回response,那么client会返回超时错误。
RPC框架作为最基本的网络通讯组件,需要具备优秀的稳定性、可维护性和扩展性。在开发本框架前,公司内外主要有如下RPC实现:
-
UB
:com组(INF前身)在08年开发的RPC框架,目前仍广泛使用,经历了大量的线上考验,稳定性优秀。UB的主要问题是依赖众多、扩展性不佳,以及缺乏调试工具。用户往往只需要UB中的一部分功能而不得不依赖大量模块,由于百度的代码库不是单根,这带来了复杂的依赖打平问题。UB每个请求都要独占一个连接,在大规模服务中每台机器都需要保持大量的连接,限制了其使用场景(比如INF自己的分布式系统都不用UB)。UB只支持nshead+mcpack协议,也没有太多考虑扩展接口,所以增加新协议和新功能往往要调整大段代码,在实践中大部分人都“知难而退”了。UB也缺乏调试和运维接口,服务的运行状态对用户基本是黑盒,只能靠低效的打日志来追踪问题,产品线服务出现问题时总是要拉上UB的维护同学一起排查,效率很低。UB有多个变种:
-
ubrpc:INF在10年基于UB开发的RPC框架,用idl文件描述数据的schema,而不是在代码中手动打包。这个RPC有被使用,但不广泛。
-
[
nova-pbrpc
](
http://websvn.work.baidu.com/repos/app_ecom_nova/list/trunk/public/pb-rpc
)
:网盟在12年基于UB开发的RPC框架,用protobuf代替mcpack作为序列化方法,协议是nshead
+
user's protobuf。
-
[
public/pbrpc
](
http://websvn.work.baidu.com/repos/public/list/trunk/pbrpc
)
:INF在13年初基于UB开发的RPC框架,用protobuf代替mcpack作为序列化方法,但协议与nova-pbrpc不同,大致是nshead
+
meta protobuf。meta protobuf中有个string字段包含user's
protobuf。由于用户数据要序列化两次,这个RPC的性能很差,基本没有被推广开。
-
[
hulu-pbrpc
](
https://svn.baidu.com/public/trunk/hulu/pbrpc/
)
:INF在13年基于saber(kylin变种)和protobuf实现的RPC框架。hulu尝试区分了机制(pbrpc)和策略(huluwa),减轻了依赖问题。但在实现上有较多问题:未封装的引用计数,混乱的生命周期,充斥的多线程问题(race
conditions & ABA
problems),运行质量很不可靠,比如hulu的短链接从来没有能正常运行过。其编程接口强依赖saber,很多功能的划分不够清晰,比如增加新协议就需要同时修改saber和pbrpc,扩展新功能仍然很困难。hulu增加了http协议的内置服务,可以提供一些简单的内部运行状态,但对于排查问题还比较简陋。hulu支持和一个server只保持一个连接,相比UB可以节省连接数,但由于多线程实现粗糙,读写不够并发,hulu的性能反而不如UB。
-
[
sofa-pbrpc
](
https://svn.baidu.com/public/trunk/sofa-pbrpc/
)
:PS在13年基于boost::asio和protobuf实现的RPC框架,这个库代码工整,接口清晰,支持同步和异步,有非HTTP协议的调试接口(最新版也支持HTTP了)。但sofa-pbrpc也有产品线自研框架的鲜明特点:不支持公司内的其他协议,对名字服务、负载均衡、服务认证、连接方式等多样化的需求的抽象不够一般化。sofa-pbrpc还对外发布了开源版本。
-
thrift:facebook最早在07年开发的RPC框架,后转为
[
apache的开源项目
](
https://thrift.apache.org/
)
。包含独特的序列化格式和IDL,支持很多编程语言。thrift让网络上的开发者能完成基本的RPC通讯,但也仅限于此了。thrift的代码看似分层很清楚,client、server选择很多,但没有一个足够通用,每个server实现都只能解决很小一块场景,每个client都线程不安全。实际使用中非常麻烦。thrift的代码质量也比较差,接口和生成的代码都比较乱。
基于这些观察,我们感到有必要开发一套新的RPC框架,即
**baidu-rpc**
,以方便各类用户开发、调试、运维网络服务,满足产品线之间对平台化、接口化日益高涨的需求,能适应核数越来越多的硬件架构,性能远好于其他实现。请注意,本文档主要指C++版本的baidu-rpc。目前的
[
pbrpc4j
](
http://websvn.work.baidu.com/repos/public/list/trunk/javalib/pbrpc4j/?revision=HEAD&bypassEmpty=true
)
基于netty改造,功能较少,我们可能会也可能不会把它作为baidu-rpc的java版。python有
[
client实现
](
https://svn.baidu.com/public/tags/pythonlib/pbrpc/pbrpc_1-0-2-5_PD_BL/
)
和
[
server实现
](
http://wiki.baidu.com/display/RPC/Python+Server
)
。php目前只支持client。
# 目标
-
提供稳定的RPC框架。
-
适用各类业务场景,提供优秀的延时,吞吐,并发度,具备优秀的多核扩展性。
-
接口易懂,用户体验佳。有完备的调试和运维接口(HTTP)。
-
开发习惯好(注释、日志、changelog),有全面的单元、模块、集成测试,能作为编码示例。
# 假设
-
用户对访问延时,吞吐,并发度,分库等指标及功能有广泛而多样的需求。
-
机器以Intel x86_64为主。
-
下游服务规模可能到数万个节点,机器和网络状况多样化。
# 应用场景
几乎所有的网络交互。
RPC不是万能的抽象,否则我们也不需要TCP/IP这一层了。但是在我们绝大部分的网络交互中,RPC既能解决问题,又能隔离更底层的网络问题。对于RPC常见的质疑有:
-
我的数据非常大,用protobuf序列化太慢了。首先这可能是个伪命题,你得用
[
profiler
](
http://wiki.baidu.com/display/RPC/cpu+profiler
)
证明慢了才是真的慢,其次一些协议支持附件,你可以在传递protobuf请求时附带二进制数据。
-
我传输的是流数据,RPC表达不了。baidu-rpc支持
[
Streaming
RPC](http://wiki.baidu.com/pages/viewpage.action?pageId=152229270),这可以表达。
-
我的场景不需要回复。简单推理可知,你的场景中请求可丢可不丢,可处理也可不处理,因为client总是无法感知,你真的确认这是OK的?即使场景真的不需要,我们仍然建议用最小的结构体回复,因为这不大会是瓶颈,并且在出问题时让你有一些线索,否则真的是盲人摸象。
# 优势
### 使用更简单的接口
我们只有三个用户类:
[
Server
](
https://svn.baidu.com/public/trunk/baidu-rpc/src/baidu/rpc/server.h
)
,
[
Channel
](
https://svn.baidu.com/public/trunk/baidu-rpc/src/baidu/rpc/channel.h?revision=HEAD
)
,
[
Controller
](
https://svn.baidu.com/public/trunk/baidu-rpc/src/baidu/rpc/controller.h?revision=HEAD
)
,分别对应server端,client端,和调整参数集合。你不需要推敲诸如“Client怎么初始化”,“XXXManager有什么用”,“Context和Controller的关系是什么“之类的问题,你要做的很简单:
-
建服务就#include
<
[
baidu
/
rpc
/
server
.
h
](
https:
//
svn
.
baidu
.
com
/
public
/
trunk
/
baidu-rpc
/
src
/
baidu
/
rpc
/
server
.
h
)
>
,并按照注释或例子使用Server对象。
-
访问服务就#include
<
[
baidu
/
rpc
/
channel
.
h
](
https:
//
svn
.
baidu
.
com
/
public
/
trunk
/
baidu-rpc
/
src
/
baidu
/
rpc
/
channel
.
h
)
>
,并按照注释或例子使用Channel对象。
-
想控制一次RPC访问的参数,就看看
[
baidu/rpc/controller.h
](
https://svn.baidu.com/public/trunk/baidu-rpc/src/baidu/rpc/controller.h
)
中到底有些什么。请注意,这个类是Server和Channel共用的,其中分成了三段,分别标记为Client-side,
Server-side和Both-side methods。
我们尝试让事情变得更加简单,以名字服务为例,在其他RPC实现中,你也许需要复制一长段晦涩的代码才可使用,而在baidu-rpc中访问BNS可以这么写"bns://node-name",
本地文件列表可以这么写"file:///home/work/server.list",相信不用我解释,你也能明白这些代表什么,这个字串可以放在配置文件中,方便地载入并使用。
### 访问各种服务,被各种服务访问
baidu-rpc能访问百度内所有基于protobuf的RPC server实现,能
[
访问ub
server](http://wiki.baidu.com/pages/viewpage.action?pageId=213828700)(idl/mcpack/compack)
baidu-rpc能被百度内所有基于protobuf的RPC
client访问,能被HTTP+json访问,通过一些额外代码可以被UB访问。
### 使服务更加可靠
我们的开发机制能持续地保证高质量:
-
分离机制(mechanism)和策略(policy):baidu-rpc把可扩展的部分都抽象为了策略,并放在了
[
单独的目录
](
https://svn.baidu.com/public/trunk/baidu-rpc/src/baidu/rpc/policy/
)
,具体的协议支持,压缩算法,名字服务,负载均衡都是策略,如果你想二次开发,可以很容易找到模板并开始上手。就像在算法问题中O(M
*
N)问题变为了O(M+N)那样,这有效地降低了baidu-rpc整体的复杂度,使得我们可以把精力集中到最核心的代码上。
-
完整的单元和集成测试:baidu-rpc有完整的单元测试,集成测试,系统测试和性能测试,在我们每次check
in代码后都会运行,这确保我们在开发阶段可以及时规避问题。
-
全面的调试和监控手段:baidu-rpc特别重视开发和运维,你可以使用浏览器或curl
[
查询服务器内部状态
](
http://wiki.baidu.com/display/RPC/Builtin+Services
)
,你也可以用pprof
[
分析在线服务的性能
](
http://wiki.baidu.com/display/RPC/cpu+profiler
)
。你可以
[
用bvar计数
](
http://wiki.baidu.com/display/RPC/bvar
)
,相比缓慢的ubmonitor几乎没有性能损耗,同时bvar能以/vars访问,这改变了我们监控在线服务的方式
-
新基础库:baidu-rpc依赖的
[
C++
base](http://wiki.baidu.com/pages/viewpage.action?pageId=38035224)改造自chromium,是百度的新基础库。
### 获得更好的延时和吞吐
虽然大部分RPC实现都号称“高性能”,但仅仅是在测试中有不错的数字,在广泛的场景中确保高性能仍是困难的,为了做到这一点,baidu-rpc有比其他RPC实现更深的技术考量,会确保以最快的速度把请求分发到合适的位置,具体来说:
-
**对不同客户端请求的读取和解析是完全并发的,用户也不用区分”IO线程“和”处理线程"**
。听上去简单,但几乎没有做到的。大部分实现会区分“IO线程”和“处理线程”,并把
[
fd
](
http://en.wikipedia.org/wiki/File_descriptor
)
(对应一个客户端)散列到IO线程中去。这听上不错,实则很糟糕。首先当一个IO线程在读取其中的fd时,同一个线程中的fd都无法得到处理。有些实现(ubaserver)不扣除关闭的fd,那么在一段时间后,不同IO线程中的fd将很不均匀,一个fd的影响范围将更大。当一些解析变慢时,比如特别大的protobuf
message,同一个IO线程中的其他fd都遭殃了。虽然不同IO线程间的fd是并发的,但由于你不太可能开太多IO线程(这类线程的事情很少,大部分时候都是闲着的),10个就比较多了,所以一个fd能影响到的”其他fd“仍有相当大的比例(10个即10%,在线检索可是要求99.99%的)。将来的RPC框架将广泛地用于多租户(multi-tenacy)环境,一个数据复杂的业务如果严重干扰到其他业务的SLA,将是无法接受的。在baidu-rpc中,对不同fd的读取是完全并发的,对同一个fd中不同消息的解析也是并发的
**。**
比如一个特别大的protobuf
message在解析时不会影响同一个客户端的其他消息,更不用提其他客户端的消息了。
-
**只要cpu和工作线程有剩余,新请求会在O(1)时间内得到处理**
。由于大部分业务请求都要花费很多毫秒,即使只有一个任务由于调度的原因而得不到及时的处理,都是无法接受的。以hulu为例,有两种任务分发模式:一种是分发到固定线程,另一种是通过condition。前者没有同步开销,对于大流量效果更好,但无法保证本要求。后者有高强度的同步开销,吞吐不会太高,但可以保证本要求。这让用户很纠结。在baidu-rpc中总是能保证本要求,并且性能好于hulu。
-
**对同一fd和不同fd的写出是高度并发的**
。当多个线程都要对一个fd写出时(常见于单连接),第一个线程会直接在原线程写出,其他线程会以
[
wait-free
](
http://en.wikipedia.org/wiki/Non-blocking_algorithm#Wait-freedom
)
的方式托付自己的写请求,多个线程在高度竞争下仍可以在1秒内对同一个fd写入500万个16字节的消息,这很可能是目前最高性能的同fd写出实现。对不同fd的写出总是并发的。值得一提的是,我们在未来可能会用类似的技术实现往磁盘写的Channel,以满足一些服务器对超高频率日志的需求。
-
**服务器线程数自动调节**
。传统的服务器需要根据下游延时的调整自身的线程数,否则吞吐可能会受影响,一般是通过配置。在baidu-rpc中,每个请求均运行在新建立的bthread中,请求结束后线程就结束了,所以天然会根据负载自动调节线程数。
baidu-rpc背后的技术和知识请阅读
[
深入RPC
](
http://wiki.baidu.com/display/RPC/RPC+in+Depth
)
,和其他RPC实现的性能对比请见
[
Benchmark
](
http://wiki.baidu.com/pages/viewpage.action?pageId=48480483
)
。
### 获得独有的功能
如果你有任何需求,都可以向我们提:这并不意味着我们会全盘接受,我们也许会提议另一种解决方案,或指出一些理解的误区,不过这终究可以使想法得到更多打磨,产生新的火花。灵活的内部结构使我们可以更快地添加新功能,有一些功能是其他RPC实现很难拥有的,举例来说:
-
编写和访问HTTP server:你写的任何protobuf
server都可以在移动端用HTTP+json访问,你也可以写纯的HTTP/HTTPS server或者访问其他HTTP server。
-
backup request: 又称tied requests,是一种降低请求延时的技术。
-
[
Locality-aware load balancer
](
http://wiki.baidu.com/display/RPC/Locality-aware+load+balancing
)
:
这种负载均衡算法会优先选择最近的服务器,即同机 > 同rack > 同机房 >
跨机房,并在近节点发生故障时快速收敛,当节点大量混部时,这可以有效地降低延时,并规避网络抖动。
-
[
内置服务
](
http://wiki.baidu.com/display/RPC/Builtin+Services
)
:
彻底改变开发和调试体验的强大工具!配合
[
bvar
](
http://wiki.baidu.com/display/RPC/bvar
)
更贴心。
-
[
组合访问
](
http://wiki.baidu.com/pages/viewpage.action?pageId=213828709
)
:并发访问,分库分环,从没有这么简单过。
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment