iobuf.md 2.85 KB
Newer Older
gejun's avatar
gejun committed
1
[English version](../en/iobuf.md)
2

gejun's avatar
gejun committed
3 4 5
brpc使用[butil::IOBuf](https://github.com/brpc/brpc/blob/master/src/butil/iobuf.h)作为一些协议中的附件或http body的数据结构,它是一种非连续零拷贝缓冲,在其他项目中得到了验证并有出色的性能。IOBuf的接口和std::string类似,但不相同。

如果你之前使用Kylin中的BufHandle,你将更能感受到IOBuf的便利性:前者几乎没有实现完整,直接暴露了内部结构,用户得小心翼翼地处理引用计数,极易出错。
6 7 8 9 10 11 12 13

# IOBuf能做的:

- 默认构造不分配内存。
- 可以拷贝,修改拷贝不影响原IOBuf。拷贝的是IOBuf的管理结构而不是数据。
- 可以append另一个IOBuf,不拷贝数据。
- 可以append字符串,拷贝数据。
- 可以从fd读取,可以写入fd。
gejun's avatar
gejun committed
14
- 可以解析或序列化为protobuf messages.
15 16 17 18 19 20 21 22
- IOBufBuilder可以把IOBuf当std::ostream用。

# IOBuf不能做的:

- 程序内的通用存储结构。IOBuf应保持较短的生命周期,以避免一个IOBuf锁定了多个block (8K each)。

# 切割

gejun's avatar
gejun committed
23
从source_buf头部切下16字节放入dest_buf:
24 25

```c++
gejun's avatar
gejun committed
26
source_buf.cut(&dest_buf, 16); // 当source_buf不足16字节时,切掉所有字节。
27 28
```

gejun's avatar
gejun committed
29
从source_buf头部弹掉16字节:
30 31 32 33 34 35 36

```c++
source_buf.pop_front(16); // 当source_buf不足16字节时,清空它
```

# 拼接

gejun's avatar
gejun committed
37
在尾部加入另一个IOBuf:
38 39 40 41 42

```c++
buf.append(another_buf);  // no data copy
```

gejun's avatar
gejun committed
43
在尾部加入std::string
44 45 46 47 48 49 50

```c++
buf.append(str);  // copy data of str into buf
```

# 解析

gejun's avatar
gejun committed
51
解析IOBuf为protobuf message
52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75

```c++
IOBufAsZeroCopyInputStream wrapper(&iobuf);
pb_message.ParseFromZeroCopyStream(&wrapper);
```

解析IOBuf为自定义结构

```c++
IOBufAsZeroCopyInputStream wrapper(&iobuf);
CodedInputStream coded_stream(&wrapper);
coded_stream.ReadLittleEndian32(&value);
...
```

# 序列化

protobuf序列化为IOBuf

```c++
IOBufAsZeroCopyOutputStream wrapper(&iobuf);
pb_message.SerializeToZeroCopyStream(&wrapper);
```

gejun's avatar
gejun committed
76
用可打印数据创建IOBuf
77 78 79 80 81 82 83 84 85

```c++
IOBufBuilder os;
os << "anything can be sent to std::ostream";
os.buf();  // IOBuf
```

# 打印

gejun's avatar
gejun committed
86 87
可直接打印至std::ostream. 注意这个例子中的iobuf必需只包含可打印字符。

88
```c++
gejun's avatar
gejun committed
89 90 91 92
std::cout << iobuf << std::endl;
// or
std::string str = iobuf.to_string(); // 注意: 会分配内存
printf("%s\n", str.c_str());
93 94 95 96
```

# 性能

gejun's avatar
gejun committed
97
IOBuf有不错的综合性能:
98

gejun's avatar
gejun committed
99 100 101 102 103
| 动作                                       | 吞吐          | QPS     |
| ---------------------------------------- | ----------- | ------- |
| 文件读入->切割12+16字节->拷贝->合并到另一个缓冲->写出到/dev/null | 240.423MB/s | 8586535 |
| 文件读入->切割12+128字节->拷贝->合并到另一个缓冲->写出到/dev/null | 790.022MB/s | 5643014 |
| 文件读入->切割12+1024字节->拷贝->合并到另一个缓冲->写出到/dev/null | 1519.99MB/s | 1467171 |