Commit 8dfaf1a3 authored by gejun's avatar gejun

Change base in docs to butil

parent 80f9f2f1
......@@ -25,7 +25,7 @@
- 一个依赖全局多生产者多消费者队列(MPMC)的程序难有很好的多核扩展性,因为这个队列的极限吞吐取决于同步cache的延时,而不是核心的个数。最好是用多个SPMC或多个MPSC队列,甚至多个SPSC队列代替,在源头就规避掉竞争。
- 另一个例子是全局计数器,如果所有线程都频繁修改一个全局变量,性能就会很差,原因同样在于不同的核心在不停地同步同一个cacheline。如果这个计数器只是用作打打日志之类的,那我们完全可以让每个线程修改thread-local变量,在需要时再合并所有线程中的值,性能可能有几十倍的差别。
做不到完全不共享,那就尽量少共享。在一些读很多的场景下,也许可以降低写的频率以减少同步cacheline的次数,以加快读的平均性能。一个相关的编程陷阱是避免false sharing:这指的是那些不怎么被修改的变量,由于同一个cacheline中的另一个变量被频繁修改,而不得不经常等待cacheline同步而显著变慢了。多线程中的变量尽量按访问规律排列,频繁被其他线程的修改要放在独立的cacheline中。要让一个变量或结构体按cacheline对齐,可以include <base/macros.h>然后使用BAIDU_CACHELINE_ALIGNMENT宏,用法请自行grep一下brpc的代码了解。
做不到完全不共享,那就尽量少共享。在一些读很多的场景下,也许可以降低写的频率以减少同步cacheline的次数,以加快读的平均性能。一个相关的编程陷阱是避免false sharing:这指的是那些不怎么被修改的变量,由于同一个cacheline中的另一个变量被频繁修改,而不得不经常等待cacheline同步而显著变慢了。多线程中的变量尽量按访问规律排列,频繁被其他线程的修改要放在独立的cacheline中。要让一个变量或结构体按cacheline对齐,可以include <butil/macros.h>然后使用BAIDU_CACHELINE_ALIGNMENT宏,用法请自行grep一下brpc的代码了解。
# Memory fence
......
......@@ -26,11 +26,11 @@ Channel开启backup request。这个Channel会先向其中一个server发送请
```c++
#include <bvar/bvar.h>
#include <base/time.h>
#include <butil/time.h>
...
bvar::LatencyRecorder my_func_latency("my_func");
...
base::Timer tm;
butil::Timer tm;
tm.start();
my_func();
tm.stop();
......
......@@ -123,18 +123,18 @@ extern bvar::Adder<int> g_task_pushed;
不同编译单元中全局变量的初始化顺序是[未定义的](https://isocpp.org/wiki/faq/ctors#static-init-order)。在foo.cpp中定义`Adder<int> foo_count`,在foo_qps.cpp中定义`PerSecond<Adder<int> > foo_qps(&foo_count);`**错误**的做法。
计时可以使用base::Timer,接口如下:
计时可以使用butil::Timer,接口如下:
```c++
#include <base/time.h>
namespace base {
#include <butil/time.h>
namespace butil {
class Timer {
public:
enum TimerType { STARTED };
Timer();
// base::Timer tm(base::Timer::STARTED); // tm is already started after creation.
// butil::Timer tm(butil::Timer::STARTED); // tm is already started after creation.
explicit Timer(TimerType);
// Start this timer
......@@ -149,7 +149,7 @@ public:
int64_t m_elapsed() const; // in milliseconds
int64_t s_elapsed() const; // in seconds
};
} // base
} // namespace butil
```
## 2.打开bvar的dump功能
......
# Introduction
COMAKE中增加bvar依赖:`CONFIGS('public/bvar@ci-base')`
源文件中`#include <bvar/bvar.h>`
bvar分为多个具体的类,常用的有:
- bvar::Adder<T> : 计数器,默认0,varname << N相当于varname += N。
......@@ -63,8 +62,8 @@ Variable是所有bvar的基类,主要提供全局注册,列举,查询等
// describe_exposed
// find_exposed
// Return 0 on success, -1 otherwise.
int expose(const base::StringPiece& name);
int expose(const base::StringPiece& prefix, const base::StringPiece& name);
int expose(const butil::StringPiece& name);
int expose(const butil::StringPiece& prefix, const butil::StringPiece& name);
```
全局曝光后的bvar名字便为name或prefix + name,可通过以_exposed为后缀的static函数查询。比如Variable::describe_exposed(name)会返回名为name的bvar的描述。
......@@ -104,7 +103,7 @@ bvar::Status<std::string> status1("count2", "hello"); // the name conflicts. if
// };
// } // foo
// } // bar
int expose_as(const base::StringPiece& prefix, const base::StringPiece& name);
int expose_as(const butil::StringPiece& prefix, const butil::StringPiece& name);
```
# Export all variables
......@@ -115,7 +114,7 @@ int expose_as(const base::StringPiece& prefix, const base::StringPiece& name);
// If dump() returns false, Variable::dump_exposed() stops and returns -1.
class Dumper {
public:
virtual bool dump(const std::string& name, const base::StringPiece& description) = 0;
virtual bool dump(const std::string& name, const butil::StringPiece& description) = 0;
};
// Options for Variable::dump_exposed().
......
......@@ -110,7 +110,7 @@ public:
// naming_service.h
struct ServerNode {
base::EndPoint addr;
butil::EndPoint addr;
std::string tag;
};
```
......@@ -365,12 +365,12 @@ brpc::StartCancel(CallId)可取消任意RPC,CallId必须**在发起RPC前**通
## 获取Server的地址和端口
remote_side()方法可知道request被送向了哪个server,返回值类型是[base::EndPoint](http://icode.baidu.com/repo/baidu/opensource/baidu-rpc/files/master/blob/src/base/endpoint.h),包含一个ip4地址和端口。在RPC结束前调用这个方法都是没有意义的。
remote_side()方法可知道request被送向了哪个server,返回值类型是[butil::EndPoint](http://icode.baidu.com/repo/baidu/opensource/baidu-rpc/files/master/blob/src/butil/endpoint.h),包含一个ip4地址和端口。在RPC结束前调用这个方法都是没有意义的。
打印方式:
```c++
LOG(INFO) << "remote_side=" << cntl->remote_side();
printf("remote_side=%s\n", base::endpoint2str(cntl->remote_side()).c_str());
printf("remote_side=%s\n", butil::endpoint2str(cntl->remote_side()).c_str());
```
## 获取Client的地址和端口
......@@ -379,7 +379,7 @@ r31384后通过local_side()方法可**在RPC结束后**获得发起RPC的地址
打印方式:
```c++
LOG(INFO) << "local_side=" << cntl->local_side();
printf("local_side=%s\n", base::endpoint2str(cntl->local_side()).c_str());
printf("local_side=%s\n", butil::endpoint2str(cntl->local_side()).c_str());
```
## 新建brpc::Controller的代价大吗
......@@ -732,7 +732,7 @@ FATAL 04-07 20:00:03 7778 public/brpc/src/brpc/channel.cpp:123] Invalid address=
2. 根据Channel的创建方式,从进程级的[SocketMap](http://icode.baidu.com/repo/baidu/opensource/baidu-rpc/files/master/blob/src/brpc/socket_map.h)中或从[LoadBalancer](http://icode.baidu.com/repo/baidu/opensource/baidu-rpc/files/master/blob/src/brpc/load_balancer.h)中选择一台下游server作为本次RPC发送的目的地。
3. 根据连接方式(单连接、连接池、短连接),选择一个[Socket](https://svn.baidu.com/public/trunk/baidu-rpc/src/baidu/rpc/socket.h)
4. 如果开启验证且当前Socket没有被验证过时,第一个请求进入验证分支,其余请求会阻塞直到第一个包含认证信息的请求写入Socket。这是因为server端只对第一个请求进行验证。
5. 根据Channel的协议,选择对应的序列化函数把request序列化至[IOBuf](http://icode.baidu.com/repo/baidu/opensource/baidu-rpc/files/master/blob/src/base/iobuf.h)
5. 根据Channel的协议,选择对应的序列化函数把request序列化至[IOBuf](http://icode.baidu.com/repo/baidu/opensource/baidu-rpc/files/master/blob/src/butil/iobuf.h)
6. 如果配置了超时,设置定时器。从这个点开始要避免使用Controller对象,因为在设定定时器后->有可能触发超时机制->调用到用户的异步回调->用户在回调中析构Controller。
7. 发送准备阶段结束,若上述任何步骤出错,会调用Channel::HandleSendFailed。
8. 将之前序列化好的IOBuf写出到Socket上,同时传入回调Channel::HandleSocketFailed,当连接断开、写失败等错误发生时会调用此回调。
......@@ -740,4 +740,4 @@ FATAL 04-07 20:00:03 7778 public/brpc/src/brpc/channel.cpp:123] Invalid address=
10. 网络上发消息+收消息。
11. 收到response后,提取出其中的correlation_id,在O(1)时间内找到对应的Controller。这个过程中不需要查找全局哈希表,有良好的多核扩展性。
12. 根据协议格式反序列化response。
13. 调用Controller::OnRPCReturned,其中会根据错误码判断是否需要重试。如果是异步发送,调用用户回调。最后摧毁correlation_id唤醒Join着的线程。
\ No newline at end of file
13. 调用Controller::OnRPCReturned,其中会根据错误码判断是否需要重试。如果是异步发送,调用用户回调。最后摧毁correlation_id唤醒Join着的线程。
......@@ -265,7 +265,7 @@ public:
char* endptr = NULL;
out->index = strtol(tag.c_str(), &endptr, 10);
if (endptr != tag.data() + pos) {
LOG(ERROR) << "Invalid index=" << base::StringPiece(tag.data(), pos);
LOG(ERROR) << "Invalid index=" << butil::StringPiece(tag.data(), pos);
return false;
}
out->num_partition_kinds = strtol(tag.c_str() + pos + 1, &endptr, 10);
......
......@@ -5,7 +5,7 @@ brpc可以分析程序中的热点函数。
1. 链接`libtcmalloc_and_profiler.a`
1. 这么写也开启了tcmalloc,不建议单独链接cpu profiler而不链接tcmalloc,可能越界访问导致[crash](https://github.com/gperftools/gperftools/blob/master/README#L226)**。**可能由于tcmalloc不及时归还内存,越界访问不会crash。
2. 如果tcmalloc使用frame pointer而不是libunwind回溯栈,请确保在CXXFLAGS或CFLAGS中加上`-fno-omit-frame-pointer`,否则函数间的调用关系会丢失,最后产生的图片中都是彼此独立的函数方框。
2. 定义宏BRPC_ENABLE_CPU_PROFILER。在COMAKE中加入`CXXFLAGS('-DBRPC_ENABLE_CPU_PROFILER')`
2. 定义宏BRPC_ENABLE_CPU_PROFILER, 一般加入编译参数-DBRPC_ENABLE_CPU_PROFILER。
3. 如果只是brpc client或没有使用brpc,看[这里](dummy_server.md)
注意要关闭Server端的认证,否则可能会看到这个:
......@@ -63,7 +63,7 @@ Total: 2946 samples
33 1.1% 68.8% 33 1.1% brpc::Socket::Write
33 1.1% 69.9% 33 1.1% epoll_ctl
28 1.0% 70.9% 42 1.4% brpc::policy::ProcessRpcRequest
27 0.9% 71.8% 27 0.9% base::IOBuf::_push_back_ref
27 0.9% 71.8% 27 0.9% butil::IOBuf::_push_back_ref
27 0.9% 72.7% 27 0.9% bthread::TaskGroup::ending_sched
```
......@@ -84,7 +84,7 @@ Total: 2954 samples
240 8.1% 53.9% 240 8.1% writev
90 3.0% 56.9% 90 3.0% ::cpp_alloc
67 2.3% 59.2% 67 2.3% __read_nocancel
47 1.6% 60.8% 47 1.6% base::IOBuf::_push_back_ref
47 1.6% 60.8% 47 1.6% butil::IOBuf::_push_back_ref
42 1.4% 62.2% 56 1.9% brpc::policy::ProcessRpcRequest
41 1.4% 63.6% 41 1.4% epoll_wait
38 1.3% 64.9% 38 1.3% epoll_ctl
......
......@@ -15,7 +15,7 @@ server端Controller的SetFailed()常由用户在服务回调中调用。当处
brpc使用的所有ErrorCode都定义在[errno.proto](http://icode.baidu.com/repo/baidu/opensource/baidu-rpc/files/master/blob/src/brpc/errno.proto)中,*SYS_*开头的来自linux系统,与/usr/include/errno.h中定义的精确一致,定义在proto中是为了跨语言。其余的是brpc自有的。
[berror(error_code)](http://icode.baidu.com/repo/baidu/opensource/baidu-rpc/files/master/blob/src/base/errno.h)可获得error_code的描述,berror()可获得[system errno](http://www.cplusplus.com/reference/cerrno/errno/)的描述。**ErrorText() != berror(****ErrorCode())**,ErrorText()会包含更具体的错误信息。brpc默认包含berror,你可以直接使用。
[berror(error_code)](http://icode.baidu.com/repo/baidu/opensource/baidu-rpc/files/master/blob/src/butil/errno.h)可获得error_code的描述,berror()可获得[system errno](http://www.cplusplus.com/reference/cerrno/errno/)的描述。**ErrorText() != berror(****ErrorCode())**,ErrorText()会包含更具体的错误信息。brpc默认包含berror,你可以直接使用。
brpc中常见错误的打印内容列表如下:
......
......@@ -151,7 +151,7 @@ const static TaskOptions TASK_OPTIONS_INPLACE = TaskOptions(false, true);
// Execute a task with defaut TaskOptions (normal task);
template <typename T>
int execution_queue_execute(ExecutionQueueId<T> id,
typename base::add_const_reference<T>::type task);
typename butil::add_const_reference<T>::type task);
// Thread-safe and Wait-free.
// Execute a task with options. e.g
......@@ -160,11 +160,11 @@ int execution_queue_execute(ExecutionQueueId<T> id,
// If |handle| is not NULL, we will assign it with the hanlder of this task.
template <typename T>
int execution_queue_execute(ExecutionQueueId<T> id,
typename base::add_const_reference<T>::type task,
typename butil::add_const_reference<T>::type task,
const TaskOptions* options);
template <typename T>
int execution_queue_execute(ExecutionQueueId<T> id,
typename base::add_const_reference<T>::type task,
typename butil::add_const_reference<T>::type task,
const TaskOptions* options,
TaskHandle* handle);
```
......
......@@ -4,8 +4,6 @@ brpc使用gflags管理配置。如果你的程序也使用gflags,那么你应
- 你可以在浏览器中查看brpc服务器中所有gflags,并对其动态修改(如果允许的话)。configure不可能做到这点。
- gflags分散在和其作用紧密关联的文件中,更好管理。而使用configure需要聚集到一个庞大的读取函数中。
如果你依赖了brpc,那么你已经依赖了third-64/gflags,如果你需要依赖某个特定版本的话,在COMAKE中加入CONFIGS('third-64/gflags@<specific-version>')。
# Usage of gflags
gflags一般定义在需要它的源文件中。#include <gflags/gflags.h>后在全局scope加入DEFINE_*<type>*(*<name>*, *<default-value>*, *<description>*); 比如:
......
......@@ -6,7 +6,7 @@ brpc可以分析内存是被哪些函数占据的。heap profiler的原理是每
1. 如果tcmalloc使用frame pointer而不是libunwind回溯栈,请确保在CXXFLAGS或CFLAGS中加上`-fno-omit-frame-pointer`,否则函数间的调用关系会丢失,最后产生的图片中都是彼此独立的函数方框。
2. 在COMAKE的CPPFLAGS中增加`-DBRPC_ENABLE_HEAP_PROFILER`
2. 定义宏BRPC_ENABLE_HEAP_PROFILER, 一般加入编译参数-DBRPC_ENABLE_HEAP_PROFILER。
3. 在shell中`export TCMALLOC_SAMPLE_PARAMETER=524288`。该变量指每分配这么多字节内存时做一次统计,默认为0,代表不开启内存统计。[官方文档](http://goog-perftools.sourceforge.net/doc/tcmalloc.html)建议设置为524288。这个变量也可在运行前临时设置,如`TCMALLOC_SAMPLE_PARAMETER=524288 ./server`。如果没有这个环境变量,可能会看到这样的结果:
......@@ -65,9 +65,9 @@ Adjusting heap profiles for 1-in-524288 sampling rate
Heap version 2
Total: 38.9 MB
35.8 92.0% 92.0% 35.8 92.0% ::cpp_alloc
2.1 5.4% 97.4% 2.1 5.4% base::FlatMap
0.5 1.3% 98.7% 0.5 1.3% base::IOBuf::append
0.5 1.3% 100.0% 0.5 1.3% base::IOBufAsZeroCopyOutputStream::Next
2.1 5.4% 97.4% 2.1 5.4% butil::FlatMap
0.5 1.3% 98.7% 0.5 1.3% butil::IOBuf::append
0.5 1.3% 100.0% 0.5 1.3% butil::IOBufAsZeroCopyOutputStream::Next
0.0 0.0% 100.0% 0.6 1.5% MallocExtension::GetHeapSample
0.0 0.0% 100.0% 0.5 1.3% ProfileHandler::Init
0.0 0.0% 100.0% 0.5 1.3% ProfileHandlerRegisterCallback
......@@ -81,9 +81,9 @@ Total: 38.9 MB
0.0 0.0% 100.0% 2.9 7.4% brpc::Socket::Write
0.0 0.0% 100.0% 3.8 9.7% brpc::Span::CreateServerSpan
0.0 0.0% 100.0% 1.4 3.5% brpc::SpanQueue::Push
0.0 0.0% 100.0% 1.9 4.8% base::ObjectPool
0.0 0.0% 100.0% 0.8 2.0% base::ResourcePool
0.0 0.0% 100.0% 1.0 2.6% base::iobuf::tls_block
0.0 0.0% 100.0% 1.9 4.8% butil::ObjectPool
0.0 0.0% 100.0% 0.8 2.0% butil::ResourcePool
0.0 0.0% 100.0% 1.0 2.6% butil::iobuf::tls_block
0.0 0.0% 100.0% 1.0 2.6% bthread::TimerThread::Bucket::schedule
0.0 0.0% 100.0% 1.6 4.1% bthread::get_stack
0.0 0.0% 100.0% 4.2 10.8% bthread_id_create
......
......@@ -27,11 +27,11 @@ 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,而不是要求连续内存。
`cntl.response_attachment()`是回复的body,类型也是butil::IOBuf。注意IOBuf转化为std::string(通过to_string()接口)是需要分配内存并拷贝所有内容的,如果关注性能,你的处理过程应该尽量直接支持IOBuf,而不是要求连续内存。
# POST
默认的HTTP Method为GET,如果需要做POST,则需要设置。待POST的数据应置入request_attachment(),它([base::IOBuf](http://icode.baidu.com/repo/baidu/opensource/baidu-rpc/files/master/blob/src/base/iobuf.h))可以直接append std::string或char*
默认的HTTP Method为GET,如果需要做POST,则需要设置。待POST的数据应置入request_attachment(),它([butil::IOBuf](http://icode.baidu.com/repo/baidu/opensource/baidu-rpc/files/master/blob/src/butil/iobuf.h))可以直接append std::string或char*
```c++
brpc::Controller cntl;
......@@ -41,13 +41,13 @@ cntl.request_attachment().append("{\"message\":\"hello world!\"}");
channel.CallMethod(NULL, &cntl, NULL, NULL, NULL/*done*/);
```
需要大量打印过程的body建议使用base::IOBufBuilder,它的用法和std::ostringstream是一样的。对于有大量对象要打印的场景,IOBufBuilder会简化代码,并且效率也更高。
需要大量打印过程的body建议使用butil::IOBufBuilder,它的用法和std::ostringstream是一样的。对于有大量对象要打印的场景,IOBufBuilder会简化代码,并且效率也更高。
```c++
brpc::Controller cntl;
cntl.http_request().uri() = "..."; // 设置为待访问的URL
cntl.http_request().set_method(brpc::HTTP_METHOD_POST);
base::IOBufBuilder os;
butil::IOBufBuilder os;
os << "A lot of printing" << printable_objects << ...;
os.move_to(cntl.request_attachment());
channel.CallMethod(NULL, &cntl, NULL, NULL, NULL/*done*/);
......@@ -120,13 +120,13 @@ cntl->http_request().set_content_type("text/plain");
```
访问body
```c++
base::IOBuf& buf = cntl->request_attachment();
butil::IOBuf& buf = cntl->request_attachment();
std::string str = cntl->request_attachment().to_string(); // 有拷贝
```
设置body
```c++
cntl->request_attachment().append("....");
base::IOBufBuilder os; os << "....";
butil::IOBufBuilder os; os << "....";
os.move_to(cntl->request_attachment());
```
......@@ -160,7 +160,7 @@ Notes on http header:
...
const std::string* encoding = cntl->http_response().GetHeader("Content-Encoding");
if (encoding != NULL && *encoding == "gzip") {
base::IOBuf uncompressed;
butil::IOBuf uncompressed;
if (!brpc::policy::GzipDecompress(cntl->response_attachment(), &uncompressed)) {
LOG(ERROR) << "Fail to un-gzip response body";
return;
......@@ -190,7 +190,7 @@ r33796后brpc client支持在读取完body前就结束RPC,让用户在RPC结
// 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;
virtual butil::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.
......@@ -198,7 +198,7 @@ r33796后brpc client支持在读取完body前就结束RPC,让用户在RPC结
// - 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;
virtual void OnEndOfMessage(const butil::Status& status) = 0;
};
```
OnReadOnePart在每读到一段数据时被调用,OnEndOfMessage在数据结束或连接断开时调用,实现前仔细阅读注释。
......
......@@ -36,7 +36,7 @@ public:
        cntl->http_response().set_content_type("text/plain");
       
        // 把请求的query-string和body打印出来,作为回复内容。
        base::IOBufBuilder os;
        butil::IOBufBuilder os;
        os << "queries:";
        for (brpc::URI::QueryIterator it = cntl->http_request().uri().QueryBegin();
                it != cntl->http_request().uri().QueryEnd(); ++it) {
......@@ -114,7 +114,7 @@ r32097后,brpc支持为service中的每个方法指定一个URL。接口如下
// PATHs是有效的HTTP路径, NAMEs是service中的方法名.
int AddService(google::protobuf::Service* service,
ServiceOwnership ownership,
base::StringPiece restful_mappings);
butil::StringPiece restful_mappings);
```
比如下面的QueueService包含多个http方法。
......@@ -281,7 +281,7 @@ http服务常对http body进行压缩,对于文本网页可以有效减少传
...
const std::string* encoding = cntl->http_request().GetHeader("Content-Encoding");
if (encoding != NULL && *encoding == "gzip") {
    base::IOBuf uncompressed;
    butil::IOBuf uncompressed;
    if (!brpc::policy::GzipDecompress(cntl->request_attachment(), &uncompressed)) {
        LOG(ERROR) << "Fail to un-gzip request body";
        return;
......@@ -295,7 +295,7 @@ if (encoding != NULL && *encoding == "gzip") {
# 开启HTTPS
要开启HTTPS,首先确保你的COMAKE/BCLOUD中依赖有最新的openssl库(openssl-1.0.2h)
要开启HTTPS,首先确保代码依赖了最新的openssl库
```python
CONFIGS('third-64/openssl@1.0.2.6123')
......
brpc使用[base::IOBuf](http://icode.baidu.com/repo/baidu/opensource/baidu-rpc/files/master/blob/src/base/iobuf.h)作为存储附件或http body的数据结构,它是一种非连续零拷贝缓冲,在其他项目中得到了验证并有出色的性能。IOBuf的接口和std::string类似,但不相同。
brpc使用[butil::IOBuf](http://icode.baidu.com/repo/baidu/opensource/baidu-rpc/files/master/blob/src/butil/iobuf.h)作为存储附件或http body的数据结构,它是一种非连续零拷贝缓冲,在其他项目中得到了验证并有出色的性能。IOBuf的接口和std::string类似,但不相同。
如果你之前使用Kylin中的BufHandle,你将更能感受到IOBuf的便利性:前者几乎没有实现完整,直接暴露了内部结构,用户得小心翼翼地处理引用计数,极易出错。BufHandle是很多bug的诱因。
......
......@@ -101,12 +101,12 @@ LoadBalancer是一个读远多于写的数据结构:大部分时候,所有
- 不同的读之间没有竞争,高度并发。
- 如果没有写,读总是能无竞争地获取和释放thread-local锁,一般小于25ns,对延时基本无影响。如果有写,由于其临界区极小(拿到立刻释放),读在大部分时候仍能快速地获得锁,少数时候释放锁时可能有唤醒写线程的代价。由于写本身就是少数情况,读整体上几乎不会碰到竞争锁。
完成这些功能的数据结构是[DoublyBufferedData<>](http://icode.baidu.com/repo/baidu/opensource/baidu-rpc/files/master/blob/src/base/containers/doubly_buffered_data.h),我们常简称为DBD。brpc中的所有load balancer都使用了这个数据结构,使不同线程在分流时几乎不会互斥。而其他rpc实现往往使用了全局锁,这使得它们无法写出复杂的分流算法:否则分流代码将会成为竞争热点。
完成这些功能的数据结构是[DoublyBufferedData<>](http://icode.baidu.com/repo/baidu/opensource/baidu-rpc/files/master/blob/src/butil/containers/doubly_buffered_data.h),我们常简称为DBD。brpc中的所有load balancer都使用了这个数据结构,使不同线程在分流时几乎不会互斥。而其他rpc实现往往使用了全局锁,这使得它们无法写出复杂的分流算法:否则分流代码将会成为竞争热点。
这个结构有广泛的应用场景:
- reload词典。大部分时候词典都是只读的,不同线程同时查询时不应查询。
- 可替换的全局callback。像base/logging.cpp支持配置全局LogSink以重定向日志,这个LogSink就是一个带状态的callback。如果只是简单的全局变量,在替换后我们无法直接删除LogSink,因为可能还有都写线程在用。用DBD可以解决这个问题。
- 可替换的全局callback。像butil/logging.cpp支持配置全局LogSink以重定向日志,这个LogSink就是一个带状态的callback。如果只是简单的全局变量,在替换后我们无法直接删除LogSink,因为可能还有都写线程在用。用DBD可以解决这个问题。
## weight tree
......
......@@ -5,7 +5,7 @@
在brpc中,[NamingService](http://icode.baidu.com/repo/baidu/opensource/baidu-rpc/files/master/blob/src/brpc/naming_service.h)用于获得服务名对应的所有节点。一个直观的做法是定期调用一个函数以获取最新的节点列表。但这会带来一定的延时(定期调用的周期一般在若干秒左右),作为通用接口不太合适。特别当名字服务提供事件通知时(比如zk),这个特性没有被利用。所以我们反转了控制权:不是我们调用用户函数,而是用户在获得列表后调用我们的接口,对应[NamingServiceActions](http://icode.baidu.com/repo/baidu/opensource/baidu-rpc/files/master/blob/src/brpc/naming_service.h)。当然我们还是得启动进行这一过程的函数,对应NamingService::RunNamingService。下面以三个实现解释这套方式:
- bns:没有事件通知,所以我们只能定期去获得最新列表,默认间隔是[5秒](http://brpc.baidu.com:8765/flags/ns_access_interval)。为了简化这类定期获取的逻辑,brpc提供了[PeriodicNamingService](http://icode.baidu.com/repo/baidu/opensource/baidu-rpc/files/master/blob/src/brpc/periodic_naming_service.h) 供用户继承,用户只需要实现单次如何获取(GetServers)。获取后调用NamingServiceActions::ResetServers告诉框架。框架会对列表去重,和之前的列表比较,通知对列表有兴趣的观察者(NamingServiceWatcher)。这套逻辑会运行在独立的bthread中,即NamingServiceThread。一个NamingServiceThread可能被多个Channel共享,通过intrusive_ptr管理ownership。
- file:列表即文件。合理的方式是在文件更新后重新读取。[该实现](http://icode.baidu.com/repo/baidu/opensource/baidu-rpc/files/master/blob/src/brpc/policy/file_naming_service.cpp)使用[FileWatcher](http://icode.baidu.com/repo/baidu/opensource/baidu-rpc/files/master/blob/src/base/files/file_watcher.h)关注文件的修改时间,当文件修改后,读取并调用NamingServiceActions::ResetServers告诉框架。
- file:列表即文件。合理的方式是在文件更新后重新读取。[该实现](http://icode.baidu.com/repo/baidu/opensource/baidu-rpc/files/master/blob/src/brpc/policy/file_naming_service.cpp)使用[FileWatcher](http://icode.baidu.com/repo/baidu/opensource/baidu-rpc/files/master/blob/src/butil/files/file_watcher.h)关注文件的修改时间,当文件修改后,读取并调用NamingServiceActions::ResetServers告诉框架。
- list:列表就在服务名里(逗号分隔)。在读取完一次并调用NamingServiceActions::ResetServers后就退出了,因为列表再不会改变了。
如果用户需要建立这些对象仍然是不够方便的,因为总是需要一些工厂代码根据配置项建立不同的对象,鉴于此,我们把工厂类做进了框架,并且是非常方便的形式:
......
......@@ -59,7 +59,7 @@ enum ProtocolType {
### parse
```c++
typedef ParseResult (*Parse)(base::IOBuf* source, Socket *socket, bool read_eof, const void *arg);
typedef ParseResult (*Parse)(butil::IOBuf* source, Socket *socket, bool read_eof, const void *arg);
```
用于把消息从source上切割下来,client端和server端使用同一个parse函数。返回的消息会被递给process_request(server端)或process_response(client端)。
......@@ -75,7 +75,7 @@ ParseResult可能是错误,也可能包含一个切割下来的message,可
### serialize_request
```c++
typedef bool (*SerializeRequest)(base::IOBuf* request_buf,
typedef bool (*SerializeRequest)(butil::IOBuf* request_buf,
Controller* cntl,
const google::protobuf::Message* request);
```
......@@ -83,11 +83,11 @@ typedef bool (*SerializeRequest)(base::IOBuf* request_buf,
### pack_request
```c++
typedef int (*PackRequest)(base::IOBuf* msg,
typedef int (*PackRequest)(butil::IOBuf* msg,
uint64_t correlation_id,
const google::protobuf::MethodDescriptor* method,
Controller* controller,
const base::IOBuf& request_buf,
const butil::IOBuf& request_buf,
const Authenticator* auth);
```
把request_buf打包入msg,每次向server发送消息前(包括重试)都会调用。当auth不为空时,需要打包认证信息。成功返回0,否则-1。
......@@ -116,9 +116,9 @@ typedef bool (*Verify)(const InputMessageBase* msg);
### parse_server_address
```c++
typedef bool (*ParseServerAddress)(base::EndPoint* out, const char* server_addr_and_port);
typedef bool (*ParseServerAddress)(butil::EndPoint* out, const char* server_addr_and_port);
```
把server_addr_and_port(Channel.Init的一个参数)转化为base::EndPoint,可选。一些协议对server地址的表达和理解可能是不同的。
把server_addr_and_port(Channel.Init的一个参数)转化为butil::EndPoint,可选。一些协议对server地址的表达和理解可能是不同的。
### get_method_name
```c++
......@@ -169,7 +169,7 @@ void ProcessXXXRequest(InputMessageBase* msg_base) {
```c++
void ProcessXXXRequest(InputMessageBase* msg_base) {
const int64_t start_parse_us = base::cpuwide_time_us();
const int64_t start_parse_us = butil::cpuwide_time_us();
- MostCommonMessage* msg = static_cast<MostCommonMessage*>(msg_base);
+ DestroyingPtr<MostCommonMessage> msg(static_cast<MostCommonMessage*>(msg_base));
+ SocketUniquePtr socket(msg->ReleaseSocket());
......@@ -189,7 +189,7 @@ void ProcessXXXRequest(InputMessageBase* msg_base) {
ProcessXXXResponse开头的修改一般是这样:
```c++
void ProcessRpcResponse(InputMessageBase* msg_base) {
const int64_t start_parse_us = base::cpuwide_time_us();
const int64_t start_parse_us = butil::cpuwide_time_us();
- MostCommonMessage* msg = static_cast<MostCommonMessage*>(msg_base);
- CheckEOFGuard eof_guard(msg->socket_id());
+ DestroyingPtr<MostCommonMessage> msg(static_cast<MostCommonMessage*>(msg_base));
......
......@@ -60,12 +60,10 @@ service EchoService {
};
```
## 在COMAKE中设置protoc和mcpack2pb的参数
## 设置protoc和mcpack2pb的参数
注意--mcpack_out要和--cpp_out一致,你可以先设成--mcpack_out=.,执行comake2或bcloud后看错误信息中的--cpp_out的值,再把--mcpack_out设成一样的。
BCLOUD中要把`/public/mcpack2pb/protoc-gen-mcpack`替换成`/public/mcpack2pb/protoc-gen-mcpack**.forbcloud**,并把ENV.WorkRoot()替换为WORKROOT的实际值。`
```pyton
PROTOC(ENV.WorkRoot()+"/third-64/protobuf/bin/protoc")
PROTOFLAGS("--plugin=protoc-gen-mcpack=" + ENV.WorkRoot() + "/public/mcpack2pb/protoc-gen-mcpack --mcpack_out=.")
......@@ -136,7 +134,7 @@ NsheadService的接口如下,基本上用户只需要实现`ProcessNsheadReque
// 代表一个nshead请求或回复。
struct NsheadMessage {
nshead_t head;
base::IOBuf body;
butil::IOBuf body;
};
// 实现这个类并复制给ServerOptions.nshead_service来让brpc处理nshead请求。
......
......@@ -108,7 +108,7 @@ CHECK_EQ(-10, response.reply(3).integer());
```c++
bool AddCommand(const char* fmt, ...);
bool AddCommandV(const char* fmt, va_list args);
bool AddCommandByComponents(const base::StringPiece* components, size_t n);
bool AddCommandByComponents(const butil::StringPiece* components, size_t n);
```
格式和hiredis基本兼容:即%b对应二进制数据(指针+length),其他和printf的参数类似。对一些细节做了改进:当某个字段包含空格时,使用单引号或双引号包围起来会被视作一个字段。比如AddCommand("Set 'a key with space' 'a value with space as well'")中的key是a key with space,value是a value with space as well。在hiredis中必须写成redisvCommand(..., "SET %s %s", "a key with space", "a value with space as well");
......
......@@ -52,7 +52,7 @@ brpc::SampleIterator it("./rpc_data/rpc_dump/echo_server");
for (SampleRequest* req = it->Next(); req != NULL; req = it->Next()) {
...
// req->meta的类型是brpc::RpcDumpMeta,定义在protocol/brpc/rpc_dump.proto
// req->request的类型是base::IOBuf,对应格式说明中的"serialized request"
// req->request的类型是butil::IOBuf,对应格式说明中的"serialized request"
// 使用结束后必须delete req。
}
```
......
......@@ -139,24 +139,24 @@ Service在插入[brpc::Server](http://icode.baidu.com/repo/baidu/opensource/baid
## 获取Client的地址和端口
controller->remote_side()可获得发送该请求的client地址和端口,类型是base::EndPoint。如果client是nginx,remote_side()是nginx的ip。要获取真实client的ip,可以在nginx里设置proxy_header ClientIp $remote_addr; 在rpc中通过controller->http_request().GetHeader("ClientIp")获得对应的值。
controller->remote_side()可获得发送该请求的client地址和端口,类型是butil::EndPoint。如果client是nginx,remote_side()是nginx的ip。要获取真实client的ip,可以在nginx里设置proxy_header ClientIp $remote_addr; 在rpc中通过controller->http_request().GetHeader("ClientIp")获得对应的值。
打印方式:
```c++
LOG(INFO) << "remote_side=" << cntl->remote_side();
printf("remote_side=%s\n", base::endpoint2str(cntl->remote_side()).c_str());
printf("remote_side=%s\n", butil::endpoint2str(cntl->remote_side()).c_str());
```
## 获取Server的地址和端口
r31384前server在接受连接时会把server端的端口打印到日志中,但没有接口获得。r31384后调用controller->local_side()获得,类型是base::EndPoint。
r31384前server在接受连接时会把server端的端口打印到日志中,但没有接口获得。r31384后调用controller->local_side()获得,类型是butil::EndPoint。
打印方式:
```c++
LOG(INFO) << "local_side=" << cntl->local_side();
printf("local_side=%s\n", base::endpoint2str(cntl->local_side()).c_str());
printf("local_side=%s\n", butil::endpoint2str(cntl->local_side()).c_str());
```
## 异步Service
......@@ -364,7 +364,7 @@ Server.set_version(...)可以为server设置一个名称+版本,可通过/vers
## 在每条日志后打印hostname
此功能只对[base/logging.h](http://icode.baidu.com/repo/baidu/opensource/baidu-rpc/files/master/blob/src/base/logging.h)中的日志宏有效。打开[-log_hostname](http://brpc.baidu.com:8765/flags/log_hostname)后每条日志后都会带本机名称,如果所有的日志需要汇总到一起进行分析,这个功能可以帮助你了解某条日志来自哪台机器。
此功能只对[butil/logging.h](http://icode.baidu.com/repo/baidu/opensource/baidu-rpc/files/master/blob/src/butil/logging.h)中的日志宏有效。打开[-log_hostname](http://brpc.baidu.com:8765/flags/log_hostname)后每条日志后都会带本机名称,如果所有的日志需要汇总到一起进行分析,这个功能可以帮助你了解某条日志来自哪台机器。
## 打印FATAL日志后退出程序
......@@ -374,7 +374,7 @@ Server.set_version(...)可以为server设置一个名称+版本,可通过/vers
## 最低日志级别
此功能只对[base/logging.h](http://icode.baidu.com/repo/baidu/opensource/baidu-rpc/files/master/blob/src/base/logging.h)中的日志宏有效。设置[-min_log_level](http://brpc.baidu.com:8765/flags/min_log_level)后只有**不低于**被设置日志级别的日志才会被打印,这个选项可以动态修改。设置值和日志级别的对应关系:0=INFO 1=NOTICE 2=WARNING 3=ERROR 4=FATAL
此功能只对[butil/logging.h](http://icode.baidu.com/repo/baidu/opensource/baidu-rpc/files/master/blob/src/butil/logging.h)中的日志宏有效。设置[-min_log_level](http://brpc.baidu.com:8765/flags/min_log_level)后只有**不低于**被设置日志级别的日志才会被打印,这个选项可以动态修改。设置值和日志级别的对应关系:0=INFO 1=NOTICE 2=WARNING 3=ERROR 4=FATAL
被拦住的日志产生的开销只是一次if判断,也不会评估参数(比如某个参数调用了函数,日志不打,这个函数就不会被调用),这和comlog是完全不同的。如果日志最终打印到comlog,那么还要经过comlog中的日志级别的过滤。
......@@ -431,7 +431,7 @@ public:                                    
    // pointer from `Controller'.                                                                                                                                                  
    // Returns 0 on success, error code otherwise                                                                                                                                  
    virtual int VerifyCredential(const std::string& auth_str,                                                                                                                      
                                 const base::EndPoint& client_addr,                                                                                                                
                                 const butil::EndPoint& client_addr,                                                                                                                
                                 AuthContext* out_ctx) const = 0;                                                                                                                                                                                                                                                                                                     
}; 
 
......@@ -918,4 +918,4 @@ process_io_write_second
# 附:Server端基本流程
![img](../images/server_side.png)
\ No newline at end of file
![img](../images/server_side.png)
......@@ -129,7 +129,7 @@ TRACEPRINTF主要适合若干次的函数调用,如果一个函数调用了很
仿照如下代码对foobar的运行时间进行监控。
```c++
#include <base/time.h>
#include <butil/time.h>
#include <bvar/bvar.h>
bvar::LatencyRecorder g_foobar_latency("foobar");
......@@ -137,7 +137,7 @@ bvar::LatencyRecorder g_foobar_latency("foobar");
...
void search() {
...
base::Timer tm;
butil::Timer tm;
tm.start();
foobar();
tm.stop();
......
......@@ -5,7 +5,7 @@ streaming_log - Print log to std::ostreams
# SYNOPSIS
```c++
#include <base/logging.h>
#include <butil/logging.h>
LOG(FATAL) << "Fatal error occurred! contexts=" << ...;
LOG(WARNING) << "Unusual thing happened ..." << ...;
......@@ -27,7 +27,7 @@ LOG_ONCE(WARNING) << "Logs that only prints once";
```c++
// logging默认重定向至comlog,要配置comlog的话,要额外include comlog_sink.h
#include <base/comlog_sink.h>
#include <butil/comlog_sink.h>
// 从./conf/log.conf读取comlog的配置。SetupFromConfig是我们提供的封装函数,不用像com_loadlog那样区分path和file。
if (logging::ComlogSink::GetInstance()->SetupFromConfig("conf/log.conf") != 0) {
......@@ -264,7 +264,7 @@ CHECK_GT(1, 2) << "1 can't be greater than 2";
```
FATAL: ... Check failed: 1 > 2 (1 vs 2). 1 can't be greater than 2
#0 0x000000afaa23 base::debug::StackTrace::StackTrace()
#0 0x000000afaa23 butil::debug::StackTrace::StackTrace()
#1 0x000000c29fec logging::LogStream::FlushWithoutReset()
#2 0x000000c2b8e6 logging::LogStream::Flush()
#3 0x000000c2bd63 logging::DestroyLogStream()
......@@ -319,6 +319,6 @@ TEST_F(StreamingLogTest, log_at) {
### ComlogSink
定义在base/comlog_sink.h中,把日志打印入comlog,主要用于线上系统,用法见[SYNOPSIS](#SYNOPSIS)一段。
定义在butil/comlog_sink.h中,把日志打印入comlog,主要用于线上系统,用法见[SYNOPSIS](#SYNOPSIS)一段。
使用ComlogSink的streaming log可以和com_writelog, ul_writelog混用。你并不需要把程序中所有日志都换成streaming log。
......@@ -78,7 +78,7 @@ int StreamAccept(StreamId* response_stream, Controller &cntl, const StreamOption
class StreamInputHandler {
public:
// 当接收到消息后被调用
virtual int on_received_messages(StreamId id, base::IOBuf *const messages[], size_t size) = 0;
virtual int on_received_messages(StreamId id, butil::IOBuf *const messages[], size_t size) = 0;
// 当Stream上长时间没有数据交互后被调用
virtual void on_idle_timeout(StreamId id) = 0;
......@@ -104,7 +104,7 @@ public:
// - EAGAIN: |stream_id| is created with positive |max_buf_size| and buf size
// which the remote side hasn't consumed yet excceeds the number.
// - EINVAL: |stream_id| is invalied or has been closed
int StreamWrite(StreamId stream_id, const base::IOBuf &message);
int StreamWrite(StreamId stream_id, const butil::IOBuf &message);
```
# 流控
......
......@@ -57,12 +57,10 @@ r31687后,brpc支持通过protobuf访问ubrpc,不需要baidu-rpc-ub,也不
};
```
2. COMAKEBCLOUD插入如下片段以使用代码生成插件。
2. 插入如下片段以使用代码生成插件。
注意--mcpack_out要和--cpp_out一致,你可以先设成--mcpack_out=.,执行comake2bcloud后看错误信息中的--cpp_out的值,再把--mcpack_out设成一样的。
BCLOUD中要把`/public/mcpack2pb/protoc-gen-mcpack`替换成`/public/mcpack2pb/protoc-gen-mcpack**.forbcloud**`,并把ENV.WorkRoot()替换为WORKROOT的实际值。
```python
PROTOC(ENV.WorkRoot()+"/third-64/protobuf/bin/protoc")
PROTOFLAGS("--plugin=protoc-gen-mcpack=" + ENV.WorkRoot() + "/public/mcpack2pb/protoc-gen-mcpack --mcpack_out=.")
......@@ -154,7 +152,7 @@ server端由public/ubrpc搭建,request/response使用idl文件描述字段,
**步骤:**
1. 依赖[public/baidu-rpc-ub](http://icode.baidu.com/repo/baidu/opensource/baidu-rpc/files/master/blob)模块,在COMAKE中增加依赖:`CONFIGS('public/baidu-rpc-ub@ci-base')。`这个模块是brpc的扩展,不需要的用户不会依赖idl/mcpack/compack等模块。baidu-rpc-ub只包含扩展代码,brpc中的新特性会自动体现在这个模块中。
1. 依赖public/baidu-rpc-ub模块,在COMAKE中增加依赖:`CONFIGS('public/baidu-rpc-ub@ci-base')。`这个模块是brpc的扩展,不需要的用户不会依赖idl/mcpack/compack等模块。baidu-rpc-ub只包含扩展代码,brpc中的新特性会自动体现在这个模块中。
2. 编写一个proto文件,其中定义了service,名字和idl中的相同,但请求类型必须是baidu.rpc.UBRequest,回复类型必须是baidu.rpc.UBResponse。这两个类型定义在brpc/ub.proto中,使用时得import
......
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