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.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Thanks, this makes sense. The implementation depends on FlatBuffers builder internals, so accepting an arbitrary installed FlatBuffers version is too loose. I will add an explicit compile-time version guard in the public FlatBuffers message header and document the supported/tested version range in both docs. The goal is to fail early with a clear diagnostic instead of relying on an internal-layout compile error or allocator abort.

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
111 changes: 111 additions & 0 deletions docs/cn/flatbuffers.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,111 @@
# 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`。
服务代码生成和进程内分派测试不涉及网络 RPC,也不是性能测试。

## 构建

运行库只需要 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`。其他测试依赖与 bRPC 原有配置相同。

Make 使用 `config_brpc.sh --with-flatbuffers`,测试时可通过
`FLATC=/path/to/flatc` 指定生成器。Bazel 使用
`--define=BRPC_WITH_FLATBUFFERS=true`。

运行库使用了 FlatBuffers builder 的内部结构,目前只支持 25.2.10,其他版本会在
编译期报错。Bzlmod 和 WORKSPACE 使用同一个源码归档,并用校验和锁定内容。
未采用 `bazel_dep`,是为了避免引入 gRPC、多语言工具以及与 bRPC 冲突的 BoringSSL
依赖。

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

旧版 flatc 2.0.x 可能生成未全限定名称。业务 schema 不要使用 `brpc` 命名空间,也不要依赖 include 顺序。

## 消息构造和所有权

先用上游 `flatc --cpp` 生成 `*_generated.h`,再将
`brpc::flatbuffers::MessageBuilder` 传给生成的 `Create...` 函数。
调用 `Finish(root)` 后,通过 `ReleaseMessage()` 取得消息。

* `ReleaseMessage()` 不复制 payload。返回的 Message 是 move-only 对象,持有 IOBuf block 引用;builder 复用或析构后,消息仍然有效。
* Message 和 builder 被移动后,源对象仍可复用。移动启用了 shared string 的 builder 会清空去重缓存,但已生成的 offset 不受影响。
* `Message::CopyFrom` 和 `MergeFrom` 共享同一个引用计数 IOBuf block,不复制
payload 或 metadata。通过任一别名修改数据,其他别名也会看到变化。有并发读者时,
写操作必须由外部同步保护。
* 从普通 `::flatbuffers::FlatBufferBuilder` 导入时,会复制 payload 和 scratch,
保留未完成 table 的状态。旧缓冲区由原分配器释放;若源 builder 拥有该分配器,
导入后也会销毁它。导入过程不猜测应使用 `free` 还是 `delete[]`,也不要求 payload
前有预留空间。
* 只使用 MessageBuilder 自身提供的 move、swap 和 release 操作。不要通过基类
cast 转移对象,也不要调用继承来的 raw-buffer release;这类 detached buffer
会保留成员 allocator 的地址。
* payload 前预留 64 字节并初始化为零。`reduce_meta_size_and_get_buf` 可以缩短
这段空间;扩大时返回失败,原对象保持不变。缩短 metadata 不会移动或改写 payload。
* 序列化 const Message 时,输出 IOBuf 会继续引用原存储。它不是 copy-on-write;仍有读者或序列化结果存活时,不要修改 payload 或 metadata。
* 分配长度在转成 SingleIOBuf 的 uint32_t 长度前会检查。SlabAllocator/builder
的分配失败会终止进程,release build 也一样,不受 `crash_on_fatal_log` 影响。
这是因为 FlatBuffers 的 `vector_downward` 无法在分配器返回 null 后继续工作。
解析时复制数据所需的内存若分配失败,则返回 false,保留原 Message。

`ParseFbFromIOBUF` 负责检查长度和 framing,解析后的 Message 持有自己的存储引用,
但该接口不负责限制接收消息大小。若 `msg_size` 来自不可信对端,调用方必须先按
自己的 max-message-size 策略检查,再调用解析接口。分片或未对齐的数据会在
schema 校验前复制到新存储,因此大小限制必须放在解析之前。

连续输入的 payload 地址按 64 字节对齐时,解析可以共享原存储。MessageBuilder
的分配按 64 字节对齐,但最终 payload 只保证满足 schema 的对齐要求,所以本地
构造的消息未必都能在接收侧零拷贝。当前不支持超过 64 字节的对齐要求。

**Framing 检查不等于 schema 校验。** 读取不可信数据前,先调用
`msg.Verify<YourRoot>()`,成功后再使用 `GetRoot<YourRoot>()` 或
`GetMutableRoot<YourRoot>()`。合法消息中的可选 string/vector 仍可能为 null。
Framing 检查失败不会修改原 Message。

## Service ID 和代码生成

`BrpcDescriptorTable` 保存 namespace、service 名称、按空白分隔的方法名和显式
方法 ID。ID 必须是唯一的非负 int32。手写 descriptor 可以传空 ID 列表,此时按
声明顺序分配 ordinal ID;生成服务必须显式声明 ID:

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

* `descriptor.method(position)` 按声明顺序取方法。
* `method.index()` 是稳定的 wire ID,不是数组下标。
* 稀疏 ID 必须通过 `descriptor.FindMethodByIndex(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 会验证请求,拒绝未知、不属于当前 service 或尚未实现的方法。
失败时,它会执行非空 completion callback;成功分派后,completion 由业务实现负责,
并且必须恰好执行一次。
Loading
Loading