Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
27 changes: 26 additions & 1 deletion BUILD.bazel
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,12 @@ licenses(["notice"]) # Apache v2

exports_files(["LICENSE"])

config_setting(
name = "brpc_with_flatbuffers",
define_values = {"BRPC_WITH_FLATBUFFERS": "true"},
visibility = ["//visibility:public"],
)

COPTS = [
"-fno-omit-frame-pointer",
] + select({
Expand All @@ -45,6 +51,9 @@ DEFINES = [
}) + select({
"//bazel/config:brpc_with_thrift": ["ENABLE_THRIFT_FRAMED_PROTOCOL=1"],
"//conditions:default": [],
}) + select({
":brpc_with_flatbuffers": ["BRPC_WITH_FLATBUFFERS=1"],
"//conditions:default": ["BRPC_WITH_FLATBUFFERS=0"],
}) + select({
"//bazel/config:brpc_with_thrift_legacy_version": [],
"//conditions:default": ["THRIFT_STDCXX=std"],
Expand Down Expand Up @@ -125,6 +134,14 @@ genrule(
"//conditions:default": "0",
}) +
"""
#ifdef BRPC_WITH_FLATBUFFERS
#undef BRPC_WITH_FLATBUFFERS
#endif
#define BRPC_WITH_FLATBUFFERS """ + select({
":brpc_with_flatbuffers": "1",
"//conditions:default": "0",
}) +
"""
#ifdef BUTIL_USE_CPU_FREQUENCY
#undef BUTIL_USE_CPU_FREQUENCY
#endif
Expand Down Expand Up @@ -539,6 +556,8 @@ brpc_proto_library(
visibility = ["//visibility:public"],
)

FLATBUFFERS_SRC_PATTERNS = ["src/brpc/flatbuffers/*.cpp"]

URMA_SRC_PATTERNS = [
"src/brpc/urma/*.cpp",
"src/brpc/urma/**/*.cpp",
Expand All @@ -562,7 +581,7 @@ BRPC_BASE_SRCS = glob(
"src/brpc/policy/thrift_protocol.cpp",
"src/brpc/event_dispatcher_epoll.cpp",
"src/brpc/event_dispatcher_kqueue.cpp",
] + URMA_SRC_PATTERNS,
] + URMA_SRC_PATTERNS + FLATBUFFERS_SRC_PATTERNS,
)

cc_library(
Expand All @@ -573,6 +592,9 @@ cc_library(
"src/brpc/**/thrift*.cpp",
]),
"//conditions:default": [],
}) + select({
":brpc_with_flatbuffers": glob(FLATBUFFERS_SRC_PATTERNS),
"//conditions:default": [],
}) + select({
"//bazel/config:brpc_with_urma_use_real": URMA_SRCS,
"//bazel/config:brpc_with_urma": URMA_SRCS + [
Expand Down Expand Up @@ -612,6 +634,9 @@ cc_library(
"@org_apache_thrift//:thrift",
],
"//conditions:default": [],
}) + select({
":brpc_with_flatbuffers": ["@com_github_google_flatbuffers//:runtime_cc"],
"//conditions:default": [],
}),
)

Expand Down
16 changes: 16 additions & 0 deletions CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ option(WITH_MESALINK "With MesaLink" OFF)
option(WITH_BORINGSSL "With BoringSSL" OFF)
option(WITH_DEBUG_SYMBOLS "With debug symbols" ON)
option(WITH_THRIFT "With thrift framed protocol supported" OFF)
option(WITH_FLATBUFFERS "With FlatBuffers message support (headers only)" OFF)
option(WITH_BTHREAD_TRACER "With bthread tracer supported" OFF)
option(WITH_SNAPPY "With snappy" OFF)
option(WITH_RDMA "With RDMA" OFF)
Expand Down Expand Up @@ -82,6 +83,17 @@ if(WITH_GLOG)
set(BRPC_WITH_GLOG 1)
endif()

set(WITH_FLATBUFFERS_VAL "0")
if(WITH_FLATBUFFERS)
find_path(FLATBUFFERS_INCLUDE_DIR NAMES flatbuffers/flatbuffers.h)

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[replied by brpc-oncall robot] WITH_FLATBUFFERS is validated only by the existence of flatbuffers/flatbuffers.h, but the runtime does not use FlatBuffers purely through its public API: src/brpc/flatbuffers/message.cpp reaches into builder internals (buf_, buf_.swap_allocator(), scratch_push_small(), string_pool, minalign_), and SlabAllocator asserts on the exact allocate()/reallocate_downward() size bookkeeping (only one live allocation, old_size == _capacity). Bazel pins 25.2.10 while CMake and config_brpc.sh accept any installed version, so with a different FlatBuffers an unsupported version fails deep in compilation or aborts inside the allocator. Please add a compile-time guard in src/brpc/flatbuffers/message.h (e.g. #if !defined(FLATBUFFERS_VERSION_MAJOR) || FLATBUFFERS_VERSION_MAJOR < X -> #error) and document the supported range, so users get a clear diagnostic instead of an obscure failure.

if(NOT FLATBUFFERS_INCLUDE_DIR)
message(FATAL_ERROR
"WITH_FLATBUFFERS requires FlatBuffers headers; set FLATBUFFERS_INCLUDE_DIR.")
endif()
set(WITH_FLATBUFFERS_VAL "1")
list(APPEND BRPC_COMMON_INCLUDE_DIRS ${FLATBUFFERS_INCLUDE_DIR})
endif()

set(WITH_CPU_FREQUENCY_VAL "0")
if(WITH_CPU_FREQUENCY)
set(WITH_CPU_FREQUENCY_VAL "1")
Expand Down Expand Up @@ -177,6 +189,7 @@ endif()

list(APPEND BRPC_COMMON_DEFINITIONS
BRPC_WITH_GLOG=${WITH_GLOG_VAL}
BRPC_WITH_FLATBUFFERS=${WITH_FLATBUFFERS_VAL}
BRPC_WITH_RDMA=${WITH_RDMA_VAL}
BRPC_WITH_URMA=${WITH_URMA_VAL}
BRPC_WITH_UBRING=${WITH_UBRING_VAL}
Expand Down Expand Up @@ -670,6 +683,9 @@ file(GLOB_RECURSE BTHREAD_SOURCES CONFIGURE_DEPENDS "${PROJECT_SOURCE_DIR}/src/b
file(GLOB_RECURSE JSON2PB_SOURCES CONFIGURE_DEPENDS "${PROJECT_SOURCE_DIR}/src/json2pb/*.cpp")
file(GLOB_RECURSE BRPC_SOURCES CONFIGURE_DEPENDS "${PROJECT_SOURCE_DIR}/src/brpc/*.cpp")
file(GLOB_RECURSE THRIFT_SOURCES CONFIGURE_DEPENDS "${PROJECT_SOURCE_DIR}/src/brpc/thrift*.cpp")
if(NOT WITH_FLATBUFFERS)
list(FILTER BRPC_SOURCES EXCLUDE REGEX "/brpc/flatbuffers/.*\\.cpp$")
endif()
file(GLOB_RECURSE EXCLUDE_SOURCES CONFIGURE_DEPENDS "${PROJECT_SOURCE_DIR}/src/brpc/event_dispatcher_*.cpp")

# When building with the real liburma, exclude the link-time mock so its urma_*
Expand Down
16 changes: 16 additions & 0 deletions MODULE.bazel
Original file line number Diff line number Diff line change
Expand Up @@ -79,3 +79,19 @@ git_repository(
remote = 'https://atomgit.com/openeuler/umdk.git',
commit = '564ee727a55523d4351a8fb3c94292b388ebb924', # v26.06.0_CAM
)

# Do not use `bazel_dep(name = 'flatbuffers', version = '25.2.10')` here.
# The BCR module pulls gRPC and JS/Go/Swift rule dependencies for FlatBuffers'
# full upstream build. bRPC only needs runtime_cc and flatc, and gRPC would
# otherwise introduce another BoringSSL version even when BRPC_WITH_FLATBUFFERS
# is false. Keep this archive and checksum in sync with WORKSPACE.
flatbuffers_http_archive = use_repo_rule(
'@bazel_tools//tools/build_defs/repo:http.bzl',
'http_archive',
)
flatbuffers_http_archive(
name = 'com_github_google_flatbuffers',
sha256 = 'b9c2df49707c57a48fc0923d52b8c73beb72d675f9d44b2211e4569be40a7421',
strip_prefix = 'flatbuffers-25.2.10',
urls = ['https://github.com/google/flatbuffers/archive/refs/tags/v25.2.10.tar.gz'],
)
3 changes: 3 additions & 0 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -204,6 +204,9 @@ JSON2PB_SOURCES = $(foreach d,$(JSON2PB_DIRS),$(wildcard $(addprefix $(d)/*,$(SR
JSON2PB_OBJS = $(addsuffix .o, $(basename $(JSON2PB_SOURCES)))

BRPC_DIRS = src/brpc src/brpc/details src/brpc/builtin src/brpc/policy src/brpc/policy/mysql src/brpc/rdma
ifeq ($(WITH_FLATBUFFERS),1)
BRPC_DIRS += src/brpc/flatbuffers
endif
ifeq ($(WITH_URMA),1)
BRPC_DIRS += src/brpc/urma
endif
Expand Down
8 changes: 8 additions & 0 deletions WORKSPACE
Original file line number Diff line number Diff line change
Expand Up @@ -150,6 +150,14 @@ http_archive(
urls = ["https://github.com/google/crc32c/archive/1.1.2.tar.gz"],
)

# Optional FlatBuffers support uses runtime_cc; keep this version in sync with MODULE.bazel.
http_archive(
name = "com_github_google_flatbuffers",
integrity = "sha256-ucLfSXB8V6SPwJI9UrjHO+ty1nX51EsiEeRWm+QKdCE=",
strip_prefix = "flatbuffers-25.2.10",
urls = ["https://github.com/google/flatbuffers/archive/refs/tags/v25.2.10.tar.gz"],
)

http_archive(
name = "com_github_google_glog", # 2021-05-07T23:06:39Z
patch_args = ["-p1"],
Expand Down
5 changes: 5 additions & 0 deletions config.h.in
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,11 @@
#endif
#cmakedefine BRPC_WITH_GLOG @WITH_GLOG_VAL@

#ifdef BRPC_WITH_FLATBUFFERS
#undef BRPC_WITH_FLATBUFFERS
#endif
#define BRPC_WITH_FLATBUFFERS @WITH_FLATBUFFERS_VAL@

#ifdef BUTIL_USE_CPU_FREQUENCY
#undef BUTIL_USE_CPU_FREQUENCY
#endif
Expand Down
19 changes: 17 additions & 2 deletions config_brpc.sh
Original file line number Diff line number Diff line change
Expand Up @@ -54,9 +54,10 @@ else
LDD=ldd
fi

TEMP=`getopt -o v: --long headers:,libs:,cc:,cxx:,with-glog,with-thrift,with-rdma,with-urma,with-urma-mock,without-urma-mock,with-mesalink,with-bthread-tracer,with-debug-bthread-sche-safety,with-debug-lock,with-asan,with-riscv-zvbc,with-riscv-zbc,with-cpu-frequency,nodebugsymbols,werror -n 'config_brpc' -- "$@"`
TEMP=`getopt -o v: --long headers:,libs:,cc:,cxx:,with-glog,with-thrift,with-flatbuffers,with-rdma,with-urma,with-urma-mock,without-urma-mock,with-mesalink,with-bthread-tracer,with-debug-bthread-sche-safety,with-debug-lock,with-asan,with-riscv-zvbc,with-riscv-zbc,with-cpu-frequency,nodebugsymbols,werror -n 'config_brpc' -- "$@"`
WITH_GLOG=0
WITH_THRIFT=0
WITH_FLATBUFFERS=0
WITH_RDMA=0
WITH_URMA=0
URMA_MOCK_MODE=auto
Expand Down Expand Up @@ -91,6 +92,7 @@ while true; do
--cxx ) CXX=$2; shift 2 ;;
--with-glog ) WITH_GLOG=1; shift 1 ;;
--with-thrift) WITH_THRIFT=1; shift 1 ;;
--with-flatbuffers) WITH_FLATBUFFERS=1; shift 1 ;;
--with-rdma) WITH_RDMA=1; shift 1 ;;
--with-urma) WITH_URMA=1; shift 1 ;;
--with-urma-mock) URMA_MOCK_MODE=on; shift 1 ;;
Expand Down Expand Up @@ -479,14 +481,15 @@ append_to_output "HDRS=$($ECHO $HDRS)"
append_to_output "LIBS=$($ECHO $LIBS)"
append_to_output "PROTOC=$PROTOC"
append_to_output "PROTOBUF_HDR=$PROTOBUF_HDR"
append_to_output "WITH_FLATBUFFERS=$WITH_FLATBUFFERS"
append_to_output "CC=$CC"
append_to_output "CXX=$CXX"
append_to_output "GCC_VERSION=$GCC_VERSION"
append_to_output "STATIC_LINKINGS=$STATIC_LINKINGS"
append_to_output "DYNAMIC_LINKINGS=$DYNAMIC_LINKINGS"

# CPP means C PreProcessing, not C PlusPlus
CPPFLAGS="${CPPFLAGS} -DBRPC_WITH_GLOG=$WITH_GLOG -DBRPC_DEBUG_BTHREAD_SCHE_SAFETY=$BRPC_DEBUG_BTHREAD_SCHE_SAFETY -DBRPC_DEBUG_LOCK=$BRPC_DEBUG_LOCK -DBUTIL_USE_CPU_FREQUENCY=$WITH_CPU_FREQUENCY"
CPPFLAGS="${CPPFLAGS} -DBRPC_WITH_GLOG=$WITH_GLOG -DBRPC_WITH_FLATBUFFERS=$WITH_FLATBUFFERS -DBRPC_DEBUG_BTHREAD_SCHE_SAFETY=$BRPC_DEBUG_BTHREAD_SCHE_SAFETY -DBRPC_DEBUG_LOCK=$BRPC_DEBUG_LOCK -DBUTIL_USE_CPU_FREQUENCY=$WITH_CPU_FREQUENCY"

# Avoid over-optimizations of TLS variables by GCC>=4.8
# See: https://github.com/apache/brpc/issues/1693
Expand All @@ -506,6 +509,12 @@ if [ "$SYSTEM" = "Darwin" ]; then
fi
fi

if [ $WITH_FLATBUFFERS != 0 ]; then
FLATBUFFERS_HDR=$(find_dir_of_header_or_die flatbuffers/flatbuffers.h) || exit 1
append_to_output_headers "$FLATBUFFERS_HDR"
print_success "Found FlatBuffers headers: $FLATBUFFERS_HDR"
fi

if [ $WITH_THRIFT != 0 ]; then
THRIFT_LIB=$(find_dir_of_lib_or_die thriftnb)
THRIFT_HDR=$(find_dir_of_header_or_die thrift/Thrift.h)
Expand Down Expand Up @@ -692,6 +701,11 @@ cat << EOF > src/butil/config.h
#endif
#define BRPC_WITH_GLOG $WITH_GLOG

#ifdef BRPC_WITH_FLATBUFFERS
#undef BRPC_WITH_FLATBUFFERS
#endif
#define BRPC_WITH_FLATBUFFERS $WITH_FLATBUFFERS

#ifdef BUTIL_USE_CPU_FREQUENCY
#undef BUTIL_USE_CPU_FREQUENCY
#endif
Expand All @@ -714,6 +728,7 @@ print_info "C++ std: $CXXFLAGS"
print_info "System: $SYSTEM"
if [ $WITH_GLOG -ne 0 ]; then print_info "With glog: yes"; fi
if [ $WITH_THRIFT -ne 0 ]; then print_info "With thrift: yes"; fi
if [ $WITH_FLATBUFFERS -ne 0 ]; then print_info "With FlatBuffers: yes (headers only)"; fi
if [ $WITH_RDMA -ne 0 ]; then print_info "With RDMA: yes"; fi
if [ $WITH_URMA -ne 0 ]; then print_info "With URMA: yes"; fi
if [ $WITH_MESALINK -ne 0 ]; then print_info "With MesaLink: yes"; fi
Expand Down
108 changes: 108 additions & 0 deletions docs/cn/flatbuffers.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,108 @@
# FlatBuffers 消息

[English version](../en/flatbuffers.md)

bRPC 提供可选的、基于 IOBuf 的 FlatBuffers 消息、构造器和服务描述符。
消息构造方案基于 [apache/brpc#3196](https://github.com/apache/brpc/pull/3196)。

该组件不会注册 `fb_rpc` 传输协议,也不会向 `brpc::Channel` 和 `brpc::Server`
增加 FlatBuffers 集成。服务代码生成和进程内分派测试不是网络 RPC,也不是性能 benchmark。

## 构建

FlatBuffers 支持默认关闭。消息运行库只需要 FlatBuffers 头文件,不链接
FlatBuffers 库。测试需要与头文件版本匹配的 `flatc`。请保留上游生成代码中的
版本断言;版本不一致时应重新生成头文件,而不是削弱断言。

例如,GoogleTest 源码安装在 `/usr/src/googletest` 时:

```sh
cmake -S . -B build -DWITH_FLATBUFFERS=ON -DBUILD_UNIT_TESTS=ON \
-DBUILD_BRPC_TOOLS=OFF -DDOWNLOAD_GTEST=OFF \
-DBRPC_SYSTEM_GTEST_SOURCE_DIR=/usr/src/googletest
cmake --build build --target brpc_flatbuffers_unittest -j6
ctest --test-dir build -R '^brpc_flatbuffers_unittest$' --output-on-failure
```

其他安装方式可按需设置 `FLATBUFFERS_INCLUDE_DIR`、
`FLATBUFFERS_FLATC_EXECUTABLE` 和 `BRPC_SYSTEM_GTEST_SOURCE_DIR`。
项目原有测试依赖仍然适用。

Make 可在 `config_brpc.sh` 中传入 `--with-flatbuffers`;测试可通过
`FLATC=/path/to/flatc` 指定官方生成器。Bazel 可传入
`--define=BRPC_WITH_FLATBUFFERS=true`,并使用匹配的 FlatBuffers 25.2.10
运行时和编译器依赖。Bzlmod 导入与 WORKSPACE 相同的 checksum 固定归档:
`runtime_cc` 和 `flatc` 不需要 FlatBuffers 的外部 gRPC 模块,否则即使该功能关闭,
也可能与 bRPC 固定的 BoringSSL 版本冲突。

公共头文件位于 `brpc/flatbuffers/`,命名空间为 `brpc::flatbuffers`。
构造消息包含 `message.h`,使用服务描述符和接口包含 `service.h`。
`butil/config.h` 中的 `BRPC_WITH_FLATBUFFERS` 始终为 0 或 1;应用应使用
`#if` 判断,而不是 `#ifdef`。

Flatc 2.0.x 会生成未全限定的 `flatbuffers::` 名称。业务 schema 应使用
`brpc` 之外的 namespace(例如 `myapp.rpc`),避免被 `brpc::flatbuffers`
遮蔽;不要依赖 include 顺序。Flatc 25.2.10 会生成全限定名称。

## 消息构造和所有权

使用上游 `flatc --cpp` 生成 schema 对应的 `*_generated.h`。将
`brpc::flatbuffers::MessageBuilder` 传给生成的 `Create...` 函数,调用
`Finish(root)`,最后调用 `ReleaseMessage()`。

* `ReleaseMessage()` 不拷贝 payload 字节。返回的 move-only Message 拥有 IOBuf
block 引用,并且在 builder 复用或析构后仍然有效。
* Message 和 builder 移动后,源对象仍可复用。移动带 shared string 的 builder 会丢弃
其可选去重缓存;已经生成的 offset 仍有效。
* 导入普通 `::flatbuffers::FlatBufferBuilder` 会复制其 payload 和 scratch,并保留
未完成 table 的状态。原始 allocator 负责释放原有存储,包括其拥有的自定义 allocator。
实现不会猜测该用 `free` 还是 `delete[]`,也不会假设 payload 前存在额外空间。
* 使用 MessageBuilder 自身的 move、swap 和 release 操作。不要通过基类 cast 转移它,
也不要使用继承来的 raw-buffer release 操作:原始 FlatBuffers detached buffer 会保留
指向成员 allocator 的地址。
* released payload 前有 64 字节零初始化空间。可用 `reduce_meta_size_and_get_buf`
缩短这段空间;增长会被拒绝且不修改对象。缩短 metadata 不会改变 payload 地址或字节。
* 序列化接受 const Message,并在输出 IOBuf 中保留其存储引用。该 buffer 是共享的,
不是 copy-on-write:当其他读者或已序列化 buffer 仍在使用时,不要修改 payload/metadata。
* 分配大小在收窄为 SingleIOBuf 的 uint32_t 长度前会先检查。分配失败在 release build
中同样 fatal,不受 bRPC `crash_on_fatal_log` 设置影响。这些路径会显式 abort,
而不是依赖 `CHECK`/`LOG(FATAL)`。上游 `vector_downward` 无法在 null allocation 后安全继续。

`ParseFbFromIOBUF` 检查长度和 framing,并保留独立所有权。当 payload 地址 64 字节对齐时,
它会共享连续输入;输入分片或对齐不足时会复制到对齐存储。builder 分配为 64 字节对齐,
但最终 payload 只需满足 schema 要求的对齐,因此并非所有本地消息都适合接收侧零拷贝。
不支持超过 64 字节的对齐要求。

**Framing 不是 schema 验证。** 对不可信对端收到的数据,应先调用
`msg.Verify<YourRoot>()`,再调用 `GetRoot<YourRoot>()` 或
`GetMutableRoot<YourRoot>()`。合法消息中的可选 FlatBuffers string/vector 仍可能为 null。
framing 检查失败会保持原消息不变。

## Service ID 和代码生成

`BrpcDescriptorTable` 包含 namespace、service 名称、以空白分隔的方法名,以及显式方法 ID。
ID 必须是唯一的非负 int32。空 ID 列表会为手写 descriptor 分配 ordinal ID;生成服务必须
使用显式 ID:

```fbs
rpc_service BenchmarkService {
First(Request):Response (id: 2);
Second(Request):Response (id: 5);
}
```

* `descriptor.method(position)` 按声明顺序枚举方法。
* `method.index()` 是稳定的 wire ID,不是数组下标。
* `descriptor.FindMethodByIndex(id)` 用于查找稀疏 wire ID。传输层必须用该查找,不能用
wire ID 直接索引稠密数组。
* 不要把已删除的方法 ID 复用于另一个方法。删除或重排声明不应改变仍存在方法的显式 ID。
* namespace `a.b` 和 `a.b.` 会规范化为相同 service 名称;空 namespace 表示全局作用域。
方法全名包含 service。service hash 使用规范化后的全名和 MurmurHash3 seed 1。
当这些 ID 被持久化或在线路上传输时,应保持 service 名称稳定。
* Descriptor 成功初始化后不能重新初始化。它们通过 RAII 拥有 method;生成的 accessor
使用函数局部静态初始化以保证线程安全。

`tools/flatbuffers/` 中的配套生成器使用上游 parser 生成服务绑定。它独立构建;只有该
可选工具需要 `libflatbuffers`。命令和限制见其 [README](../../tools/flatbuffers/README.md)。
生成的 dispatch 会校验请求、拒绝未知或外来方法,并在失败时执行非空 completion callback,
包括未实现方法。成功实现拥有 completion,并且必须恰好执行一次回调。
Loading
Loading