Unverified Commit 9f4c1920 authored by Ge Jun's avatar Ge Jun Committed by GitHub

Merge pull request #521 from brpc/h2_docs

Add h2/grpc docs
parents ea0a56d7 d1da7627
...@@ -8,14 +8,13 @@ An industrial-grade RPC framework used throughout [Baidu](http://ir.baidu.com/ph ...@@ -8,14 +8,13 @@ An industrial-grade RPC framework used throughout [Baidu](http://ir.baidu.com/ph
You can use it to: You can use it to:
* Build a server that can talk in multiple protocols (**on same port**), or access all sorts of services * Build a server that can talk in multiple protocols (**on same port**), or access all sorts of services
* restful http/https, h2/h2c (compatible with [grpc](https://github.com/grpc/grpc), will be opensourced). using http in brpc is much more friendly than [libcurl](https://curl.haxx.se/libcurl/). * restful http/https, [h2](https://http2.github.io/http2-spec)/[gRPC](https://grpc.io). using http/h2 in brpc is much more friendly than [libcurl](https://curl.haxx.se/libcurl/). Access protobuf-based protocols with HTTP/h2+json, probably from another language.
* [redis](docs/en/redis_client.md) and [memcached](docs/en/memcache_client.md), thread-safe, more friendly and performant than the official clients. * [redis](docs/en/redis_client.md) and [memcached](docs/en/memcache_client.md), thread-safe, more friendly and performant than the official clients.
* [rtmp](https://github.com/brpc/brpc/blob/master/src/brpc/rtmp.h)/[flv](https://en.wikipedia.org/wiki/Flash_Video)/[hls](https://en.wikipedia.org/wiki/HTTP_Live_Streaming), for building [streaming services](https://github.com/brpc/media-server). * [rtmp](https://github.com/brpc/brpc/blob/master/src/brpc/rtmp.h)/[flv](https://en.wikipedia.org/wiki/Flash_Video)/[hls](https://en.wikipedia.org/wiki/HTTP_Live_Streaming), for building [streaming services](https://github.com/brpc/media-server).
* hadoop_rpc (may be opensourced) * hadoop_rpc (may be opensourced)
* [rdma](https://en.wikipedia.org/wiki/Remote_direct_memory_access) support (will be opensourced) * [rdma](https://en.wikipedia.org/wiki/Remote_direct_memory_access) support (will be opensourced)
* [thrift](docs/en/thrift.md) support, thread-safe, more friendly and performant than the official clients. * [thrift](docs/en/thrift.md) support, thread-safe, more friendly and performant than the official clients.
* all sorts of protocols used in Baidu: [baidu_std](docs/cn/baidu_std.md), [streaming_rpc](docs/en/streaming_rpc.md), hulu_pbrpc, [sofa_pbrpc](https://github.com/baidu/sofa-pbrpc), nova_pbrpc, public_pbrpc, ubrpc and nshead-based ones. * all sorts of protocols used in Baidu: [baidu_std](docs/cn/baidu_std.md), [streaming_rpc](docs/en/streaming_rpc.md), hulu_pbrpc, [sofa_pbrpc](https://github.com/baidu/sofa-pbrpc), nova_pbrpc, public_pbrpc, ubrpc and nshead-based ones.
* Access protobuf-based protocols with HTTP+json, probably from another language.
* Build [HA](https://en.wikipedia.org/wiki/High_availability) distributed services using an industrial-grade implementation of [RAFT consensus algorithm](https://raft.github.io) which is opensourced at [braft](https://github.com/brpc/braft) * Build [HA](https://en.wikipedia.org/wiki/High_availability) distributed services using an industrial-grade implementation of [RAFT consensus algorithm](https://raft.github.io) which is opensourced at [braft](https://github.com/brpc/braft)
* Servers can handle requests [synchronously](docs/en/server.md) or [asynchronously](docs/en/server.md#asynchronous-service). * Servers can handle requests [synchronously](docs/en/server.md) or [asynchronously](docs/en/server.md#asynchronous-service).
* Clients can access servers [synchronously](docs/en/client.md#synchronus-call), [asynchronously](docs/en/client.md#asynchronous-call), [semi-synchronously](docs/en/client.md#semi-synchronous-call), or use [combo channels](docs/en/combo_channel.md) to simplify sharded or parallel accesses declaratively. * Clients can access servers [synchronously](docs/en/client.md#synchronus-call), [asynchronously](docs/en/client.md#asynchronous-call), [semi-synchronously](docs/en/client.md#semi-synchronous-call), or use [combo channels](docs/en/combo_channel.md) to simplify sharded or parallel accesses declaratively.
...@@ -39,7 +38,8 @@ You can use it to: ...@@ -39,7 +38,8 @@ You can use it to:
* [Basics](docs/en/client.md) * [Basics](docs/en/client.md)
* [Error code](docs/en/error_code.md) * [Error code](docs/en/error_code.md)
* [Combo channels](docs/en/combo_channel.md) * [Combo channels](docs/en/combo_channel.md)
* [Access HTTP](docs/en/http_client.md) * [Access http/h2](docs/en/http_client.md)
* [Access gRPC](docs/en/http_derivatives.md#h2grpc)
* [Access thrift](docs/en/thrift.md#client-accesses-thrift-server) * [Access thrift](docs/en/thrift.md#client-accesses-thrift-server)
* [Access UB](docs/cn/ub_client.md) * [Access UB](docs/cn/ub_client.md)
* [Streaming RPC](docs/en/streaming_rpc.md) * [Streaming RPC](docs/en/streaming_rpc.md)
...@@ -49,7 +49,8 @@ You can use it to: ...@@ -49,7 +49,8 @@ You can use it to:
* [Dummy server](docs/en/dummy_server.md) * [Dummy server](docs/en/dummy_server.md)
* Server * Server
* [Basics](docs/en/server.md) * [Basics](docs/en/server.md)
* [Serve HTTP](docs/en/http_service.md) * [Serve http/h2](docs/en/http_service.md)
* [Serve gRPC](docs/en/http_derivatives.md#h2grpc)
* [Serve thrift](docs/en/thrift.md#server-processes-thrift-requests) * [Serve thrift](docs/en/thrift.md#server-processes-thrift-requests)
* [Serve Nshead](docs/cn/nshead_service.md) * [Serve Nshead](docs/cn/nshead_service.md)
* [Debug server issues](docs/cn/server_debugging.md) * [Debug server issues](docs/cn/server_debugging.md)
......
...@@ -9,14 +9,13 @@ ...@@ -9,14 +9,13 @@
你可以使用它: 你可以使用它:
* 搭建能在**一个端口**支持多协议的服务, 或访问各种服务 * 搭建能在**一个端口**支持多协议的服务, 或访问各种服务
* restful http/https, h2/h2c (与[grpc](https://github.com/grpc/grpc)兼容, 即将开源). 使用brpc的http实现比[libcurl](https://curl.haxx.se/libcurl/)方便多了。 * restful http/https, [h2](https://http2.github.io/http2-spec)/[gRPC](https://grpc.io)。使用brpc的http实现比[libcurl](https://curl.haxx.se/libcurl/)方便多了。从其他语言通过HTTP/h2+json访问基于protobuf的协议.
* [redis](docs/cn/redis_client.md)[memcached](docs/cn/memcache_client.md), 线程安全,比官方client更方便。 * [redis](docs/cn/redis_client.md)[memcached](docs/cn/memcache_client.md), 线程安全,比官方client更方便。
* [rtmp](https://github.com/brpc/brpc/blob/master/src/brpc/rtmp.h)/[flv](https://en.wikipedia.org/wiki/Flash_Video)/[hls](https://en.wikipedia.org/wiki/HTTP_Live_Streaming), 可用于搭建[流媒体服务](https://github.com/brpc/media-server). * [rtmp](https://github.com/brpc/brpc/blob/master/src/brpc/rtmp.h)/[flv](https://en.wikipedia.org/wiki/Flash_Video)/[hls](https://en.wikipedia.org/wiki/HTTP_Live_Streaming), 可用于搭建[流媒体服务](https://github.com/brpc/media-server).
* hadoop_rpc(可能开源) * hadoop_rpc(可能开源)
* 支持[rdma](https://en.wikipedia.org/wiki/Remote_direct_memory_access)(即将开源) * 支持[rdma](https://en.wikipedia.org/wiki/Remote_direct_memory_access)(即将开源)
* 支持[thrift](docs/cn/thrift.md) , 线程安全,比官方client更方便 * 支持[thrift](docs/cn/thrift.md) , 线程安全,比官方client更方便
* 各种百度内使用的协议: [baidu_std](docs/cn/baidu_std.md), [streaming_rpc](docs/cn/streaming_rpc.md), hulu_pbrpc, [sofa_pbrpc](https://github.com/baidu/sofa-pbrpc), nova_pbrpc, public_pbrpc, ubrpc和使用nshead的各种协议. * 各种百度内使用的协议: [baidu_std](docs/cn/baidu_std.md), [streaming_rpc](docs/cn/streaming_rpc.md), hulu_pbrpc, [sofa_pbrpc](https://github.com/baidu/sofa-pbrpc), nova_pbrpc, public_pbrpc, ubrpc和使用nshead的各种协议.
* 从其他语言通过HTTP+json访问基于protobuf的协议.
* 基于工业级的[RAFT算法](https://raft.github.io)实现搭建[高可用](https://en.wikipedia.org/wiki/High_availability)分布式系统,已在[braft](https://github.com/brpc/braft)开源。 * 基于工业级的[RAFT算法](https://raft.github.io)实现搭建[高可用](https://en.wikipedia.org/wiki/High_availability)分布式系统,已在[braft](https://github.com/brpc/braft)开源。
* Server能[同步](docs/cn/server.md)[异步](docs/cn/server.md#异步service)处理请求。 * Server能[同步](docs/cn/server.md)[异步](docs/cn/server.md#异步service)处理请求。
* Client支持[同步](docs/cn/client.md#同步访问)[异步](docs/cn/client.md#异步访问)[半同步](docs/cn/client.md#半同步),或使用[组合channels](docs/cn/combo_channel.md)简化复杂的分库或并发访问。 * Client支持[同步](docs/cn/client.md#同步访问)[异步](docs/cn/client.md#异步访问)[半同步](docs/cn/client.md#半同步),或使用[组合channels](docs/cn/combo_channel.md)简化复杂的分库或并发访问。
...@@ -40,7 +39,8 @@ ...@@ -40,7 +39,8 @@
* [基础功能](docs/cn/client.md) * [基础功能](docs/cn/client.md)
* [错误码](docs/cn/error_code.md) * [错误码](docs/cn/error_code.md)
* [组合channels](docs/cn/combo_channel.md) * [组合channels](docs/cn/combo_channel.md)
* [访问HTTP](docs/cn/http_client.md) * [访问http/h2](docs/cn/http_client.md)
* [访问gRPC](docs/cn/http_derivatives.md#h2grpc)
* [访问thrift](docs/cn/thrift.md#client端访问thrift-server) * [访问thrift](docs/cn/thrift.md#client端访问thrift-server)
* [访问UB](docs/cn/ub_client.md) * [访问UB](docs/cn/ub_client.md)
* [Streaming RPC](docs/cn/streaming_rpc.md) * [Streaming RPC](docs/cn/streaming_rpc.md)
...@@ -50,7 +50,8 @@ ...@@ -50,7 +50,8 @@
* [Dummy server](docs/cn/dummy_server.md) * [Dummy server](docs/cn/dummy_server.md)
* Server * Server
* [基础功能](docs/cn/server.md) * [基础功能](docs/cn/server.md)
* [搭建HTTP服务](docs/cn/http_service.md) * [搭建http/h2服务](docs/cn/http_service.md)
* [搭建gRPC服务](docs/cn/http_derivatives.md#h2grpc)
* [搭建thrift服务](docs/cn/thrift.md#server端处理thrift请求) * [搭建thrift服务](docs/cn/thrift.md#server端处理thrift请求)
* [搭建Nshead服务](docs/cn/nshead_service.md) * [搭建Nshead服务](docs/cn/nshead_service.md)
* [高效率排查server卡顿](docs/cn/server_debugging.md) * [高效率排查server卡顿](docs/cn/server_debugging.md)
......
...@@ -2,9 +2,9 @@ NOTE: following tests were done in 2015, which may not reflect latest status of ...@@ -2,9 +2,9 @@ NOTE: following tests were done in 2015, which may not reflect latest status of
# 序言 # 序言
在多核的前提下,性能和线程是紧密联系在一起的。线程间的跳转对高频IO操作的性能有决定性作用:一次跳转意味着至少3-20微秒的延时,由于每个核心的L1 cache独立(我们的cpu L2 cache也是独立的),随之而来是大量的cache miss,一些变量的读取、写入延时会从纳秒级上升几百倍至微秒级:等待cpu把对应的cacheline同步过来。有时这带来了一个出乎意料的结果,当每次的处理都很简短时,一个多线程程序未必比一个单线程程序更快。因为前者可能在每次付出了大的切换代价后只做了一点点“正事”,而后者在不停地做“正事”。不过单线程也是有代价的,它工作良好的前提是“正事”都很快,否则一旦某次变慢就使后续的所有“正事”都被延迟了。在一些处理时间普遍较短的程序中,使用(多个不相交的)单线程能最大程度地”做正事“,由于每个请求的处理时间确定,延时表现也很稳定,各种http server正是这样。但我们的检索服务要做的事情可就复杂多了,有大量的后端服务需要访问,广泛存在的长尾请求使每次处理的时间无法确定,排序策略也越来越复杂。如果还是使用(多个不相交的)单线程的话,一次难以预计的性能抖动,或是一个大请求可能导致后续一堆请求被延迟。 在多核的前提下,性能和线程是紧密联系在一起的。线程间的跳转对高频IO操作的性能有决定性作用: 一次跳转意味着至少3-20微秒的延时,由于每个核心的L1 cache独立(我们的cpu L2 cache也是独立的),随之而来是大量的cache miss,一些变量的读取、写入延时会从纳秒级上升几百倍至微秒级: 等待cpu把对应的cacheline同步过来。有时这带来了一个出乎意料的结果,当每次的处理都很简短时,一个多线程程序未必比一个单线程程序更快。因为前者可能在每次付出了大的切换代价后只做了一点点“正事”,而后者在不停地做“正事”。不过单线程也是有代价的,它工作良好的前提是“正事”都很快,否则一旦某次变慢就使后续的所有“正事”都被延迟了。在一些处理时间普遍较短的程序中,使用(多个不相交的)单线程能最大程度地”做正事“,由于每个请求的处理时间确定,延时表现也很稳定,各种http server正是这样。但我们的检索服务要做的事情可就复杂多了,有大量的后端服务需要访问,广泛存在的长尾请求使每次处理的时间无法确定,排序策略也越来越复杂。如果还是使用(多个不相交的)单线程的话,一次难以预计的性能抖动,或是一个大请求可能导致后续一堆请求被延迟。
为了避免请求之间相互影响,请求级的线程跳转是brpc必须付出的代价,我们能做的是使[线程跳转最优化](io.md#the-full-picture)。不过,对服务的性能测试还不能很好地体现这点。测试中的处理往往极为简单,使得线程切换的影响空前巨大,通过控制多线程和单线程处理的比例,我们可以把一个测试服务的qps从100万到500万操纵自如(同机),这损伤了性能测试结果的可信度。要知道,真实的服务并不是在累加一个数字,或者echo一个字符串,一个qps几百万的echo程序没有指导意义。鉴于此,在发起性能测试一年后(15年底),在brpc又经历了1200多次改动后,我们需要review所有的测试,加强其中的线程因素,以获得对真实场景有明确意义的结果。具体来说 为了避免请求之间相互影响,请求级的线程跳转是brpc必须付出的代价,我们能做的是使[线程跳转最优化](io.md#the-full-picture)。不过,对服务的性能测试还不能很好地体现这点。测试中的处理往往极为简单,使得线程切换的影响空前巨大,通过控制多线程和单线程处理的比例,我们可以把一个测试服务的qps从100万到500万操纵自如(同机),这损伤了性能测试结果的可信度。要知道,真实的服务并不是在累加一个数字,或者echo一个字符串,一个qps几百万的echo程序没有指导意义。鉴于此,在发起性能测试一年后(15年底),在brpc又经历了1200多次改动后,我们需要review所有的测试,加强其中的线程因素,以获得对真实场景有明确意义的结果。具体来说:
- 请求不应等长,要有长尾。这能考察RPC能否让请求并发,否则一个慢请求会影响大量后续请求。 - 请求不应等长,要有长尾。这能考察RPC能否让请求并发,否则一个慢请求会影响大量后续请求。
- 要有多级server的场景。server内用client访问下游server,这能考察server和client的综合表现。 - 要有多级server的场景。server内用client访问下游server,这能考察server和client的综合表现。
...@@ -16,12 +16,12 @@ NOTE: following tests were done in 2015, which may not reflect latest status of ...@@ -16,12 +16,12 @@ NOTE: following tests were done in 2015, which may not reflect latest status of
## UB ## UB
百度在08年开发的RPC框架,在百度产品线广泛使用,已被brpc代替。UB的每个请求独占一个连接(连接池),在大规模服务中每台机器都需要保持大量的连接,限制了其使用场景,像百度的分布式系统没有用UB。UB只支持nshead+mcpack协议,也没怎么考虑扩展性,所以增加新协议和新功能往往要调整大段代码,在实践中大部分人“知难而退”了。UB缺乏调试和运维接口,服务的运行状态对用户基本是黑盒,只能靠低效地打日志来追踪问题,服务出现问题时常要拉上维护者一起排查,效率很低。UB有多个变种 百度在08年开发的RPC框架,在百度产品线广泛使用,已被brpc代替。UB的每个请求独占一个连接(连接池),在大规模服务中每台机器都需要保持大量的连接,限制了其使用场景,像百度的分布式系统没有用UB。UB只支持nshead+mcpack协议,也没怎么考虑扩展性,所以增加新协议和新功能往往要调整大段代码,在实践中大部分人“知难而退”了。UB缺乏调试和运维接口,服务的运行状态对用户基本是黑盒,只能靠低效地打日志来追踪问题,服务出现问题时常要拉上维护者一起排查,效率很低。UB有多个变种:
* ubrpc百度在10年基于UB开发的RPC框架,用.idl文件(类似.proto)描述数据的schema,而不是手动打包。这个RPC有被使用,但不广泛。 * ubrpc: 百度在10年基于UB开发的RPC框架,用.idl文件(类似.proto)描述数据的schema,而不是手动打包。这个RPC有被使用,但不广泛。
- nova_pbrpc百度网盟团队在12年基于UB开发的RPC框架,用protobuf代替mcpack作为序列化方法,协议是nshead + user's protobuf。 - nova_pbrpc: 百度网盟团队在12年基于UB开发的RPC框架,用protobuf代替mcpack作为序列化方法,协议是nshead + user's protobuf。
- public_pbrpc百度在13年初基于UB开发的RPC框架,用protobuf代替mcpack作为序列化方法,但协议与nova_pbrpc不同,大致是nshead + meta protobuf。meta protobuf中有个string字段包含user's protobuf。由于用户数据要序列化两次,这个RPC的性能很差,没有被推广开来。 - public_pbrpc: 百度在13年初基于UB开发的RPC框架,用protobuf代替mcpack作为序列化方法,但协议与nova_pbrpc不同,大致是nshead + meta protobuf。meta protobuf中有个string字段包含user's protobuf。由于用户数据要序列化两次,这个RPC的性能很差,没有被推广开来。
我们以在百度网盟团队广泛使用的nova_pbrpc为UB的代表。测试时其代码为r10500。早期的UB支持CPOOL和XPOOL,分别使用[select](http://linux.die.net/man/2/select)[leader-follower模型](http://kircher-schwanninger.de/michael/publications/lf.pdf),后来提供了EPOLL,使用[epoll](http://man7.org/linux/man-pages/man7/epoll.7.html)处理多路连接。鉴于产品线大都是用EPOLL模型,我们的UB配置也使用EPOLL。UB只支持[连接池](client.md#连接方式),结果用“**ubrpc_mc**"指代(mc代表"multiple 我们以在百度网盟团队广泛使用的nova_pbrpc为UB的代表。测试时其代码为r10500。早期的UB支持CPOOL和XPOOL,分别使用[select](http://linux.die.net/man/2/select)[leader-follower模型](http://kircher-schwanninger.de/michael/publications/lf.pdf),后来提供了EPOLL,使用[epoll](http://man7.org/linux/man-pages/man7/epoll.7.html)处理多路连接。鉴于产品线大都是用EPOLL模型,我们的UB配置也使用EPOLL。UB只支持[连接池](client.md#连接方式),结果用“**ubrpc_mc**"指代(mc代表"multiple
connection")。虽然这个名称不太准确(见上文对ubrpc的介绍),但在本文的语境下,请默认ubrpc = UB。 connection")。虽然这个名称不太准确(见上文对ubrpc的介绍),但在本文的语境下,请默认ubrpc = UB。
...@@ -40,11 +40,11 @@ INF在2014年底开发至今的rpc产品,支持百度内所有协议(不限 ...@@ -40,11 +40,11 @@ INF在2014年底开发至今的rpc产品,支持百度内所有协议(不限
## apache thrift ## apache thrift
thrift是由facebook最早在07年开发的序列化方法和rpc框架,包含独特的序列化格式和IDL,支持很多编程语言。开源后改名[apache thrift](https://thrift.apache.org/),fb自己有一个[fbthrift分支](https://github.com/facebook/fbthrift),我们使用的是apache thrift。测试时其代码为`thrift_0-9-1-400_PD_BL`。thrift的缺点是:代码看似分层清晰,client和server选择很多,但没有一个足够通用,每个server实现都只能解决很小一块场景,每个client都线程不安全,实际使用很麻烦。由于thrift没有线程安全的client,所以每个线程中都得建立一个client,使用独立的连接。在测试中thrift其实是占了其他实现的便宜:它的client不需要处理多线程问题。thrift的结果用"**thrift_mc**"指代。 thrift是由facebook最早在07年开发的序列化方法和rpc框架,包含独特的序列化格式和IDL,支持很多编程语言。开源后改名[apache thrift](https://thrift.apache.org/),fb自己有一个[fbthrift分支](https://github.com/facebook/fbthrift),我们使用的是apache thrift。测试时其代码为`thrift_0-9-1-400_PD_BL`。thrift的缺点是: 代码看似分层清晰,client和server选择很多,但没有一个足够通用,每个server实现都只能解决很小一块场景,每个client都线程不安全,实际使用很麻烦。由于thrift没有线程安全的client,所以每个线程中都得建立一个client,使用独立的连接。在测试中thrift其实是占了其他实现的便宜: 它的client不需要处理多线程问题。thrift的结果用"**thrift_mc**"指代。
## grpc ## gRPC
由google开发的rpc框架,使用http/2和protobuf 3.0,测试时其代码为<https://github.com/grpc/grpc/tree/release-0_11>。grpc并不是stubby,定位更像是为了推广http/2和protobuf 3.0,但鉴于很多人对它的表现很感兴趣,我们也(很麻烦地)把它加了进来。grpc的结果用"**grpc**"指代。 由google开发的rpc框架,使用http/2和protobuf 3.0,测试时其代码为<https://github.com/grpc/grpc/tree/release-0_11>。gRPC并不是stubby,定位更像是为了推广http/2和protobuf 3.0,但鉴于很多人对它的表现很感兴趣,我们也(很麻烦地)把它加了进来。gRPC的结果用"**grpc**"指代。
# 测试方法 # 测试方法
...@@ -54,7 +54,7 @@ thrift是由facebook最早在07年开发的序列化方法和rpc框架,包含 ...@@ -54,7 +54,7 @@ thrift是由facebook最早在07年开发的序列化方法和rpc框架,包含
在百度的环境中,这是句大白话,哪个产品线,哪个系统没有长尾呢?作为承载大部分服务的RPC框架自然得处理好长尾,减少长尾对正常请求的影响。但在实现层面,这个问题对设计的影响太大了。如果测试中没有长尾,那么RPC实现就可以假设每个请求都差不多快,这时候最优的方法是用多个线程独立地处理请求。由于没有上下文切换和cache一致性同步,程序的性能会显著高于多个线程协作时的表现。 在百度的环境中,这是句大白话,哪个产品线,哪个系统没有长尾呢?作为承载大部分服务的RPC框架自然得处理好长尾,减少长尾对正常请求的影响。但在实现层面,这个问题对设计的影响太大了。如果测试中没有长尾,那么RPC实现就可以假设每个请求都差不多快,这时候最优的方法是用多个线程独立地处理请求。由于没有上下文切换和cache一致性同步,程序的性能会显著高于多个线程协作时的表现。
比如简单的echo程序,处理一个请求只需要200-300纳秒,单个线程可以达到300-500万的吞吐。但如果多个线程协作,即使在极其流畅的系统中,也要付出3-5微秒的上下文切换代价和1微秒的cache同步代价,这还没有考虑多个线程间的其他互斥逻辑,一般来说单个线程的吞吐很难超过10万,即使24核全部用满,吞吐也只有240万,不及一个线程。这正是以http server为典型的服务选用[单线程模型](threading_overview.md#单线程reactor)的原因(多个线程独立运行eventloop)大部分http请求的处理时间是可预测的,对下游的访问也不会有任何阻塞代码。这个模型可以最大化cpu利用率,同时提供可接受的延时。 比如简单的echo程序,处理一个请求只需要200-300纳秒,单个线程可以达到300-500万的吞吐。但如果多个线程协作,即使在极其流畅的系统中,也要付出3-5微秒的上下文切换代价和1微秒的cache同步代价,这还没有考虑多个线程间的其他互斥逻辑,一般来说单个线程的吞吐很难超过10万,即使24核全部用满,吞吐也只有240万,不及一个线程。这正是以http server为典型的服务选用[单线程模型](threading_overview.md#单线程reactor)的原因(多个线程独立运行eventloop): 大部分http请求的处理时间是可预测的,对下游的访问也不会有任何阻塞代码。这个模型可以最大化cpu利用率,同时提供可接受的延时。
多线程付出这么大的代价是为了**隔离请求间的影响**。一个计算复杂或索性阻塞的过程不会影响到其他请求,1%的长尾最终只会影响到1%的性能。而多个独立的线程是保证不了这点的,一个请求进入了一个线程就等于“定了终生”,如果前面的请求慢了一下,那也只能跟着慢了。1%的长尾会影响远超1%的请求,最终表现不佳。换句话说,乍看上去多线程模型“慢”了,但在真实应用中反而会获得更好的综合性能。 多线程付出这么大的代价是为了**隔离请求间的影响**。一个计算复杂或索性阻塞的过程不会影响到其他请求,1%的长尾最终只会影响到1%的性能。而多个独立的线程是保证不了这点的,一个请求进入了一个线程就等于“定了终生”,如果前面的请求慢了一下,那也只能跟着慢了。1%的长尾会影响远超1%的请求,最终表现不佳。换句话说,乍看上去多线程模型“慢”了,但在真实应用中反而会获得更好的综合性能。
...@@ -64,13 +64,13 @@ thrift是由facebook最早在07年开发的序列化方法和rpc框架,包含 ...@@ -64,13 +64,13 @@ thrift是由facebook最早在07年开发的序列化方法和rpc框架,包含
## 环境 ## 环境
性能测试使用的机器配置为 性能测试使用的机器配置为:
- 单机1CPU开超线程24核,E5-2620 @ 2.00GHz;64GB内存;OS linux 2.6.32_1-15-0-0 - 单机1: CPU开超线程24核,E5-2620 @ 2.00GHz;64GB内存;OS linux 2.6.32_1-15-0-0
- 多机1(15台+8台)CPU均未开超线程12核,其中15台的CPU为E5-2420 @ 1.90GHz.,64GB内存,千兆网卡,无法开启多队列。其余8台为E5-2620 2.0GHz,千兆网卡,绑定多队列到前8个核。这些长期测试机器比较杂,跨了多个机房,测试中延时在1ms以上的就是这批机器。 - 多机1(15台+8台): CPU均未开超线程12核,其中15台的CPU为E5-2420 @ 1.90GHz.,64GB内存,千兆网卡,无法开启多队列。其余8台为E5-2620 2.0GHz,千兆网卡,绑定多队列到前8个核。这些长期测试机器比较杂,跨了多个机房,测试中延时在1ms以上的就是这批机器。
- 多机2(30台)CPU未开超线程12核,E5-2620 v3 @ 2.40GHz.;96GB内存;OS linux 2.6.32_1-17-0-0;万兆网卡,绑定多队列到前8个核。这是临时借用的新机器,配置非常好,都在广州机房,延时非常短,测试中延时在几百微秒的就是这批机器。 - 多机2(30台): CPU未开超线程12核,E5-2620 v3 @ 2.40GHz.;96GB内存;OS linux 2.6.32_1-17-0-0;万兆网卡,绑定多队列到前8个核。这是临时借用的新机器,配置非常好,都在广州机房,延时非常短,测试中延时在几百微秒的就是这批机器。
测试代码<https://svn.baidu.com/com-test/trunk/public/rpc-perf/> 测试代码: <https://svn.baidu.com/com-test/trunk/public/rpc-perf/>
下面所有的曲线图是使用brpc开发的dashboard程序绘制的,去掉路径后可以看到和所有brpc 下面所有的曲线图是使用brpc开发的dashboard程序绘制的,去掉路径后可以看到和所有brpc
server一样的[内置服务](builtin_service.md) server一样的[内置服务](builtin_service.md)
...@@ -79,17 +79,17 @@ server一样的[内置服务](builtin_service.md)。 ...@@ -79,17 +79,17 @@ server一样的[内置服务](builtin_service.md)。
如无特殊说明,所有测试中的配置只是数量差异(线程数,请求大小,client个数etc),而不是模型差异。我们确保用户看到的qps和延时是同一个场景的不同维度,而不是无法统一的两个场景。 如无特殊说明,所有测试中的配置只是数量差异(线程数,请求大小,client个数etc),而不是模型差异。我们确保用户看到的qps和延时是同一个场景的不同维度,而不是无法统一的两个场景。
所有RPC server都配置了24个工作线程,这些线程一般运行用户的处理逻辑。关于每种RPC的特殊说明 所有RPC server都配置了24个工作线程,这些线程一般运行用户的处理逻辑。关于每种RPC的特殊说明:
- UB配置了12个reactor线程,使用EPOOL模型。连接池限制数配置为线程个数(24) - UB: 配置了12个reactor线程,使用EPOOL模型。连接池限制数配置为线程个数(24)
- hulu-pbrpc: 额外配置了12个IO线程。这些线程会处理fd读取,请求解析等任务。hulu有个“共享队列“的配置项,默认不打开,作用是把fd静态散列到多个线程中,由于线程间不再争抢,hulu的qps会显著提高,但会明显地被长尾影响(原因见[测试方法](#测试方法))。考虑到大部分使用者并不会去改配置,我们也选择不打开。 - hulu-pbrpc: 额外配置了12个IO线程。这些线程会处理fd读取,请求解析等任务。hulu有个“共享队列“的配置项,默认不打开,作用是把fd静态散列到多个线程中,由于线程间不再争抢,hulu的qps会显著提高,但会明显地被长尾影响(原因见[测试方法](#测试方法))。考虑到大部分使用者并不会去改配置,我们也选择不打开。
- thrift: 额外配置了12个IO线程。这些线程会处理fd读取,请求解析等任务。thrift的client不支持多线程,每个线程得使用独立的client,连接也都是分开的。 - thrift: 额外配置了12个IO线程。这些线程会处理fd读取,请求解析等任务。thrift的client不支持多线程,每个线程得使用独立的client,连接也都是分开的。
- sofa-pbrpc按照sofa同学的要求,把io_service_pool_size配置为24,work_thread_num配置为1。大概含义是使用独立的24组线程池,每组1个worker thread。和hulu不打开“共享队列”时类似,这个配置会显著提高sofa-pbrpc的QPS,但同时使它失去了处理长尾的能力。如果你在真实产品中使用,我们不建议这个配置。(而应该用io_service_pool_size=1, work_thread_num=24) - sofa-pbrpc: 按照sofa同学的要求,把io_service_pool_size配置为24,work_thread_num配置为1。大概含义是使用独立的24组线程池,每组1个worker thread。和hulu不打开“共享队列”时类似,这个配置会显著提高sofa-pbrpc的QPS,但同时使它失去了处理长尾的能力。如果你在真实产品中使用,我们不建议这个配置。(而应该用io_service_pool_size=1, work_thread_num=24)
- brpc尽管brpc的client运行在bthread中时会获得10%~20%的QPS提升和更低的延时,但测试中的client都运行统一的pthread中。 - brpc: 尽管brpc的client运行在bthread中时会获得10%~20%的QPS提升和更低的延时,但测试中的client都运行统一的pthread中。
所有的RPC client都以多个线程同步方式发送,这种方法最接近于真实系统中的情况,在考察QPS时也兼顾了延时因素。 所有的RPC client都以多个线程同步方式发送,这种方法最接近于真实系统中的情况,在考察QPS时也兼顾了延时因素。
一种流行的方案是client不停地往连接中写入数据看server表现,这个方法的弊端在于server一下子能读出大量请求,不同RPC的比拼变成了“for循环执行用户代码”的比拼,而不是分发请求的效率。在真实系统中server很少能同时读到超过4个请求。这个方法也完全放弃了延时,client其实是让server陷入了雪崩时才会进入的状态,所有请求都因大量排队而超时了。 一种流行的方案是client不停地往连接中写入数据看server表现,这个方法的弊端在于: server一下子能读出大量请求,不同RPC的比拼变成了“for循环执行用户代码”的比拼,而不是分发请求的效率。在真实系统中server很少能同时读到超过4个请求。这个方法也完全放弃了延时,client其实是让server陷入了雪崩时才会进入的状态,所有请求都因大量排队而超时了。
## 同机单client→单server在不同请求下的QPS(越高越好) ## 同机单client→单server在不同请求下的QPS(越高越好)
...@@ -103,11 +103,11 @@ server一样的[内置服务](builtin_service.md)。 ...@@ -103,11 +103,11 @@ server一样的[内置服务](builtin_service.md)。
**分析** **分析**
* brpc:当请求包小于16KB时,单连接下的吞吐超过了多连接的ubrpc_mc和thrift_mc,随着请求包变大,内核对单个连接的写入速度成为瓶颈。而多连接下的brpc则达到了测试中最高的2.3GB/s。注意:虽然使用连接池的brpc在发送大包时吞吐更高,但也会耗费更多的CPU(UB和thrift也是这样)。下图中的单连接brpc已经可以提供800多兆的吞吐,足以打满万兆网卡,而使用的CPU可能只有多链接下的1/2(写出过程是[wait-free的](io.md#发消息)),真实系统中请优先使用单链接。 * brpc: 当请求包小于16KB时,单连接下的吞吐超过了多连接的ubrpc_mc和thrift_mc,随着请求包变大,内核对单个连接的写入速度成为瓶颈。而多连接下的brpc则达到了测试中最高的2.3GB/s。注意: 虽然使用连接池的brpc在发送大包时吞吐更高,但也会耗费更多的CPU(UB和thrift也是这样)。下图中的单连接brpc已经可以提供800多兆的吞吐,足以打满万兆网卡,而使用的CPU可能只有多链接下的1/2(写出过程是[wait-free的](io.md#发消息)),真实系统中请优先使用单链接。
* thrift: 初期明显低于brpc,随着包变大超过了单连接的brpc。 * thrift: 初期明显低于brpc,随着包变大超过了单连接的brpc。
* UB:和thrift类似的曲线,但平均要低4-5万QPS,在32K包时超过了单连接的brpc。整个过程中QPS几乎没变过。 * UB:和thrift类似的曲线,但平均要低4-5万QPS,在32K包时超过了单连接的brpc。整个过程中QPS几乎没变过。
* grpc: 初期几乎与UB平行,但低1万左右,超过8K开始下降。 * gRPC: 初期几乎与UB平行,但低1万左右,超过8K开始下降。
* hulu-pbrpc和sofa-pbrpc: 512字节前高于UB和grpc,但之后就急转直下,相继垫底。这个趋势是写不够并发的迹象。 * hulu-pbrpc和sofa-pbrpc: 512字节前高于UB和gRPC,但之后就急转直下,相继垫底。这个趋势是写不够并发的迹象。
## 同机单client→单server在不同线程数下的QPS(越高越好) ## 同机单client→单server在不同线程数下的QPS(越高越好)
...@@ -121,12 +121,12 @@ server一样的[内置服务](builtin_service.md)。 ...@@ -121,12 +121,12 @@ server一样的[内置服务](builtin_service.md)。
brpc: 随着发送线程增加,QPS在快速增加,有很好的多线程扩展性。 brpc: 随着发送线程增加,QPS在快速增加,有很好的多线程扩展性。
UB和thrift8个线程下高于brpc,但超过8个线程后被brpc迅速超过,thrift继续“平移”,UB出现了明显下降。 UB和thrift: 8个线程下高于brpc,但超过8个线程后被brpc迅速超过,thrift继续“平移”,UB出现了明显下降。
grpc,hulu-pbrpc,sofa-pbrpc: 几乎重合,256个线程时相比1个线程时只有1倍的提升,多线程扩展性不佳。 gRPC,hulu-pbrpc,sofa-pbrpc: 几乎重合,256个线程时相比1个线程时只有1倍的提升,多线程扩展性不佳。
## 同机单client→单server在固定QPS下的延时[CDF](vars.md#统计和查看分位值)(越左越好,越直越好) ## 同机单client→单server在固定QPS下的延时[CDF](vars.md#统计和查看分位值)(越左越好,越直越好)
本测试运行在[单机1](#环境)上。考虑到不同RPC的处理能力,我们选择了一个较低、在不少系统中会达到的的QPS1万。 本测试运行在[单机1](#环境)上。考虑到不同RPC的处理能力,我们选择了一个较低、在不少系统中会达到的的QPS: 1万。
本测试中有1%的长尾请求耗时5毫秒,长尾请求的延时不计入结果,因为我们考察的是普通请求是否被及时处理了。 本测试中有1%的长尾请求耗时5毫秒,长尾请求的延时不计入结果,因为我们考察的是普通请求是否被及时处理了。
...@@ -135,11 +135,11 @@ grpc,hulu-pbrpc,sofa-pbrpc: 几乎重合,256个线程时相比1个线程 ...@@ -135,11 +135,11 @@ grpc,hulu-pbrpc,sofa-pbrpc: 几乎重合,256个线程时相比1个线程
![img](../images/latency_cdf.png) ![img](../images/latency_cdf.png)
**分析** **分析**
- brpc平均延时短,几乎没有被长尾影响。 - brpc: 平均延时短,几乎没有被长尾影响。
- UB和thrift平均延时比brpc高1毫秒,受长尾影响不大。 - UB和thrift: 平均延时比brpc高1毫秒,受长尾影响不大。
- hulu-pbrpc走向和UB和thrift类似,但平均延时进一步增加了1毫秒。 - hulu-pbrpc: 走向和UB和thrift类似,但平均延时进一步增加了1毫秒。
- grpc : 初期不错,到长尾区域后表现糟糕,直接有一部分请求超时了。(反复测试都是这样,像是有bug) - gRPC : 初期不错,到长尾区域后表现糟糕,直接有一部分请求超时了。(反复测试都是这样,像是有bug)
- sofa-pbrpc30%的普通请求(上图未显示)被长尾严重干扰。 - sofa-pbrpc: 30%的普通请求(上图未显示)被长尾严重干扰。
## 跨机多client→单server的QPS(越高越好) ## 跨机多client→单server的QPS(越高越好)
...@@ -153,9 +153,9 @@ grpc,hulu-pbrpc,sofa-pbrpc: 几乎重合,256个线程时相比1个线程 ...@@ -153,9 +153,9 @@ grpc,hulu-pbrpc,sofa-pbrpc: 几乎重合,256个线程时相比1个线程
* brpc: 随着cilent增加,server的QPS在快速增加,有不错的client扩展性。 * brpc: 随着cilent增加,server的QPS在快速增加,有不错的client扩展性。
* sofa-pbrpc: 随着client增加,server的QPS也在快速增加,但幅度不如brpc,client扩展性也不错。从16个client到32个client时的提升较小。 * sofa-pbrpc: 随着client增加,server的QPS也在快速增加,但幅度不如brpc,client扩展性也不错。从16个client到32个client时的提升较小。
* hulu-pbrpc: 随着client增加,server的QPS在增加,但幅度进一步小于sofa-pbrpc。 * hulu-pbrpc: 随着client增加,server的QPS在增加,但幅度进一步小于sofa-pbrpc。
* UB增加client几乎不能增加server的QPS。 * UB: 增加client几乎不能增加server的QPS。
* thrift平均QPS低于UB,增加client几乎不能增加server的QPS。 * thrift: 平均QPS低于UB,增加client几乎不能增加server的QPS。
* grpc:垫底、增加client几乎不能增加server的QPS。 * gRPC: 垫底、增加client几乎不能增加server的QPS。
## 跨机多client→单server在固定QPS下的延时[CDF](vars.md#统计和查看分位值)(越左越好,越直越好) ## 跨机多client→单server在固定QPS下的延时[CDF](vars.md#统计和查看分位值)(越左越好,越直越好)
...@@ -168,15 +168,15 @@ grpc,hulu-pbrpc,sofa-pbrpc: 几乎重合,256个线程时相比1个线程 ...@@ -168,15 +168,15 @@ grpc,hulu-pbrpc,sofa-pbrpc: 几乎重合,256个线程时相比1个线程
![img](../images/multi_client_latency_cdf.png) ![img](../images/multi_client_latency_cdf.png)
**分析** **分析**
- brpc平均延时短,几乎没有被长尾影响。 - brpc: 平均延时短,几乎没有被长尾影响。
- UB和thrift平均延时短,受长尾影响小,平均延时高于brpc - UB和thrift: 平均延时短,受长尾影响小,平均延时高于brpc
- sofa-pbrpc14%的普通请求被长尾严重干扰。 - sofa-pbrpc: 14%的普通请求被长尾严重干扰。
- hulu-pbrpc15%的普通请求被长尾严重干扰。 - hulu-pbrpc: 15%的普通请求被长尾严重干扰。
- grpc : 已经完全失控,非常糟糕。 - gRPC: 已经完全失控,非常糟糕。
## 跨机多client→多server在固定QPS下的延时[CDF](vars.md#统计和查看分位值)(越左越好,越直越好) ## 跨机多client→多server在固定QPS下的延时[CDF](vars.md#统计和查看分位值)(越左越好,越直越好)
本测试运行在[多机2](#环境)上。20台每台运行4个client,多线程同步访问10台server。负载均衡算法为round-robin或RPC默认提供的。由于grpc访问多server较麻烦且有很大概率仍表现不佳,这个测试不包含grpc 本测试运行在[多机2](#环境)上。20台每台运行4个client,多线程同步访问10台server。负载均衡算法为round-robin或RPC默认提供的。由于gRPC访问多server较麻烦且有很大概率仍表现不佳,这个测试不包含gRPC
本测试中有1%的长尾请求耗时10毫秒,长尾请求的延时不计入结果,因为我们考察的是普通请求是否被及时处理了。 本测试中有1%的长尾请求耗时10毫秒,长尾请求的延时不计入结果,因为我们考察的是普通请求是否被及时处理了。
...@@ -185,14 +185,14 @@ grpc,hulu-pbrpc,sofa-pbrpc: 几乎重合,256个线程时相比1个线程 ...@@ -185,14 +185,14 @@ grpc,hulu-pbrpc,sofa-pbrpc: 几乎重合,256个线程时相比1个线程
![img](../images/multi_server_latency_cdf.png) ![img](../images/multi_server_latency_cdf.png)
**分析** **分析**
- brpc和UB平均延时短,几乎没有被长尾影响。 - brpc和UB: 平均延时短,几乎没有被长尾影响。
- thrift: 平均延时显著高于brpc和UB。 - thrift: 平均延时显著高于brpc和UB。
- sofa-pbrpc2.5%的普通请求被长尾严重干扰。 - sofa-pbrpc: 2.5%的普通请求被长尾严重干扰。
- hulu-pbrpc22%的普通请求被长尾严重干扰。 - hulu-pbrpc: 22%的普通请求被长尾严重干扰。
## 跨机多client→多server→多server在固定QPS下的延时[CDF](vars.md#统计和查看分位值)(越左越好,越直越好) ## 跨机多client→多server→多server在固定QPS下的延时[CDF](vars.md#统计和查看分位值)(越左越好,越直越好)
本测试运行在[多机2](#环境)上。14台每台运行4个client,多线程同步访问8台server,这些server还会同步访问另外8台server。负载均衡算法为round-robin或RPC默认提供的。由于grpc访问多server较麻烦且有很大概率仍表现不佳,这个测试不包含grpc 本测试运行在[多机2](#环境)上。14台每台运行4个client,多线程同步访问8台server,这些server还会同步访问另外8台server。负载均衡算法为round-robin或RPC默认提供的。由于gRPC访问多server较麻烦且有很大概率仍表现不佳,这个测试不包含gRPC
本测试中有1%的长尾请求耗时10毫秒,长尾请求的延时不计入结果,因为我们考察的是普通请求是否被及时处理了。 本测试中有1%的长尾请求耗时10毫秒,长尾请求的延时不计入结果,因为我们考察的是普通请求是否被及时处理了。
...@@ -201,23 +201,23 @@ grpc,hulu-pbrpc,sofa-pbrpc: 几乎重合,256个线程时相比1个线程 ...@@ -201,23 +201,23 @@ grpc,hulu-pbrpc,sofa-pbrpc: 几乎重合,256个线程时相比1个线程
![img](../images/twolevel_server_latency_cdf.png) ![img](../images/twolevel_server_latency_cdf.png)
**分析** **分析**
- brpc平均延时短,几乎没有被长尾影响。 - brpc: 平均延时短,几乎没有被长尾影响。
- UB平均延时短,长尾区域略差于brpc。 - UB: 平均延时短,长尾区域略差于brpc。
- thrift: 平均延时显著高于brpc和UB。 - thrift: 平均延时显著高于brpc和UB。
- sofa-pbrpc17%的普通请求被长尾严重干扰,其中2%的请求延时极长。 - sofa-pbrpc: 17%的普通请求被长尾严重干扰,其中2%的请求延时极长。
- hulu-pbrpc基本消失在视野中,已无法正常工作。 - hulu-pbrpc: 基本消失在视野中,已无法正常工作。
# 结论 # 结论
brpc在吞吐,平均延时,长尾处理上都表现优秀。 brpc: 在吞吐,平均延时,长尾处理上都表现优秀。
UB平均延时和长尾处理的表现都不错,吞吐的扩展性较差,提高线程数和client数几乎不能提升吞吐。 UB: 平均延时和长尾处理的表现都不错,吞吐的扩展性较差,提高线程数和client数几乎不能提升吞吐。
thrift单机的平均延时和吞吐尚可,多机的平均延时明显高于brpc和UB。吞吐的扩展性较差,提高线程数和client数几乎不能提升吞吐。 thrift: 单机的平均延时和吞吐尚可,多机的平均延时明显高于brpc和UB。吞吐的扩展性较差,提高线程数和client数几乎不能提升吞吐。
sofa-pbrpc处理小包的吞吐尚可,大包的吞吐显著低于其他RPC,延时受长尾影响很大。 sofa-pbrpc: 处理小包的吞吐尚可,大包的吞吐显著低于其他RPC,延时受长尾影响很大。
hulu-pbrpc单机表现和sofa-pbrpc类似,但多机的延时表现极差。 hulu-pbrpc: 单机表现和sofa-pbrpc类似,但多机的延时表现极差。
grpc:几乎在所有参与的测试中垫底,可能它的定位是给google cloud platform的用户提供一个多语言,对网络友好的实现,性能还不是要务。 gRPC: 几乎在所有参与的测试中垫底,可能它的定位是给google cloud platform的用户提供一个多语言,对网络友好的实现,性能还不是要务。
...@@ -121,6 +121,10 @@ BNS是百度内常用的命名服务,比如bns://rdev.matrix.all,其中"bns" ...@@ -121,6 +121,10 @@ BNS是百度内常用的命名服务,比如bns://rdev.matrix.all,其中"bns"
缺点: 受限于DNS的格式限制无法传递复杂的meta数据,也无法实现通知机制。 缺点: 受限于DNS的格式限制无法传递复杂的meta数据,也无法实现通知机制。
### https://\<url\>
和http前缀类似,只是会自动开启SSL。
### consul://\<service-name\> ### consul://\<service-name\>
通过consul获取服务名称为service-name的服务列表。consul的默认地址是localhost:8500,可通过gflags设置-consul\_agent\_addr来修改。consul的连接超时时间默认是200ms,可通过-consul\_connect\_timeout\_ms来修改。 通过consul获取服务名称为service-name的服务列表。consul的默认地址是localhost:8500,可通过gflags设置-consul\_agent\_addr来修改。consul的连接超时时间默认是200ms,可通过-consul\_connect\_timeout\_ms来修改。
...@@ -251,7 +255,7 @@ stub.some_method(controller, request, response, done); ...@@ -251,7 +255,7 @@ stub.some_method(controller, request, response, done);
```c++ ```c++
XXX_Stub(&channel).some_method(controller, request, response, done); XXX_Stub(&channel).some_method(controller, request, response, done);
``` ```
一个例外是http client。访问http服务和protobuf没什么关系,直接调用CallMethod即可,除了Controller和done均为NULL,详见[访问HTTP服务](http_client.md) 一个例外是http/h2 client。访问http服务和protobuf没什么关系,直接调用CallMethod即可,除了Controller和done均为NULL,详见[访问http/h2服务](http_client.md)
## 同步访问 ## 同步访问
...@@ -531,7 +535,7 @@ Controller.set_max_retry(0)或ChannelOptions.max_retry=0关闭重试。 ...@@ -531,7 +535,7 @@ Controller.set_max_retry(0)或ChannelOptions.max_retry=0关闭重试。
一些错误重试是没有意义的,就不会重试,比如请求有错时(EREQUEST)不会重试,因为server总不会接受,没有意义。 一些错误重试是没有意义的,就不会重试,比如请求有错时(EREQUEST)不会重试,因为server总不会接受,没有意义。
用户可以通过继承[brpc::RetryPolicy](https://github.com/brpc/brpc/blob/master/src/brpc/retry_policy.h)自定义重试条件。比如brpc默认不重试HTTP相关的错误,而你的程序中希望在碰到HTTP_STATUS_FORBIDDEN (403)时重试,可以这么做: 用户可以通过继承[brpc::RetryPolicy](https://github.com/brpc/brpc/blob/master/src/brpc/retry_policy.h)自定义重试条件。比如brpc默认不重试http/h2相关的错误,而你的程序中希望在碰到HTTP_STATUS_FORBIDDEN (403)时重试,可以这么做:
```c++ ```c++
#include <brpc/retry_policy.h> #include <brpc/retry_policy.h>
...@@ -539,7 +543,7 @@ Controller.set_max_retry(0)或ChannelOptions.max_retry=0关闭重试。 ...@@ -539,7 +543,7 @@ Controller.set_max_retry(0)或ChannelOptions.max_retry=0关闭重试。
class MyRetryPolicy : public brpc::RetryPolicy { class MyRetryPolicy : public brpc::RetryPolicy {
public: public:
bool DoRetry(const brpc::Controller* cntl) const { bool DoRetry(const brpc::Controller* cntl) const {
if (cntl->ErrorCode() == brpc::EHTTP && // HTTP错误 if (cntl->ErrorCode() == brpc::EHTTP && // http/h2错误
cntl->http_response().status_code() == brpc::HTTP_STATUS_FORBIDDEN) { cntl->http_response().status_code() == brpc::HTTP_STATUS_FORBIDDEN) {
return true; return true;
} }
...@@ -573,19 +577,25 @@ Channel的默认协议是baidu_std,可通过设置ChannelOptions.protocol换 ...@@ -573,19 +577,25 @@ Channel的默认协议是baidu_std,可通过设置ChannelOptions.protocol换
目前支持的有: 目前支持的有:
- PROTOCOL_BAIDU_STD 或 “baidu_std",即[百度标准协议](baidu_std.md),默认为单连接。 - PROTOCOL_BAIDU_STD 或 “baidu_std",即[百度标准协议](baidu_std.md),默认为单连接。
- PROTOCOL_HTTP 或 ”http", http/1.0或http/1.1协议,默认为连接池(Keep-Alive)。
- 访问普通http服务的方法见[访问http/h2服务](http_client.md)
- 通过http:json或http:proto访问pb服务的方法见[基于http/h2的协议](http_derivatives.md)
- PROTOCOL_H2 或 ”h2", http/2.0协议,默认是单连接。
- 访问普通h2服务的方法见[访问http/h2服务](http_client.md)
- 通过h2:json或h2:proto访问pb服务的方法见[基于http/h2的协议](http_derivatives.md)
- "h2:grpc", [gRPC](https://grpc.io)的协议,也是h2的衍生协议,默认为单连接,具体见[基于http/h2的协议](http_derivatives.md)
- PROTOCOL_THRIFT 或 "thrift",[apache thrift](https://thrift.apache.org)的协议,默认为连接池, 具体方法见[访问thrift](thrift.md)
- PROTOCOL_MEMCACHE 或 "memcache",memcached的二进制协议,默认为单连接。具体方法见[访问memcached](memcache_client.md)
- PROTOCOL_REDIS 或 "redis",redis 1.2后的协议(也是hiredis支持的协议),默认为单连接。具体方法见[访问Redis](redis_client.md)
- PROTOCOL_HULU_PBRPC 或 "hulu_pbrpc",hulu的协议,默认为单连接。 - PROTOCOL_HULU_PBRPC 或 "hulu_pbrpc",hulu的协议,默认为单连接。
- PROTOCOL_NOVA_PBRPC 或 ”nova_pbrpc“,网盟的协议,默认为连接池。 - PROTOCOL_NOVA_PBRPC 或 ”nova_pbrpc“,网盟的协议,默认为连接池。
- PROTOCOL_HTTP 或 ”http", http 1.0或1.1协议,默认为连接池(Keep-Alive)。具体方法见[访问HTTP服务](http_client.md)
- PROTOCOL_SOFA_PBRPC 或 "sofa_pbrpc",sofa-pbrpc的协议,默认为单连接。 - PROTOCOL_SOFA_PBRPC 或 "sofa_pbrpc",sofa-pbrpc的协议,默认为单连接。
- PROTOCOL_PUBLIC_PBRPC 或 "public_pbrpc",public_pbrpc的协议,默认为连接池。 - PROTOCOL_PUBLIC_PBRPC 或 "public_pbrpc",public_pbrpc的协议,默认为连接池。
- PROTOCOL_UBRPC_COMPACK 或 "ubrpc_compack",public/ubrpc的协议,使用compack打包,默认为连接池。具体方法见[ubrpc (by protobuf)](ub_client.md)。相关的还有PROTOCOL_UBRPC_MCPACK2或ubrpc_mcpack2,使用mcpack2打包。 - PROTOCOL_UBRPC_COMPACK 或 "ubrpc_compack",public/ubrpc的协议,使用compack打包,默认为连接池。具体方法见[ubrpc (by protobuf)](ub_client.md)。相关的还有PROTOCOL_UBRPC_MCPACK2或ubrpc_mcpack2,使用mcpack2打包。
- PROTOCOL_NSHEAD_CLIENT 或 "nshead_client",这是发送baidu-rpc-ub中所有UBXXXRequest需要的协议,默认为连接池。具体方法见[访问UB](ub_client.md) - PROTOCOL_NSHEAD_CLIENT 或 "nshead_client",这是发送baidu-rpc-ub中所有UBXXXRequest需要的协议,默认为连接池。具体方法见[访问UB](ub_client.md)
- PROTOCOL_NSHEAD 或 "nshead",这是发送NsheadMessage需要的协议,默认为连接池。具体方法见[nshead+blob](ub_client.md#nshead-blob) - PROTOCOL_NSHEAD 或 "nshead",这是发送NsheadMessage需要的协议,默认为连接池。具体方法见[nshead+blob](ub_client.md#nshead-blob)
- PROTOCOL_MEMCACHE 或 "memcache",memcached的二进制协议,默认为单连接。具体方法见[访问memcached](memcache_client.md)
- PROTOCOL_REDIS 或 "redis",redis 1.2后的协议(也是hiredis支持的协议),默认为单连接。具体方法见[访问Redis](redis_client.md)
- PROTOCOL_NSHEAD_MCPACK 或 "nshead_mcpack", 顾名思义,格式为nshead + mcpack,使用mcpack2pb适配,默认为连接池。 - PROTOCOL_NSHEAD_MCPACK 或 "nshead_mcpack", 顾名思义,格式为nshead + mcpack,使用mcpack2pb适配,默认为连接池。
- PROTOCOL_ESP 或 "esp",访问使用esp协议的服务,默认为连接池。 - PROTOCOL_ESP 或 "esp",访问使用esp协议的服务,默认为连接池。
- PROTOCOL_THRIFT 或 "thrift",访问使用thrift协议的服务,默认为连接池, 具体方法见[访问thrift](thrift.md)
## 连接方式 ## 连接方式
...@@ -661,7 +671,7 @@ brpc支持[Streaming RPC](streaming_rpc.md),这是一种应用层的连接, ...@@ -661,7 +671,7 @@ brpc支持[Streaming RPC](streaming_rpc.md),这是一种应用层的连接,
baidu_std和hulu_pbrpc协议支持附件,这段数据由用户自定义,不经过protobuf的序列化。站在client的角度,设置在Controller::request_attachment()的附件会被server端收到,response_attachment()则包含了server端送回的附件。附件不受压缩选项影响。 baidu_std和hulu_pbrpc协议支持附件,这段数据由用户自定义,不经过protobuf的序列化。站在client的角度,设置在Controller::request_attachment()的附件会被server端收到,response_attachment()则包含了server端送回的附件。附件不受压缩选项影响。
在http协议中,附件对应[message body](http://www.w3.org/Protocols/rfc2616/rfc2616-sec4.html),比如要POST的数据就设置在request_attachment()中。 在http/h2协议中,附件对应[message body](http://www.w3.org/Protocols/rfc2616/rfc2616-sec4.html),比如要POST的数据就设置在request_attachment()中。
## 开启SSL ## 开启SSL
...@@ -718,7 +728,7 @@ set_request_compress_type()设置request的压缩方式,默认不压缩。 ...@@ -718,7 +728,7 @@ set_request_compress_type()设置request的压缩方式,默认不压缩。
注意:附件不会被压缩。 注意:附件不会被压缩。
HTTP body的压缩方法见[client压缩request body](http_client#压缩request-body) http/h2 body的压缩方法见[client压缩request body](http_client#压缩request-body)
支持的压缩方法有: 支持的压缩方法有:
......
...@@ -4,22 +4,28 @@ ...@@ -4,22 +4,28 @@
[example/http_c++](https://github.com/brpc/brpc/blob/master/example/http_c++/http_client.cpp) [example/http_c++](https://github.com/brpc/brpc/blob/master/example/http_c++/http_client.cpp)
# 关于h2
brpc把HTTP/2协议统称为"h2",不论是否加密。然而未开启ssl的HTTP/2连接在/connections中会按官方名称h2c显示,而开启ssl的会显示为h2。
brpc中http和h2的编程接口基本没有区别。除非特殊说明,所有提到的http特性都同时对h2有效。
# 创建Channel # 创建Channel
brpc::Channel可访问HTTP服务,ChannelOptions.protocol须指定为PROTOCOL_HTTP brpc::Channel可访问http/h2服务,ChannelOptions.protocol须指定为PROTOCOL_HTTP或PROTOCOL_H2
设定为HTTP协议后,`Channel::Init`的第一个参数可为任意合法的URL。注意:允许任意URL是为了省去用户取出host和port的麻烦,`Channel::Init`只用其中的host及port,其他部分都会丢弃。 设定协议后,`Channel::Init`的第一个参数可为任意合法的URL。注意:允许任意URL是为了省去用户取出host和port的麻烦,`Channel::Init`只用其中的host及port,其他部分都会丢弃。
```c++ ```c++
brpc::ChannelOptions options; brpc::ChannelOptions options;
options.protocol = brpc::PROTOCOL_HTTP; options.protocol = brpc::PROTOCOL_HTTP; // or brpc::PROTOCOL_H2
if (channel.Init("www.baidu.com" /*any url*/, &options) != 0) { if (channel.Init("www.baidu.com" /*any url*/, &options) != 0) {
LOG(ERROR) << "Fail to initialize channel"; LOG(ERROR) << "Fail to initialize channel";
return -1; return -1;
} }
``` ```
http channel也支持bns地址或其他NamingService。 http/h2 channel也支持bns地址或其他NamingService。
# GET # GET
...@@ -29,7 +35,7 @@ cntl.http_request().uri() = "www.baidu.com/index.html"; // 设置为待访问 ...@@ -29,7 +35,7 @@ cntl.http_request().uri() = "www.baidu.com/index.html"; // 设置为待访问
channel.CallMethod(NULL, &cntl, NULL, NULL, NULL/*done*/); channel.CallMethod(NULL, &cntl, NULL, NULL, NULL/*done*/);
``` ```
HTTP和protobuf关系不大,所以除了Controller和done,CallMethod的其他参数均为NULL。如果要异步操作,最后一个参数传入done。 HTTP/h2和protobuf关系不大,所以除了Controller和done,CallMethod的其他参数均为NULL。如果要异步操作,最后一个参数传入done。
`cntl.response_attachment()`是回复的body,类型也是butil::IOBuf。IOBuf可通过to_string()转化为std::string,但是需要分配内存并拷贝所有内容,如果关注性能,处理过程应直接支持IOBuf,而不要求连续内存。 `cntl.response_attachment()`是回复的body,类型也是butil::IOBuf。IOBuf可通过to_string()转化为std::string,但是需要分配内存并拷贝所有内容,如果关注性能,处理过程应直接支持IOBuf,而不要求连续内存。
...@@ -56,6 +62,18 @@ os << "A lot of printing" << printable_objects << ...; ...@@ -56,6 +62,18 @@ os << "A lot of printing" << printable_objects << ...;
os.move_to(cntl.request_attachment()); os.move_to(cntl.request_attachment());
channel.CallMethod(NULL, &cntl, NULL, NULL, NULL/*done*/); channel.CallMethod(NULL, &cntl, NULL, NULL, NULL/*done*/);
``` ```
# 控制HTTP版本
brpc的http行为默认是http 1.1。
http 1.0相比1.1缺少长连接功能,brpc client与一些古老的http server通信时可能需要按如下方法设置为1.0。
```c++
cntl.http_request().set_version(1, 0);
```
设置http版本对h2无效,但是client收到的h2 response和server收到的h2 request中的version会被设置为(2, 0)。
brpc server会自动识别HTTP版本,并相应回复,无需用户设置。
# URL # URL
...@@ -87,17 +105,19 @@ URL的一般形式如下图: ...@@ -87,17 +105,19 @@ URL的一般形式如下图:
确实,在简单使用场景下,这两者有所重复,但在复杂场景中,两者差别很大,比如: 确实,在简单使用场景下,这两者有所重复,但在复杂场景中,两者差别很大,比如:
- 访问命名服务(如BNS)下的多个http server。此时Channel.Init传入的是对该命名服务有意义的名称(如BNS中的节点名称),对uri()的赋值则是包含Host的完整URL(比如"www.foo.com/index.html?name=value")。 - 访问命名服务(如BNS)下的多个http/h2 server。此时Channel.Init传入的是对该命名服务有意义的名称(如BNS中的节点名称),对uri()的赋值则是包含Host的完整URL(比如"www.foo.com/index.html?name=value")。
- 通过http proxy访问目标server。此时Channel.Init传入的是proxy server的地址,但uri()填入的是目标server的URL。 - 通过http/h2 proxy访问目标server。此时Channel.Init传入的是proxy server的地址,但uri()填入的是目标server的URL。
## Host字段 ## Host字段
若用户自己填写了host字段(http header),框架不会修改。 若用户自己填写了"host"字段(大小写不敏感),框架不会修改。
若用户没有填且URL中包含host,比如http://www.foo.com/path,则http request中会包含"Host: www.foo.com"。 若用户没有填且URL中包含host,比如http://www.foo.com/path,则http request中会包含"Host: www.foo.com"。
若用户没有填且URL不包含host,比如"/index.html?name=value",则框架会以目标server的ip和port为Host,地址为10.46.188.39:8989的http server将会看到"Host: 10.46.188.39:8989"。 若用户没有填且URL不包含host,比如"/index.html?name=value",则框架会以目标server的ip和port为Host,地址为10.46.188.39:8989的http server将会看到"Host: 10.46.188.39:8989"。
对应的字段在h2中叫":authority"。
# 常见设置 # 常见设置
以http request为例 (对response的操作自行替换), 常见操作方式如下所示: 以http request为例 (对response的操作自行替换), 常见操作方式如下所示:
...@@ -118,7 +138,7 @@ const std::string* value = cntl->http_request().uri().GetQuery("Foo"); // 不存 ...@@ -118,7 +138,7 @@ const std::string* value = cntl->http_request().uri().GetQuery("Foo"); // 不存
```c++ ```c++
cntl->http_request().uri().SetQuery("Foo", "value"); cntl->http_request().uri().SetQuery("Foo", "value");
``` ```
设置HTTP方法 设置HTTP Method
```c++ ```c++
cntl->http_request().set_method(brpc::HTTP_METHOD_POST); cntl->http_request().set_method(brpc::HTTP_METHOD_POST);
``` ```
...@@ -150,11 +170,11 @@ Notes on http header: ...@@ -150,11 +170,11 @@ Notes on http header:
# 查看HTTP消息 # 查看HTTP消息
打开[-http_verbose](http://brpc.baidu.com:8765/flags/http_verbose)即可看到所有的http request和response,注意这应该只用于线下调试,而不是线上程序。 打开[-http_verbose](http://brpc.baidu.com:8765/flags/http_verbose)即可看到所有的http/h2 request和response,注意这应该只用于线下调试,而不是线上程序。
# HTTP错误 # HTTP错误
当Server返回的http status code不是2xx时,该次http访问被视为失败,client端会把`cntl->ErrorCode()`设置为EHTTP,用户可通过`cntl->http_response().status_code()`获得具体的http错误。同时server端可以把代表错误的html或json置入`cntl->response_attachment()`作为http body传递回来。 当Server返回的http status code不是2xx时,该次http/h2访问被视为失败,client端会把`cntl->ErrorCode()`设置为EHTTP,用户可通过`cntl->http_response().status_code()`获得具体的http错误。同时server端可以把代表错误的html或json置入`cntl->response_attachment()`作为http body传递回来。
# 压缩request body # 压缩request body
......
[English version](../en/http_derivatives.md)
http协议的基本用法见[http_client](http_client.md)[http_service](http_service.md)
下文中的小节名均为可填入ChannelOptions.protocol中的协议名。冒号后的内容是协议参数,用于动态选择衍生行为,但基本协议仍然是http/1.x或http/2,故这些协议在服务端只会被显示为http或h2/h2c。
# http:json, h2:json
如果pb request不为空,则会被转为json并作为http/h2请求的body,此时Controller.request_attachment()必须为空否则报错。
如果pb response不为空,则会以json解析http/h2回复的body,并转填至pb response的对应字段。
http/1.x默认是这个行为,所以"http"和"http:json"等价。
# http:proto, h2:proto
如果pb request不为空,则会把pb序列化结果作为http/h2请求的body,此时Controller.request_attachment()必须为空否则报错。
如果pb response不为空,则会以pb序列化格式解析http/h2回复的body,并填至pb response。
http/2默认是这个行为,所以"h2"和"h2:proto"等价。
# h2:grpc
[gRPC](https://github.com/grpc)的默认协议,具体格式可阅读[gRPC over HTTP2](https://github.com/grpc/grpc/blob/master/doc/PROTOCOL-HTTP2.md)
使用brpc的客户端把ChannelOptions.protocol设置为"h2:grpc"一般就能连通gRPC。
使用brpc的服务端一般无需修改代码即可自动被gRPC客户端访问。
gRPC默认序列化是pb二进制格式,所以"h2:grpc"和"h2:grpc+proto"等价。
TODO: gRPC其他配置
# h2:grpc+json
这个协议相比h2:grpc就是用json序列化结果代替pb序列化结果。gRPC未必直接支持这个格式,如grpc-go可参考[这里](https://github.com/johanbrandhorst/grpc-json-example/blob/master/codec/json.go)注册对应的codec后才会支持。
[English version](../en/http_service.md) [English version](../en/http_service.md)
这里指我们通常说的HTTP服务,而不是可通过HTTP访问的pb服务。 这里指我们通常说的http/h2服务,而不是可通过http/h2访问的pb服务。
虽然用不到pb消息,但brpc中的HTTP服务接口也得定义在.proto文件中,只是request和response都是空的结构体。这确保了所有的服务声明集中在proto文件中,而不是散列在proto文件、程序、配置等多个地方。示例代码见[http_server.cpp](https://github.com/brpc/brpc/blob/master/example/http_c++/http_server.cpp) 虽然用不到pb消息,但brpc中的http/h2服务接口也得定义在proto文件中,只是request和response都是空的结构体。这确保了所有的服务声明集中在proto文件中,而不是散列在proto文件、程序、配置等多个地方。
#示例
[http_server.cpp](https://github.com/brpc/brpc/blob/master/example/http_c++/http_server.cpp)
# 关于h2
brpc把HTTP/2协议统称为"h2",不论是否加密。然而未开启ssl的HTTP/2连接在/connections中会按官方名称h2c显示,而开启ssl的会显示为h2。
brpc中http和h2的编程接口基本没有区别。除非特殊说明,所有提到的http特性都同时对h2有效。
# URL类型 # URL类型
## 前缀为/ServiceName/MethodName ## 前缀为/ServiceName/MethodName
定义一个service名为ServiceName(不包含package名), method名为MethodName的pb服务,且让request和reponse定义为空,则该服务默认在/ServiceName/MethodName上提供HTTP服务。 定义一个service名为ServiceName(不包含package名), method名为MethodName的pb服务,且让request和reponse定义为空,则该服务默认在/ServiceName/MethodName上提供http/h2服务。
request和response可为空是因为http数据在Controller中: request和response可为空是因为数据都在Controller中:
* http request的header在Controller.http_request()中,body在Controller.request_attachment()中。 * http/h2 request的header在Controller.http_request()中,body在Controller.request_attachment()中。
* http response的header在Controller.http_response()中,body在Controller.response_attachment()中。 * http/h2 response的header在Controller.http_response()中,body在Controller.response_attachment()中。
实现步骤如下: 实现步骤如下:
...@@ -71,7 +80,7 @@ public: ...@@ -71,7 +80,7 @@ public:
## 前缀为/ServiceName ## 前缀为/ServiceName
资源类的HTTP服务可能需要这样的URL,ServiceName后均为动态内容。比如/FileService/foobar.txt代表./foobar.txt,/FileService/app/data/boot.cfg代表./app/data/boot.cfg。 资源类的http/h2服务可能需要这样的URL,ServiceName后均为动态内容。比如/FileService/foobar.txt代表./foobar.txt,/FileService/app/data/boot.cfg代表./app/data/boot.cfg。
实现方法: 实现方法:
...@@ -120,15 +129,15 @@ public: ...@@ -120,15 +129,15 @@ public:
brpc支持为service中的每个方法指定一个URL。API如下: brpc支持为service中的每个方法指定一个URL。API如下:
```c++ ```c++
// 如果restful_mappings不为空, service中的方法可通过指定的URL被HTTP协议访问,而不是/ServiceName/MethodName. // 如果restful_mappings不为空, service中的方法可通过指定的URL被http/h2协议访问,而不是/ServiceName/MethodName.
// 映射格式:"PATH1 => NAME1, PATH2 => NAME2 ..." // 映射格式:"PATH1 => NAME1, PATH2 => NAME2 ..."
// PATHs是有效的HTTP路径, NAMEs是service中的方法名. // PATHs是有效的路径, NAMEs是service中的方法名.
int AddService(google::protobuf::Service* service, int AddService(google::protobuf::Service* service,
ServiceOwnership ownership, ServiceOwnership ownership,
butil::StringPiece restful_mappings); butil::StringPiece restful_mappings);
``` ```
下面的QueueService包含多个http方法。如果我们像之前那样把它插入server,那么只能通过`/QueueService/start, /QueueService/stop`等url来访问。 下面的QueueService包含多个方法。如果我们像之前那样把它插入server,那么只能通过`/QueueService/start, /QueueService/stop`等url来访问。
```protobuf ```protobuf
service QueueService { service QueueService {
...@@ -167,7 +176,7 @@ if (server.AddService(&queue_svc, ...@@ -167,7 +176,7 @@ if (server.AddService(&queue_svc,
关于映射规则: 关于映射规则:
- 多个路径可映射至同一个方法。 - 多个路径可映射至同一个方法。
- service不要求是纯HTTP,pb service也支持。 - service不要求是纯http/h2,pb service也支持。
- 没有出现在映射中的方法仍旧通过/ServiceName/MethodName访问。出现在映射中的方法不再能通过/ServiceName/MethodName访问。 - 没有出现在映射中的方法仍旧通过/ServiceName/MethodName访问。出现在映射中的方法不再能通过/ServiceName/MethodName访问。
- ==> ===> ...都是可以的。开头结尾的空格,额外的斜杠(/),最后多余的逗号,都不要紧。 - ==> ===> ...都是可以的。开头结尾的空格,额外的斜杠(/),最后多余的逗号,都不要紧。
- PATH和PATH/*两者可以共存。 - PATH和PATH/*两者可以共存。
...@@ -273,7 +282,7 @@ cntl->http_request().uri().SetQuery("time", "2015/1/2"); ...@@ -273,7 +282,7 @@ cntl->http_request().uri().SetQuery("time", "2015/1/2");
# 调试 # 调试
打开[-http_verbose](http://brpc.baidu.com:8765/flags/http_verbose)即可看到所有的http request和response,注意这应该只用于线下调试,而不是线上程序。 打开[-http_verbose](http://brpc.baidu.com:8765/flags/http_verbose)即可看到所有的http/h2 request和response,注意这应该只用于线下调试,而不是线上程序。
# 压缩response body # 压缩response body
......
...@@ -42,13 +42,13 @@ RPC不是万能的抽象,否则我们也不需要TCP/IP这一层了。但是 ...@@ -42,13 +42,13 @@ RPC不是万能的抽象,否则我们也不需要TCP/IP这一层了。但是
你可以使用它: 你可以使用它:
* 搭建能在**一个端口**支持多协议的服务, 或访问各种服务 * 搭建能在**一个端口**支持多协议的服务, 或访问各种服务
* restful http/https, h2/h2c (与[grpc](https://github.com/grpc/grpc)兼容, 即将开源). 使用brpc的http实现比[libcurl](https://curl.haxx.se/libcurl/)方便多了。 * restful http/https, [h2](https://http2.github.io/http2-spec)/[gRPC](https://grpc.io)。使用brpc的http实现比[libcurl](https://curl.haxx.se/libcurl/)方便多了。从其他语言通过HTTP/h2+json访问基于protobuf的协议.
* [redis](redis_client.md)[memcached](memcache_client.md), 线程安全,比官方client更方便。 * [redis](redis_client.md)[memcached](memcache_client.md), 线程安全,比官方client更方便。
* [rtmp](https://github.com/brpc/brpc/blob/master/src/brpc/rtmp.h)/[flv](https://en.wikipedia.org/wiki/Flash_Video)/[hls](https://en.wikipedia.org/wiki/HTTP_Live_Streaming), 可用于搭建[流媒体服务](https://github.com/brpc/media-server). * [rtmp](https://github.com/brpc/brpc/blob/master/src/brpc/rtmp.h)/[flv](https://en.wikipedia.org/wiki/Flash_Video)/[hls](https://en.wikipedia.org/wiki/HTTP_Live_Streaming), 可用于搭建[流媒体服务](https://github.com/brpc/media-server).
* hadoop_rpc(可能开源) * hadoop_rpc(可能开源)
* 支持[rdma](https://en.wikipedia.org/wiki/Remote_direct_memory_access)(即将开源) * 支持[rdma](https://en.wikipedia.org/wiki/Remote_direct_memory_access)(即将开源)
* 支持[thrift](thrift.md) , 线程安全,比官方client更方便
* 各种百度内使用的协议: [baidu_std](baidu_std.md), [streaming_rpc](streaming_rpc.md), hulu_pbrpc, [sofa_pbrpc](https://github.com/baidu/sofa-pbrpc), nova_pbrpc, public_pbrpc, ubrpc和使用nshead的各种协议. * 各种百度内使用的协议: [baidu_std](baidu_std.md), [streaming_rpc](streaming_rpc.md), hulu_pbrpc, [sofa_pbrpc](https://github.com/baidu/sofa-pbrpc), nova_pbrpc, public_pbrpc, ubrpc和使用nshead的各种协议.
* 从其他语言通过HTTP+json访问基于protobuf的协议.
* 基于工业级的[RAFT算法](https://raft.github.io)实现搭建[高可用](https://en.wikipedia.org/wiki/High_availability)分布式系统,已在[braft](https://github.com/brpc/braft)开源。 * 基于工业级的[RAFT算法](https://raft.github.io)实现搭建[高可用](https://en.wikipedia.org/wiki/High_availability)分布式系统,已在[braft](https://github.com/brpc/braft)开源。
* Server能[同步](server.md)[异步](server.md#异步service)处理请求。 * Server能[同步](server.md)[异步](server.md#异步service)处理请求。
* Client支持[同步](client.md#同步访问)[异步](client.md#异步访问)[半同步](client.md#半同步),或使用[组合channels](combo_channel.md)简化复杂的分库或并发访问。 * Client支持[同步](client.md#同步访问)[异步](client.md#异步访问)[半同步](client.md#半同步),或使用[组合channels](combo_channel.md)简化复杂的分库或并发访问。
......
...@@ -249,9 +249,11 @@ server.RunUntilAskedToQuit(); ...@@ -249,9 +249,11 @@ server.RunUntilAskedToQuit();
Join()完成后可以修改其中的Service,并重新Start。 Join()完成后可以修改其中的Service,并重新Start。
# 被HTTP client访问 # 被http/h2访问
使用Protobuf的服务通常可以通过HTTP+json访问,存于http body的json串可与对应protobuf消息相互转化。以[echo server](https://github.com/brpc/brpc/blob/master/example/echo_c%2B%2B/server.cpp)为例,你可以用[curl](https://curl.haxx.se/)访问这个服务。 使用Protobuf的服务通常可以通过http/h2+json访问,存于body的json串可与对应protobuf消息相互自动转化。
[echo server](https://github.com/brpc/brpc/blob/master/example/echo_c%2B%2B/server.cpp)为例,你可以用[curl](https://curl.haxx.se/)访问这个服务。
```shell ```shell
# -H 'Content-Type: application/json' is optional # -H 'Content-Type: application/json' is optional
...@@ -259,7 +261,7 @@ $ curl -d '{"message":"hello"}' http://brpc.baidu.com:8765/EchoService/Echo ...@@ -259,7 +261,7 @@ $ curl -d '{"message":"hello"}' http://brpc.baidu.com:8765/EchoService/Echo
{"message":"hello"} {"message":"hello"}
``` ```
注意:也可以指定`Content-Type: application/proto`用http+protobuf二进制串访问服务,序列化性能更好。 注意:也可以指定`Content-Type: application/proto`用http/h2+protobuf二进制串访问服务,序列化性能更好。
## json<=>pb ## json<=>pb
...@@ -269,7 +271,7 @@ json字段通过匹配的名字和结构与pb字段一一对应。json中一定 ...@@ -269,7 +271,7 @@ json字段通过匹配的名字和结构与pb字段一一对应。json中一定
## 兼容早期版本client ## 兼容早期版本client
早期的brpc允许一个pb service被http协议访问时不设置pb请求,即使里面有required字段。一般来说这种service会自行解析http请求和设置http回复,并不会访问pb请求。但这也是非常危险的行为,毕竟这是pb service,但pb请求却是未定义的。 早期的brpc允许一个pb service被http协议访问时不填充pb请求,即使里面有required字段。一般来说这种service会自行解析http请求和设置http回复,并不会访问pb请求。但这也是非常危险的行为,毕竟这是pb service,但pb请求却是未定义的。
这种服务在升级到新版本rpc时会遇到障碍,因为brpc已不允许这种行为。为了帮助这种服务升级,brpc允许经过一些设置后不把http body自动转化为pb request(从而可自行处理),方法如下: 这种服务在升级到新版本rpc时会遇到障碍,因为brpc已不允许这种行为。为了帮助这种服务升级,brpc允许经过一些设置后不把http body自动转化为pb request(从而可自行处理),方法如下:
...@@ -277,11 +279,11 @@ json字段通过匹配的名字和结构与pb字段一一对应。json中一定 ...@@ -277,11 +279,11 @@ json字段通过匹配的名字和结构与pb字段一一对应。json中一定
brpc::ServiceOptions svc_opt; brpc::ServiceOptions svc_opt;
svc_opt.ownership = ...; svc_opt.ownership = ...;
svc_opt.restful_mappings = ...; svc_opt.restful_mappings = ...;
svc_opt.allow_http_body_to_pb = false; //关闭http body至pb request的自动转化 svc_opt.allow_http_body_to_pb = false; //关闭http/h2 body至pb request的自动转化
server.AddService(service, svc_opt); server.AddService(service, svc_opt);
``` ```
如此设置后service收到http请求后不会尝试把body转化为pb请求,所以pb请求总是未定义状态,用户得在`cntl->request_protocol() == brpc::PROTOCOL_HTTP`认定请求是http时自行解析http body。 如此设置后service收到http/h2请求后不会尝试把body转化为pb请求,所以pb请求总是未定义状态,用户得在`cntl->request_protocol() == brpc::PROTOCOL_HTTP || cntl->request_protocol() == brpc::PROTOCOL_H2`成立时自行解析body。
相应地,当cntl->response_attachment()不为空且pb回复不为空时,框架不再报错,而是直接把cntl->response_attachment()作为回复的body。这个功能和设置allow_http_body_to_pb与否无关。如果放开自由度导致过多的用户犯错,可能会有进一步的调整。 相应地,当cntl->response_attachment()不为空且pb回复不为空时,框架不再报错,而是直接把cntl->response_attachment()作为回复的body。这个功能和设置allow_http_body_to_pb与否无关。如果放开自由度导致过多的用户犯错,可能会有进一步的调整。
...@@ -293,7 +295,9 @@ server端会自动尝试其支持的协议,无需用户指定。`cntl->protoco ...@@ -293,7 +295,9 @@ server端会自动尝试其支持的协议,无需用户指定。`cntl->protoco
- [流式RPC协议](streaming_rpc.md),显示为"streaming_rpc", 默认启用。 - [流式RPC协议](streaming_rpc.md),显示为"streaming_rpc", 默认启用。
- http 1.0/1.1,显示为”http“,默认启用。 - http/1.0和http/1.1协议,显示为”http“,默认启用。
- http/2和gRPC协议,显示为"h2c"(未加密)或"h2"(加密),默认启用。
- RTMP协议,显示为"rtmp", 默认启用。 - RTMP协议,显示为"rtmp", 默认启用。
......
...@@ -123,6 +123,10 @@ Pros: Versatility of DNS, useable both in private or public network. ...@@ -123,6 +123,10 @@ Pros: Versatility of DNS, useable both in private or public network.
Cons: limited by transmission formats of DNS, unable to implement notification mechanisms. Cons: limited by transmission formats of DNS, unable to implement notification mechanisms.
### https://\<url\>
Similar with "http" prefix besides that the connections will be encrypted with SSL.
### consul://\<service-name\> ### consul://\<service-name\>
Get a list of servers with the specified service-name through consul. The default address of consul is localhost:8500, which can be modified by setting -consul\_agent\_addr in gflags. The connection timeout of consul is 200ms by default, which can be modified by -consul\_connect\_timeout\_ms. Get a list of servers with the specified service-name through consul. The default address of consul is localhost:8500, which can be modified by setting -consul\_agent\_addr in gflags. The connection timeout of consul is 200ms by default, which can be modified by -consul\_connect\_timeout\_ms.
...@@ -253,7 +257,7 @@ Or even: ...@@ -253,7 +257,7 @@ Or even:
```c++ ```c++
XXX_Stub(&channel).some_method(controller, request, response, done); XXX_Stub(&channel).some_method(controller, request, response, done);
``` ```
A exception is http client, which is not related to protobuf much. Call CallMethod directly to make a http call, setting all parameters to NULL except for `Controller` and `done`, check [Access HTTP](http_client.md) for details. A exception is http/h2 client, which is not related to protobuf much. Call CallMethod directly to make a http call, setting all parameters to NULL except for `Controller` and `done`, check [Access http/h2](http_client.md) for details.
## Synchronous call ## Synchronous call
...@@ -540,7 +544,7 @@ Controller.set_max_retry(0) or ChannelOptions.max_retry = 0 disables retries. ...@@ -540,7 +544,7 @@ Controller.set_max_retry(0) or ChannelOptions.max_retry = 0 disables retries.
If the RPC fails due to request(EREQUEST), no retry will be done because server is very likely to reject the request again, retrying makes no sense here. If the RPC fails due to request(EREQUEST), no retry will be done because server is very likely to reject the request again, retrying makes no sense here.
Users can inherit [brpc::RetryPolicy](https://github.com/brpc/brpc/blob/master/src/brpc/retry_policy.h) to customize conditions of retrying. For example brpc does not retry for HTTP related errors by default. If you want to retry for HTTP_STATUS_FORBIDDEN(403) in your app, you can do as follows: Users can inherit [brpc::RetryPolicy](https://github.com/brpc/brpc/blob/master/src/brpc/retry_policy.h) to customize conditions of retrying. For example brpc does not retry for http/h2 related errors by default. If you want to retry for HTTP_STATUS_FORBIDDEN(403) in your app, you can do as follows:
```c++ ```c++
#include <brpc/retry_policy.h> #include <brpc/retry_policy.h>
...@@ -548,7 +552,7 @@ Users can inherit [brpc::RetryPolicy](https://github.com/brpc/brpc/blob/master/s ...@@ -548,7 +552,7 @@ Users can inherit [brpc::RetryPolicy](https://github.com/brpc/brpc/blob/master/s
class MyRetryPolicy : public brpc::RetryPolicy { class MyRetryPolicy : public brpc::RetryPolicy {
public: public:
bool DoRetry(const brpc::Controller* cntl) const { bool DoRetry(const brpc::Controller* cntl) const {
if (cntl->ErrorCode() == brpc::EHTTP && // HTTP error if (cntl->ErrorCode() == brpc::EHTTP && // http/h2 error
cntl->http_response().status_code() == brpc::HTTP_STATUS_FORBIDDEN) { cntl->http_response().status_code() == brpc::HTTP_STATUS_FORBIDDEN) {
return true; return true;
} }
...@@ -582,16 +586,23 @@ The default protocol used by Channel is baidu_std, which is changeable by settin ...@@ -582,16 +586,23 @@ The default protocol used by Channel is baidu_std, which is changeable by settin
Supported protocols: Supported protocols:
- PROTOCOL_BAIDU_STD or "baidu_std", which is [the standard binary protocol inside Baidu](baidu_std.md), using single connection by default. - PROTOCOL_BAIDU_STD or "baidu_std", which is [the standard binary protocol inside Baidu](baidu_std.md), using single connection by default.
- PROTOCOL_HTTP or "http", which is http/1.0 or http/1.1, using pooled connection by default (Keep-Alive).
- Methods for accessing ordinary http services are listed in [Access http/h2](http_client.md).
- Methods for accessing pb services by using http:json or http:proto are listed in [Protocols based on http/h2](http_derivatives.md)
- PROTOCOL_H2 or ”h2", which is http/2.0, using single connection by default.
- Methods for accessing ordinary h2 services are listed in [Access http/h2](http_client.md).
- Methods for accessing pb services by using h2:json or h2:proto are listed in [Protocols based on http/h2](http_derivatives.md)
- "h2:grpc", which is the protocol of [gRPC](https://grpc.io) and based on h2, using single connection by default, check out [Protocols based on http/h2](http_derivatives.md) for details.
- PROTOCOL_THRIFT or "thrift", which is the protocol of [apache thrift](https://thrift.apache.org), using pooled connection by default, check out [access thrift](thrift.md) for details.
- PROTOCOL_MEMCACHE or "memcache", which is binary protocol of memcached, using **single connection** by default. Check out [access memcached](memcache_client.md) for details.
- PROTOCOL_REDIS or "redis", which is protocol of redis 1.2+ (the one supported by hiredis), using **single connection** by default. Check out [Access Redis](redis_client.md) for details.
- PROTOCOL_HULU_PBRPC or "hulu_pbrpc", which is protocol of hulu-pbrpc, using single connection by default. - PROTOCOL_HULU_PBRPC or "hulu_pbrpc", which is protocol of hulu-pbrpc, using single connection by default.
- PROTOCOL_NOVA_PBRPC or "nova_pbrpc", which is protocol of Baidu ads union, using pooled connection by default. - PROTOCOL_NOVA_PBRPC or "nova_pbrpc", which is protocol of Baidu ads union, using pooled connection by default.
- PROTOCOL_HTTP or "http", which is http 1.0 or 1.1, using pooled connection by default (Keep-Alive). Check out [Access HTTP service](http_client.md) for details.
- PROTOCOL_SOFA_PBRPC or "sofa_pbrpc", which is protocol of sofa-pbrpc, using single connection by default. - PROTOCOL_SOFA_PBRPC or "sofa_pbrpc", which is protocol of sofa-pbrpc, using single connection by default.
- PROTOCOL_PUBLIC_PBRPC or "public_pbrpc", which is protocol of public_pbrpc, using pooled connection by default. - PROTOCOL_PUBLIC_PBRPC or "public_pbrpc", which is protocol of public_pbrpc, using pooled connection by default.
- PROTOCOL_UBRPC_COMPACK or "ubrpc_compack", which is protocol of public/ubrpc, packing with compack, using pooled connection by default. check out [ubrpc (by protobuf)](ub_client.md) for details. A related protocol is PROTOCOL_UBRPC_MCPACK2 or ubrpc_mcpack2, packing with mcpack2. - PROTOCOL_UBRPC_COMPACK or "ubrpc_compack", which is protocol of public/ubrpc, packing with compack, using pooled connection by default. check out [ubrpc (by protobuf)](ub_client.md) for details. A related protocol is PROTOCOL_UBRPC_MCPACK2 or ubrpc_mcpack2, packing with mcpack2.
- PROTOCOL_NSHEAD_CLIENT or "nshead_client", which is required by UBXXXRequest in baidu-rpc-ub, using pooled connection by default. Check out [Access UB](ub_client.md) for details. - PROTOCOL_NSHEAD_CLIENT or "nshead_client", which is required by UBXXXRequest in baidu-rpc-ub, using pooled connection by default. Check out [Access UB](ub_client.md) for details.
- PROTOCOL_NSHEAD or "nshead", which is required by sending NsheadMessage, using pooled connection by default. Check out [nshead+blob](ub_client.md#nshead-blob) for details. - PROTOCOL_NSHEAD or "nshead", which is required by sending NsheadMessage, using pooled connection by default. Check out [nshead+blob](ub_client.md#nshead-blob) for details.
- PROTOCOL_MEMCACHE or "memcache", which is binary protocol of memcached, using **single connection** by default. Check out [access memcached](memcache_client.md) for details.
- PROTOCOL_REDIS or "redis", which is protocol of redis 1.2+ (the one supported by hiredis), using **single connection** by default. Check out [Access Redis](redis_client.md) for details.
- PROTOCOL_NSHEAD_MCPACK or "nshead_mcpack", which is as the name implies, nshead + mcpack (parsed by protobuf via mcpack2pb), using pooled connection by default. - PROTOCOL_NSHEAD_MCPACK or "nshead_mcpack", which is as the name implies, nshead + mcpack (parsed by protobuf via mcpack2pb), using pooled connection by default.
- PROTOCOL_ESP or "esp", for accessing services with esp protocol, using pooled connection by default. - PROTOCOL_ESP or "esp", for accessing services with esp protocol, using pooled connection by default.
...@@ -600,7 +611,7 @@ The default protocol used by Channel is baidu_std, which is changeable by settin ...@@ -600,7 +611,7 @@ The default protocol used by Channel is baidu_std, which is changeable by settin
brpc supports following connection types: brpc supports following connection types:
- short connection: Established before each RPC, closed after completion. Since each RPC has to pay the overhead of establishing connection, this type is used for occasionally launched RPC, not frequently launched ones. No protocol use this type by default. Connections in http 1.0 are handled similarly as short connections. - short connection: Established before each RPC, closed after completion. Since each RPC has to pay the overhead of establishing connection, this type is used for occasionally launched RPC, not frequently launched ones. No protocol use this type by default. Connections in http 1.0 are handled similarly as short connections.
- pooled connection: Pick an unused connection from a pool before each RPC, return after completion. One connection carries at most one request at the same time. One client may have multiple connections to one server. http and the protocols using nshead use this type by default. - pooled connection: Pick an unused connection from a pool before each RPC, return after completion. One connection carries at most one request at the same time. One client may have multiple connections to one server. http 1.1 and the protocols using nshead use this type by default.
- single connection: all clients in one process has at most one connection to one server, one connection may carry multiple requests at the same time. The sequence of received responses does not need to be same as sending requests. This type is used by baidu_std, hulu_pbrpc, sofa_pbrpc by default. - single connection: all clients in one process has at most one connection to one server, one connection may carry multiple requests at the same time. The sequence of received responses does not need to be same as sending requests. This type is used by baidu_std, hulu_pbrpc, sofa_pbrpc by default.
| | short connection | pooled connection | single connection | | | short connection | pooled connection | single connection |
...@@ -671,7 +682,7 @@ baidu_std and hulu_pbrpc supports attachments which are sent along with messages ...@@ -671,7 +682,7 @@ baidu_std and hulu_pbrpc supports attachments which are sent along with messages
Attachment is not compressed by framework. Attachment is not compressed by framework.
In http, attachment corresponds to [message body](http://www.w3.org/Protocols/rfc2616/rfc2616-sec4.html), namely the data to post to server is stored in request_attachment(). In http/h2, attachment corresponds to [message body](http://www.w3.org/Protocols/rfc2616/rfc2616-sec4.html), namely the data to post to server is stored in request_attachment().
## Turn on SSL ## Turn on SSL
...@@ -729,7 +740,7 @@ set_request_compress_type() sets compress-type of the request, no compression by ...@@ -729,7 +740,7 @@ set_request_compress_type() sets compress-type of the request, no compression by
NOTE: Attachment is not compressed by brpc. NOTE: Attachment is not compressed by brpc.
Check out [compress request body](http_client#压缩request-body) to compress http body. Check out [compress request body](http_client#压缩request-body) to compress http/h2 body.
Supported compressions: Supported compressions:
......
...@@ -4,22 +4,28 @@ ...@@ -4,22 +4,28 @@
[example/http_c++](https://github.com/brpc/brpc/blob/master/example/http_c++/http_client.cpp) [example/http_c++](https://github.com/brpc/brpc/blob/master/example/http_c++/http_client.cpp)
# About h2
brpc names the HTTP/2 protocol to "h2", no matter encrypted or not. However HTTP/2 connections without SSL are shown on /connections with the official name "h2c", and the ones with SSL are shown as "h2".
The APIs for http and h2 in brpc are basically same. Without explicit statement, mentioned http features work for h2 as well.
# Create Channel # Create Channel
In order to use `brpc::Channel` to access HTTP services, `ChannelOptions.protocol` must be set to `PROTOCOL_HTTP`. In order to use `brpc::Channel` to access http/h2 services, `ChannelOptions.protocol` must be set to `PROTOCOL_HTTP` or `PROTOCOL_H2`.
Once the HTTP protocol is set, the first parameter of `Channel::Init` can be any valid URL. *Note*: Only host and port inside the URL are used by Init(), other parts are discarded. Allowing full URL simply saves the user from additional parsing code. Once the protocol is set, the first parameter of `Channel::Init` can be any valid URL. *Note*: Only host and port inside the URL are used by Init(), other parts are discarded. Allowing full URL simply saves the user from additional parsing code.
```c++ ```c++
brpc::ChannelOptions options; brpc::ChannelOptions options;
options.protocol = brpc::PROTOCOL_HTTP; options.protocol = brpc::PROTOCOL_HTTP; // or brpc::PROTOCOL_H2
if (channel.Init("www.baidu.com" /*any url*/, &options) != 0) { if (channel.Init("www.baidu.com" /*any url*/, &options) != 0) {
LOG(ERROR) << "Fail to initialize channel"; LOG(ERROR) << "Fail to initialize channel";
return -1; return -1;
} }
``` ```
http channel also support BNS address or other naming services. http/h2 channel also support BNS address or other naming services.
# GET # GET
...@@ -29,9 +35,9 @@ cntl.http_request().uri() = "www.baidu.com/index.html"; // Request URL ...@@ -29,9 +35,9 @@ cntl.http_request().uri() = "www.baidu.com/index.html"; // Request URL
channel.CallMethod(NULL, &cntl, NULL, NULL, NULL/*done*/); channel.CallMethod(NULL, &cntl, NULL, NULL, NULL/*done*/);
``` ```
HTTP does not relate to protobuf much, thus all parameters of `CallMethod` are NULL except `Controller` and `done`. Issue asynchronous RPC with non-NULL `done`. http/h2 does not relate to protobuf much, thus all parameters of `CallMethod` are NULL except `Controller` and `done`. Issue asynchronous RPC with non-NULL `done`.
`cntl.response_attachment()` is body of the http response and typed `butil::IOBuf`. `IOBuf` can be converted to `std::string` by `to_string()`, which needs to allocate memory and copy all data. If performance is important, the code should consider supporting `IOBuf` directly rather than requiring continuous memory. `cntl.response_attachment()` is body of the http/h2 response and typed `butil::IOBuf`. `IOBuf` can be converted to `std::string` by `to_string()`, which needs to allocate memory and copy all data. If performance is important, the code should consider supporting `IOBuf` directly rather than requiring continuous memory.
# POST # POST
...@@ -57,6 +63,19 @@ os.move_to(cntl.request_attachment()); ...@@ -57,6 +63,19 @@ os.move_to(cntl.request_attachment());
channel.CallMethod(NULL, &cntl, NULL, NULL, NULL/*done*/); channel.CallMethod(NULL, &cntl, NULL, NULL, NULL/*done*/);
``` ```
# Change HTTP version
brpc behaves as http 1.1 by default.
Comparing to 1.1, http 1.0 lacks of long connections(KeepAlive). To communicate brpc client with some legacy http servers, the client may be configured as follows:
```c++
cntl.http_request().set_version(1, 0);
```
Setting http version does not work for h2, but the versions in h2 responses received by client and h2 requests received by server are set to (2, 0).
brpc server recognizes http versions automically and responds accordingly without users' aid.
# URL # URL
Genaral form of an URL: Genaral form of an URL:
...@@ -88,16 +107,18 @@ As we saw in examples above, `Channel.Init()` and `cntl.http_request().uri()` bo ...@@ -88,16 +107,18 @@ As we saw in examples above, `Channel.Init()` and `cntl.http_request().uri()` bo
Indeed, the settings are repeated in simple cases. But they are different in more complex scenes: Indeed, the settings are repeated in simple cases. But they are different in more complex scenes:
- Access multiple servers under a NamingService (for example BNS), in which case `Channel::Init` accepts a name meaningful to the NamingService(for example node names in BNS), while `uri()` is assigned with the URL. - Access multiple servers under a NamingService (for example BNS), in which case `Channel::Init` accepts a name meaningful to the NamingService(for example node names in BNS), while `uri()` is assigned with the URL.
- Access servers via http proxy, in which case `Channel::Init` takes the address of the proxy server, while `uri()` is still assigned with the URL. - Access servers via http/h2 proxy, in which case `Channel::Init` takes the address of the proxy server, while `uri()` is still assigned with the URL.
## Host header ## Host header
If user already sets `Host` (a http header), framework makes no change. If user already sets `Host` header(case insensitive), framework makes no change.
If user does not set `Host` header and the URL has host, for example http://www.foo.com/path, the http request contains "Host: www.foo.com". If user does not set `Host` header and the URL has host, for example http://www.foo.com/path, the http request contains "Host: www.foo.com".
If user does not set host header and the URL does not have host as well, for example "/index.html?name=value", framework sets `Host` header with IP and port of the target server. A http server at 10.46.188.39:8989 should see `Host: 10.46.188.39:8989`. If user does not set host header and the URL does not have host as well, for example "/index.html?name=value", framework sets `Host` header with IP and port of the target server. A http server at 10.46.188.39:8989 should see `Host: 10.46.188.39:8989`.
The header is named ":authority" in h2.
# Common usages # Common usages
Take http request as an example (similar with http response), common operations are listed as follows: Take http request as an example (similar with http response), common operations are listed as follows:
......
[中文版](../cn/http_derivatives.md)
Basics for accessing and serving http in brpc are listed in [http_client](http_client.md) and [http_service](http_service.md).
Following section names are protocol names that can be directly set to ChannelOptions.protocol. The content after colon is parameters for the protocol to select derivative behaviors dynamically, but the base protocol is still http/1.x or http/2. As a result, these protocols are displayed at server-side as http or h2/h2c only.
# http:json, h2:json
Non-empty pb request is serialized to json and set to the body of the http/h2 request. The Controller.request_attachment() must be empty otherwise the RPC fails.
Non-empty pb response is converted from a json which is parsed from the body of the http/h2 response.
http/1.x behaves in this way by default, so "http" and "http:json" are just same.
# http:proto, h2:proto
Non-empty pb request is serialized (in pb's wire format) and set to the body of the http/h2 request. The Controller.request_attachment() must be empty otherwise the RPC fails.
Non-empty pb response is parsed from the body of the http/h2 response(in pb's wire format).
http/2 behaves in this way by default, so "h2" and "h2:proto" are just same.
# h2:grpc
Default protocol of [gRPC](https://github.com/grpc). The detailed format is described in [gRPC over HTTP2](https://github.com/grpc/grpc/blob/master/doc/PROTOCOL-HTTP2.md).
Clients using brpc should be able to talk with gRPC after changing ChannelOptions.protocol to "h2:grpc".
Servers using brpc should be accessible by gRPC clients automatically without changing the code.
gRPC serializes message into pb wire format by default, so "h2:grpc" and "h2:grpc+proto" are just same.
TODO: Other configurations for gRPC
# h2:grpc+json
Comparing to h2:grpc, this protocol serializes messages into json instead of pb, which may not be supported by gRPC directly. For example, grpc-go may reference [here](https://github.com/johanbrandhorst/grpc-json-example/blob/master/codec/json.go) to register the corresponding codec and turn on the support.
[中文版](../cn/http_service.md) [中文版](../cn/http_service.md)
This document talks about ordinary HTTP services rather than protobuf services accessible via HTTP. HTTP services in brpc have to declare interfaces with empty request and response in a .proto file. This requirement keeps all service declarations inside proto files rather than scattering in code, configurations, and proto files. Check [http_server.cpp](https://github.com/brpc/brpc/blob/master/example/http_c++/http_server.cpp) for an example. This document talks about ordinary htt/h2 services rather than protobuf services accessible via http/h2.
http/h2 services in brpc have to declare interfaces with empty request and response in a .proto file. This requirement keeps all service declarations inside proto files rather than scattering in code, configurations, and proto files.
# Example
[http_server.cpp](https://github.com/brpc/brpc/blob/master/example/http_c++/http_server.cpp)
# About h2
brpc names the HTTP/2 protocol to "h2", no matter encrypted or not. However HTTP/2 connections without SSL are shown on /connections with the official name "h2c", and the ones with SSL are shown as "h2".
The APIs for http and h2 in brpc are basically same. Without explicit statement, mentioned http features work for h2 as well.
# URL types # URL types
## /ServiceName/MethodName as the prefix ## /ServiceName/MethodName as the prefix
Define a service named `ServiceName`(not including the package name), with a method named `MethodName` and empty request/response, the service will provide http service on `/ServiceName/MethodName` by default. Define a service named `ServiceName`(not including the package name), with a method named `MethodName` and empty request/response, the service will provide http/h2 service on `/ServiceName/MethodName` by default.
The reason that request and response can be empty is that the HTTP data is in Controller: The reason that request and response can be empty is that all data are in Controller:
- Header of the http request is in Controller.http_request() and the body is in Controller.request_attachment(). - Header of the http/h2 request is in Controller.http_request() and the body is in Controller.request_attachment().
- Header of the http response is in Controller.http_response() and the body is in Controller.response_attachment(). - Header of the http/h2 response is in Controller.http_response() and the body is in Controller.response_attachment().
Implementation steps: Implementation steps:
...@@ -69,7 +80,7 @@ public: ...@@ -69,7 +80,7 @@ public:
## /ServiceName as the prefix ## /ServiceName as the prefix
HTTP services to manage resources may need this kind of URL, such as `/FileService/foobar.txt` represents `./foobar.txt` and `/FileService/app/data/boot.cfg` represents `./app/data/boot.cfg`. http/h2 services for managing resources may need this kind of URL, such as `/FileService/foobar.txt` represents `./foobar.txt` and `/FileService/app/data/boot.cfg` represents `./app/data/boot.cfg`.
Implementation steps: Implementation steps:
...@@ -121,13 +132,13 @@ brpc supports specifying a URL for each method in a service. The API is as follo ...@@ -121,13 +132,13 @@ brpc supports specifying a URL for each method in a service. The API is as follo
// If `restful_mappings' is non-empty, the method in service can // If `restful_mappings' is non-empty, the method in service can
// be accessed by the specified URL rather than /ServiceName/MethodName. // be accessed by the specified URL rather than /ServiceName/MethodName.
// Mapping rules: "PATH1 => NAME1, PATH2 => NAME2 ..." // Mapping rules: "PATH1 => NAME1, PATH2 => NAME2 ..."
// where `PATH' is a valid HTTP path and `NAME' is the method name. // where `PATH' is a valid path and `NAME' is the method name.
int AddService(google::protobuf::Service* service, int AddService(google::protobuf::Service* service,
ServiceOwnership ownership, ServiceOwnership ownership,
butil::StringPiece restful_mappings); butil::StringPiece restful_mappings);
``` ```
`QueueService` defined below contains several HTTP methods. If the service is added into the server normally, it's accessible via URLs like `/QueueService/start` and ` /QueueService/stop`. `QueueService` defined below contains several methods. If the service is added into the server normally, it's accessible via URLs like `/QueueService/start` and ` /QueueService/stop`.
```protobuf ```protobuf
service QueueService { service QueueService {
...@@ -165,7 +176,7 @@ There are 3 mappings separated by comma in the 3rd parameter (which is a string ...@@ -165,7 +176,7 @@ There are 3 mappings separated by comma in the 3rd parameter (which is a string
More about mapping rules: More about mapping rules:
- Multiple paths can be mapped to a same method. - Multiple paths can be mapped to a same method.
- Both HTTP and protobuf services are supported. - Both http/h2 and protobuf services are supported.
- Un-mapped methods are still accessible via `/ServiceName/MethodName`. Mapped methods are **not** accessible via `/ServiceName/MethodName` anymore. - Un-mapped methods are still accessible via `/ServiceName/MethodName`. Mapped methods are **not** accessible via `/ServiceName/MethodName` anymore.
- `==>` and ` ===>` are both OK, namely extra spaces at the beginning or the end, extra slashes, extra commas at the end, are all accepted. - `==>` and ` ===>` are both OK, namely extra spaces at the beginning or the end, extra slashes, extra commas at the end, are all accepted.
- Pattern `PATH` and `PATH/*` can coexist. - Pattern `PATH` and `PATH/*` can coexist.
......
...@@ -38,13 +38,13 @@ An industrial-grade RPC framework used throughout [Baidu](http://ir.baidu.com/ph ...@@ -38,13 +38,13 @@ An industrial-grade RPC framework used throughout [Baidu](http://ir.baidu.com/ph
You can use it to: You can use it to:
* Build a server that can talk in multiple protocols (**on same port**), or access all sorts of services * Build a server that can talk in multiple protocols (**on same port**), or access all sorts of services
* restful http/https, h2/h2c (compatible with [grpc](https://github.com/grpc/grpc), will be opensourced). using http in brpc is much more friendly than [libcurl](https://curl.haxx.se/libcurl/). * restful http/https, [h2](https://http2.github.io/http2-spec)/[gRPC](https://grpc.io). using http/h2 in brpc is much more friendly than [libcurl](https://curl.haxx.se/libcurl/). Access protobuf-based protocols with HTTP/h2+json, probably from another language.
* [redis](redis_client.md) and [memcached](memcache_client.md), thread-safe, more friendly and performant than the official clients * [redis](redis_client.md) and [memcached](memcache_client.md), thread-safe, more friendly and performant than the official clients
* [rtmp](https://github.com/brpc/brpc/blob/master/src/brpc/rtmp.h)/[flv](https://en.wikipedia.org/wiki/Flash_Video)/[hls](https://en.wikipedia.org/wiki/HTTP_Live_Streaming), for building [streaming services](https://github.com/brpc/media-server). * [rtmp](https://github.com/brpc/brpc/blob/master/src/brpc/rtmp.h)/[flv](https://en.wikipedia.org/wiki/Flash_Video)/[hls](https://en.wikipedia.org/wiki/HTTP_Live_Streaming), for building [streaming services](https://github.com/brpc/media-server).
* hadoop_rpc (may be opensourced) * hadoop_rpc (may be opensourced)
* [rdma](https://en.wikipedia.org/wiki/Remote_direct_memory_access) support (will be opensourced) * [rdma](https://en.wikipedia.org/wiki/Remote_direct_memory_access) support (will be opensourced)
* [thrift](thrift.md) support, thread-safe, more friendly and performant than the official clients.
* all sorts of protocols used in Baidu: [baidu_std](../cn/baidu_std.md), [streaming_rpc](streaming_rpc.md), hulu_pbrpc, [sofa_pbrpc](https://github.com/baidu/sofa-pbrpc), nova_pbrpc, public_pbrpc, ubrpc, and nshead-based ones. * all sorts of protocols used in Baidu: [baidu_std](../cn/baidu_std.md), [streaming_rpc](streaming_rpc.md), hulu_pbrpc, [sofa_pbrpc](https://github.com/baidu/sofa-pbrpc), nova_pbrpc, public_pbrpc, ubrpc, and nshead-based ones.
* Access protobuf-based protocols with HTTP+json, probably from another language.
* Build [HA](https://en.wikipedia.org/wiki/High_availability) distributed services using an industrial-grade implementation of [RAFT consensus algorithm](https://raft.github.io) which is opensourced at [braft](https://github.com/brpc/braft) * Build [HA](https://en.wikipedia.org/wiki/High_availability) distributed services using an industrial-grade implementation of [RAFT consensus algorithm](https://raft.github.io) which is opensourced at [braft](https://github.com/brpc/braft)
* Servers can handle requests [synchronously](server.md) or [asynchronously](server.md#asynchronous-service). * Servers can handle requests [synchronously](server.md) or [asynchronously](server.md#asynchronous-service).
* Clients can access servers [synchronously](client.md#synchronus-call), [asynchronously](client.md#asynchronous-call), [semi-synchronously](client.md#semi-synchronous-call), or use [combo channels](combo_channel.md) to simplify sharded or parallel accesses declaratively. * Clients can access servers [synchronously](client.md#synchronus-call), [asynchronously](client.md#asynchronous-call), [semi-synchronously](client.md#semi-synchronous-call), or use [combo channels](combo_channel.md) to simplify sharded or parallel accesses declaratively.
......
...@@ -251,9 +251,12 @@ server.RunUntilAskedToQuit(); ...@@ -251,9 +251,12 @@ server.RunUntilAskedToQuit();
Services can be added or removed after Join() returns and server can be Start() again. Services can be added or removed after Join() returns and server can be Start() again.
# Accessed by HTTP client # Accessed by http/h2
Services using protobuf can be accessed via http/h2+json generally. The json string stored in body is convertible to/from corresponding protobuf message.
[echo server](https://github.com/brpc/brpc/blob/master/example/echo_c%2B%2B/server.cpp) as an example, is accessible from [curl](https://curl.haxx.se/).
Services using protobuf can be accessed via http+json generally. The json string stored in http body is convertible to/from corresponding protobuf message. [echo server](https://github.com/brpc/brpc/blob/master/example/echo_c%2B%2B/server.cpp) as an example, is accessible from [curl](https://curl.haxx.se/).
```shell ```shell
# -H 'Content-Type: application/json' is optional # -H 'Content-Type: application/json' is optional
...@@ -261,7 +264,7 @@ $ curl -d '{"message":"hello"}' http://brpc.baidu.com:8765/EchoService/Echo ...@@ -261,7 +264,7 @@ $ curl -d '{"message":"hello"}' http://brpc.baidu.com:8765/EchoService/Echo
{"message":"hello"} {"message":"hello"}
``` ```
Note: Set `Content-Type: application/proto` to access services with http + protobuf-serialized-data, which performs better at serialization. Note: Set `Content-Type: application/proto` to access services with http/h2 + protobuf-serialized-data, which performs better at serialization.
## json<=>pb ## json<=>pb
...@@ -271,7 +274,7 @@ When -pb_enum_as_number is turned on, enums in pb are converted to values instea ...@@ -271,7 +274,7 @@ When -pb_enum_as_number is turned on, enums in pb are converted to values instea
## Adapt old clients ## Adapt old clients
Early-version brpc allows pb service being accessed via http without setting the pb request, even if there're required fields in. This kind of service often parses http requests and sets http responses by itself, and does not touch the pb request. However this behavior is still very dangerous: a service with an undefined request. Early-version brpc allows pb service being accessed via http without filling the pb request, even if there're required fields. This kind of service often parses http requests and sets http responses by itself, and does not touch the pb request. However this behavior is still very dangerous: a service with an undefined request.
This kind of services may meet issues after upgrading to latest brpc, which already deprecated the behavior for a long time. To help these services to upgrade, brpc allows bypassing the conversion from http body to pb request (so that users can parse http requests differently), the setting is as follows: This kind of services may meet issues after upgrading to latest brpc, which already deprecated the behavior for a long time. To help these services to upgrade, brpc allows bypassing the conversion from http body to pb request (so that users can parse http requests differently), the setting is as follows:
...@@ -279,13 +282,13 @@ This kind of services may meet issues after upgrading to latest brpc, which alre ...@@ -279,13 +282,13 @@ This kind of services may meet issues after upgrading to latest brpc, which alre
brpc::ServiceOptions svc_opt; brpc::ServiceOptions svc_opt;
svc_opt.ownership = ...; svc_opt.ownership = ...;
svc_opt.restful_mappings = ...; svc_opt.restful_mappings = ...;
svc_opt.allow_http_body_to_pb = false; // turn off conversion from http body to pb request svc_opt.allow_http_body_to_pb = false; // turn off conversion from http/h2 body to pb request
server.AddService(service, svc_opt); server.AddService(service, svc_opt);
``` ```
After the setting, service does not convert http body to pb request after receiving http request, which also makes the pb request undefined. Users have to parse the http body by themselves when `cntl->request_protocol() == brpc::PROTOCOL_HTTP` is true which indicates the request is from http. After the setting, service does not convert the body to pb request after receiving http/h2 request, which also makes the pb request undefined. Users have to parse the body by themselves when `cntl->request_protocol() == brpc::PROTOCOL_HTTP || cntl->request_protocol() == brpc::PROTOCOL_H2` is true which indicates the request is from http/h2.
As a correspondence, if cntl->response_attachment() is not empty and pb response is set as well, brpc does not report the ambiguous anymore, instead cntl->response_attachment() will be used as body of the http response. This behavior is unaffected by setting allow_http_body_to_pb or not. If the relaxation results in more users' errors, we may restrict it in future. As a correspondence, if cntl->response_attachment() is not empty and pb response is set as well, brpc does not report the ambiguous anymore, instead cntl->response_attachment() will be used as body of the http/h2 response. This behavior is unaffected by setting allow_http_body_to_pb or not. If the relaxation results in more users' errors, we may restrict it in future.
# Protocols # Protocols
...@@ -295,7 +298,9 @@ Server detects supported protocols automatically, without assignment from users. ...@@ -295,7 +298,9 @@ Server detects supported protocols automatically, without assignment from users.
- [Streaming RPC](streaming_rpc.md), shown as "streaming_rpc", enabled by default. - [Streaming RPC](streaming_rpc.md), shown as "streaming_rpc", enabled by default.
- http 1.0/1.1, shown as "http", enabled by default. - http/1.0 and http/1.1, shown as "http", enabled by default.
- http/2 and gRPC, shown as "h2c"(unencrypted) or "h2"(encrypted), enabled by default.
- Protocol of RTMP, shown as "rtmp", enabled by default. - Protocol of RTMP, shown as "rtmp", enabled by default.
......
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