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
364a2f1f
Commit
364a2f1f
authored
Aug 29, 2017
by
gejun
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
more docs
parent
ec901d82
Show whitespace changes
Inline
Side-by-side
Showing
2 changed files
with
194 additions
and
8 deletions
+194
-8
access_http.md
docs/access_http.md
+191
-0
combo_channel.md
docs/combo_channel.md
+3
-8
No files found.
docs/access_http.md
0 → 100644
View file @
364a2f1f
http client的例子见
[
example/http_c++
](
https://svn.baidu.com/public/trunk/baidu-rpc/example/http_c++/http_client.cpp
)
# 创建Channel
baidu::rpc::Channel可访问HTTP服务,ChannelOptions.protocol须指定为PROTOCOL_HTTP。
设定为HTTP协议后,Channel::Init的第一个参数可为任意合法的URL。注意:允许任意URL是为了省去用户取出host和port的麻烦,Channel::Init只用其中的host及port,其他部分都会丢弃。
```
c++
baidu
::
rpc
::
ChannelOptions
options
;
options
.
protocol
=
baidu
::
rpc
::
PROTOCOL_HTTP
;
if
(
channel
.
Init
(
"www.baidu.com"
/*any url*/
,
&
options
)
!=
0
)
{
LOG
(
ERROR
)
<<
"Fail to initialize channel"
;
return
-
1
;
}
```
http channel也支持bns地址。
# GET
```
c++
baidu
::
rpc
::
Controller
cntl
;
cntl
.
http_request
().
uri
()
=
"www.baidu.com/index.html"
;
// 设置为待访问的URL
channel
.
CallMethod
(
NULL
,
&
cntl
,
NULL
,
NULL
,
NULL
/*done*/
);
```
HTTP和protobuf无关,所以除了Controller和done,CallMethod的其他参数均为NULL。如果要异步操作,最后一个参数传入done。
cntl.response_attachment()是回复的body,类型也是base::IOBuf。注意IOBuf转化为std::string(通过to_string()接口)是需要分配内存并拷贝所有内容的,如果关注性能,你的处理过程应该尽量直接支持IOBuf,而不是要求连续内存。
# POST
默认的HTTP Method为GET,如果需要做POST,则需要设置。待POST的数据应置入request_attachment(),它(
[
base::IOBuf
](
https://svn.baidu.com/public/trunk/iobuf/base/iobuf.h
)
)可以直接append std::string或char
*
```
c++
baidu
::
rpc
::
Controller
cntl
;
cntl
.
http_request
().
uri
()
=
"..."
;
// 设置为待访问的URL
cntl
.
http_request
().
set_method
(
baidu
::
rpc
::
HTTP_METHOD_POST
);
cntl
.
request_attachment
().
append
(
"{
\"
message
\"
:
\"
hello world!
\"
}"
);
channel
.
CallMethod
(
NULL
,
&
cntl
,
NULL
,
NULL
,
NULL
/*done*/
);
```
需要大量打印过程的body建议使用base::IOBufBuilder,它的用法和std::ostringstream是一样的。对于有大量对象要打印的场景,IOBufBuilder会简化代码,并且效率也更高。
```
c++
baidu
::
rpc
::
Controller
cntl
;
cntl
.
http_request
().
uri
()
=
"..."
;
// 设置为待访问的URL
cntl
.
http_request
().
set_method
(
baidu
::
rpc
::
HTTP_METHOD_POST
);
base
::
IOBufBuilder
os
;
os
<<
"A lot of printing"
<<
printable_objects
<<
...;
os
.
move_to
(
cntl
.
request_attachment
());
channel
.
CallMethod
(
NULL
,
&
cntl
,
NULL
,
NULL
,
NULL
/*done*/
);
```
# URL
URL的一般形式如下图:
```
// URI scheme : http://en.wikipedia.org/wiki/URI_scheme
//
// foo://username:password@example.com:8042/over/there/index.dtb?type=animal&name=narwhal#nose
// \_/ \_______________/ \_________/ \__/ \___/ \_/ \______________________/ \__/
// | | | | | | | |
// | userinfo host port | | query fragment
// | \________________________________/\_____________|____|/ \__/ \__/
// schema | | | | | |
// authority | | | | |
// path | | interpretable as keys
// | |
// \_______________________________________________|____|/ \____/ \_____/
// | | | | |
// hierarchical part | | interpretable as values
// | |
// interpretable as filename |
// |
// |
// interpretable as extension
```
问题:Channel为什么不直接利用Init时传入的URL,而需要给uri()再设置一次?
确实,在简单使用场景下,这两者有所重复,但在复杂场景中,两者差别很大,比如:
-
访问挂在bns下的多个http server。此时Channel.Init传入的是bns节点名称,对uri()的赋值则是包含Host的完整URL(比如"www.foo.com/index.html?name=value"),BNS下所有的http server都会看到"Host:
[
www.foo.com
](
http://www.foo.com/
)
";uri()也可以是只包含路径的URL,比如"/index.html?name=value",框架会以目标server的ip和port为Host,地址为10.46.188.39:8989的http server将会看到"Host: 10.46.188.39:8989"。
-
通过http proxy访问目标server。此时Channel.Init传入的是proxy server的地址,但uri()填入的是目标server的URL。
# 常见设置
**以http request为例(对response的操作自行替换)**
,常见操作方式如下表所示:
| 操作 | 方法 |
| -------------- | ---------------------------------------- |
| 访问名为Foo的header |
`const std::string* value = cntl->http_request().GetHeader("Foo"); //不存在为NULL`
|
| 设置名为Foo的header |
`cntl->http_request().SetHeader("Foo", "value")`
|
| 访问名为Foo的query |
`const std::string* value = cntl->http_request().uri().GetQuery("Foo"); // 不存在为NULL`
|
| 设置名为Foo的query |
`cntl->http_request().uri().SetQuery("Foo", "value")`
|
| 设置HTTP方法 |
`cntl->http_request().set_method(baidu::rpc::HTTP_METHOD_POST);`
|
| 设置url |
`cntl->http_request().uri() = "http://www.baidu.com";`
|
| 设置content-type |
`cntl->http_request().set_content_type("text/plain");`
|
| 访问body |
`base::IOBuf& buf = cntl->request_attachment(); std::string str = cntl->request_attachment().to_string(); // 有拷贝`
|
| 设置body |
`cntl->request_attachment().append("...."); base::IOBufBuilder os; os << "...."; os.move_to(cntl->request_attachment());`
|
Notes on http header:
-
根据 HTTP 协议
[
规定
](
http://www.w3.org/Protocols/rfc2616/rfc2616-sec4.html#sec4.2
)
, header 的 field_name部分不区分大小写。在r33861前,field_name都转为了小写,在r33861后,大小写能保持不变(仍然支持大小写不敏感)。
-
如果 HTTP 头中出现了相同的 field_name, 根据协议
[
规定
](
http://www.w3.org/Protocols/rfc2616/rfc2616-sec4.html#sec4.2
)
,value将被合并到一起, 中间用逗号(,) 分隔, 具体value如何理解,需要用户自己确定.
-
query之间用"&"分隔, key和value之间用"="分隔, value可以省略,比如key1=value1&key2&key3=value3中key2是合理的query,值为空字符串。
# 查看client发出的请求和收到的回复
打开
[
-http_verbose
](
http://brpc.baidu.com:8765/flags/http_verbose
)
即可在stderr看到所有的http request和response,注意这应该只用于线下调试,而不是线上程序。
# HTTP的错误处理
当Server返回的http status code不是2xx时,该次http访问即视为失败,client端会设置对应的ErrorCode:
-
在r31923前,1xx和3xx错误对应EPROTONOSUPPORT,4xx错误对应EREQUEST,其余错误对应EINTERNAL。http body不会置入cntl->response_attachment()。
-
在r31923后,所有错误被统一为EHTTP。如果用户发现cntl->ErrorCode()为EHTTP,那么可以检查cntl->http_response().status_code()以获得具体的http错误。同时http body会置入cntl->response_attachment(),用户可以把代表错误的html或json传递回来。
# 压缩request body
在r33877后,调用Controller::set_request_compress_type(baidu::rpc::COMPRESS_TYPE_GZIP)可将http body用gzip压缩,并设置"Content-Encoding"为"gzip"。
# 解压response body
出于通用性考虑且解压代码不复杂,baidu-rpc不会自动解压response body,用户可以自己做,方法如下:
```
c++
#include <baidu/rpc/policy/gzip_compress.h>
...
const
std
::
string
*
encoding
=
cntl
->
http_response
().
GetHeader
(
"Content-Encoding"
);
if
(
encoding
!=
NULL
&&
*
encoding
==
"gzip"
)
{
base
::
IOBuf
uncompressed
;
if
(
!
baidu
::
rpc
::
policy
::
GzipDecompress
(
cntl
->
response_attachment
(),
&
uncompressed
))
{
LOG
(
ERROR
)
<<
"Fail to un-gzip response body"
;
return
;
}
cntl
->
response_attachment
().
swap
(
uncompressed
);
}
// cntl->response_attachment()中已经是解压后的数据了
```
# 持续下载
r33796前baidu-rpc client在下载一个超长的body时,需要一直等待直到body完整才会视作RPC结束,这个过程中超长body都会存在内存中,如果body是无限长的(比如直播用的flv文件),那么内存会持续增长,直到超时。换句话说,r33796前的baidu-rpc client不适合下载大文件。
r33796后baidu-rpc client支持在读取完body前就结束RPC,让用户在RPC结束后再读取持续增长的body。注意这个功能不等同于“支持http chunked mode”,baidu-rpc的http实现一直支持解析chunked mode,这里的问题是如何让用户处理超长或无限长的body,和body是否以chunked mode传输无关。
使用方法如下:
1.
首先实现ProgressiveReader,接口如下:
```c++
#include <baidu/rpc/progressive_reader.h>
...
class ProgressiveReader {
public:
// Called when one part was read.
// Error returned is treated as *permenant* and the socket where the
// data was read will be closed.
// A temporary error may be handled by blocking this function, which
// may block the HTTP parsing on the socket.
virtual base::Status OnReadOnePart(const void* data, size_t length) = 0;
// Called when there's nothing to read anymore. The `status' is a hint for
// why this method is called.
// - status.ok(): the message is complete and successfully consumed.
// - otherwise: socket was broken or OnReadOnePart() failed.
// This method will be called once and only once. No other methods will
// be called after. User can release the memory of this object inside.
virtual void OnEndOfMessage(const base::Status& status) = 0;
};
```
OnReadOnePart在每读到一段数据时被调用,OnEndOfMessage在数据结束或连接断开时调用,实现前仔细阅读注释。
2.
发起RPC前设置cntl.response_will_be_read_progressively();
这告诉baidu-rpc在读取http response时只要读完header部分RPC就可以结束了。
3.
RPC结束后调用cntl.ReadProgressiveAttachmentBy(new MyProgressiveReader);
MyProgressiveReader就是用户实现ProgressiveReader的实例。用户可以在这个实例的OnEndOfMessage接口中删除这个实例。
# 持续上传
目前Post的数据必须是完整生成好的,不适合POST超长的body。
# 访问带认证的Server
根据Server的认证方式生成对应的auth_data,并设置为http header "Authorization"的值。比如用的是curl,那就加上选项
`-H "Authorization : <auth_data>"。`
查询
[
giano文档
](
http://doc.noah.baidu.com/new/baas/base_tool.md
)
了解如何在Shell中生成auth_data。
\ No newline at end of file
docs/combo_channel.md
View file @
364a2f1f
...
...
@@ -319,7 +319,7 @@ TRACE: 09-06 10:40:42: * 0 server.cpp:192] S[0]=0 S[1]=0 S[2]=0 [total=0]
启动后每个Server每秒会打印上一秒收到的流量,目前都是0。然后我们在本地启动使用DynamicPartitionChannel的Client,初始化DynamicPartitionChannel的代码如下:
```
c++
...
...
baidu
::
rpc
::
DynamicPartitionChannel
channel
;
baidu
::
rpc
::
PartitionChannelOptions
options
;
options
.
succeed_without_server
=
true
;
// 表示允许server_list在DynamicPartitionChannel.Init启动时为空,否则Init会失败。
...
...
@@ -328,19 +328,16 @@ TRACE: 09-06 10:40:42: * 0 server.cpp:192] S[0]=0 S[1]=0 S[2]=0 [total=0]
LOG
(
ERROR
)
<<
"Fail to init channel"
;
return
-
1
;
}
...
...
```
名字服务"file://server_list"的内容是:
```
0.0.0.0:8004 0/3 # 表示3分库中的第一个分库,其他依次类推
0.0.0.0:8004 1/3
0.0.0.0:8004 2/3
```
****
3分库方案的3个库都在8004端口对应的server上。启动Client后Client发现了8004,并向其发送流量。
```
...
...
@@ -406,8 +403,6 @@ TRACE: 09-06 10:57:15: * 0 server.cpp:192] S[0]=208453 S[1]=276803 S[2]=0 [tot
在我们这儿的场景中,3分库和4分库的容量是一样的,都是1。所有的3库都在8004,所有的4库都在8005,所以这两个Server的流量比例就是分库数的比例。
我们可以让4分库方案加入更多机器。修改server_list加入8006:
```
...
...
@@ -449,7 +444,7 @@ TRACE: 09-06 11:11:54: * 0 server.cpp:192] S[0]=135534 S[1]=180386 S[2]=180333
我们尝试下掉3分库中的一个分库:
```
0.0.0.0:8004 0/3
0.0.0.0:8004 0/3
0.0.0.0:8004 1/3
#0.0.0.0:8004 2/3
...
...
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