Skip to content

Commit 4d56434

Browse files
committed
Bugfix: RDMA client cannot fall back to TCP when server does not recognize RDMA handshake
1 parent 11c8ea9 commit 4d56434

14 files changed

Lines changed: 748 additions & 233 deletions

CLAUDE.md

Lines changed: 182 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,182 @@
1+
# CLAUDE.md
2+
3+
This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.
4+
5+
作者:yahah
6+
链接:https://www.zhihu.com/question/1979609139266213083/answer/2009429788666909340
7+
来源:知乎
8+
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。
9+
10+
## 使用方式
11+
12+
- 本文件分为默认规则与按需模式两层。
13+
- 默认规则始终生效。
14+
- 按需模式仅在用户明确要求,或任务明显需要且已获用户确认时启用;未启用前,不默认进入重流程。
15+
- 涉及项目/系统设计、文档体系设计或高风险改动时,应在开始时让用户选择是否启用相关按需模式。
16+
17+
## 默认规则
18+
19+
### 工作原则
20+
21+
- 非微小改动先说明方法。
22+
- 需求有歧义、风险高或影响大时,先澄清并获批,再开始写代码。
23+
- 坚持 Spec Coding,避免 Vibe Coding;Plan 只写方案、范围、风险和验收标准,不写实现代码。
24+
- 优先小步迭代;实现与审查分离。
25+
- 完成后可执行 /simplify;必要时使用 /loop。
26+
27+
### 编码约束
28+
29+
- 代码中只使用英文。
30+
- 注释说明意图、约束和边界,不记录开发过程式说明。
31+
- 优先用概念、模块、职责和符号名定位代码;不要只依赖易漂移的行号,必要时可补充文件路径。
32+
- Spec 不依赖行号定位代码。
33+
- 不为未被请求的未来需求提前抽象、泛化或暴露配置。
34+
35+
### 质量与验证
36+
37+
- 项目早期只保留最小必要质量标准:可运行、可验证、可回滚。
38+
- 关键路径、高风险改动和外部接口必须可验证。
39+
- 修复 bug 时,先复现,再修复,再验证。
40+
- 任何"已完成""已修复""已通过"的结论,都必须附验证方式、命令或结果摘要。
41+
- 若当前无法验证,必须明确说明原因、风险和未覆盖范围。
42+
43+
### 拆分与沉淀
44+
45+
- 将任务拆成低耦合、可独立验证的子任务;必要时使用 /batch。
46+
- 重复出现且边界稳定的流程,应沉淀为 Skill、脚本或检查清单。
47+
- 公共规则优先沉淀为文档、测试或自动化,而不是只停留在对话里。
48+
49+
### 协作与纠错
50+
51+
- 被纠正时,先验证问题是否适用于当前代码库,再调整做法。
52+
- 外部建议先核对是否适用,再决定是否采纳。
53+
- 对重复性问题,沉淀为明确规则、测试或自动检查。
54+
55+
### Codex 协作
56+
57+
- Codex 是补充能力,不是默认执行者;当前 Agent 负责主线推进、需求澄清、关键决策、首轮实现和最终验收。
58+
- 仅在以下场景使用 Codex:独立只读代码评审、adversarial review、边界清晰且可并行的子任务、或长耗时调查与后台续跑;委派前必须先定义目标、约束、验收标准和边界。
59+
- 不要把需求澄清、方案收敛、架构取舍、小而集中的直接实现或需要持续用户交互的主线任务交给 Codex;Codex 的结果必须由当前 Agent 整合并复核。
60+
- 可用命令:`/codex:review``/codex:adversarial-review``/codex:rescue``/codex:status``/codex:result``/codex:cancel`(需安装 [codex-plugin-cc](https://github.com/openai/codex-plugin-cc)
61+
62+
### 禁止事项
63+
64+
- 永远不要使用 /init,除非项目明确要求。
65+
- CLAUDE.md 必须按项目实际需求编写,不套用空泛模板。
66+
- 不要在代码注释、commit message 或 PR body 中使用描述开发进度的词,如 FIXED、Step、Week、Section、Phase、AC-x。
67+
- 不要在代码注释、commit message 或 PR body 中出现 AI 工具名称,如 Codex、Claude、Grok、Gemini 等。
68+
- 不要把外部实现细节、外部文档或外部技能树直接提升为当前项目的硬约束。
69+
70+
## 按需模式
71+
72+
### 架构与演进模式
73+
74+
- 适用:用户要求项目或系统设计满足分层、稳定接口、可演进,或明确要求按架构流程推进。
75+
- 优先做分层设计;不同层次保持职责分离,只通过明确、稳定的接口交互。
76+
- 不要让上层依赖下层实现细节,也不要建立非必要的跨层耦合;若必须依赖,应收敛为单向、最小依赖。
77+
- 每个层次内优先做 primitive 设计;primitive 应是独立、可替换、可组合、可验证的最小功能单元。
78+
- 若项目采用多 Agent 协作,应按层次设计专用 agent 与 skill,使其职责、输入、输出和边界清晰。
79+
- 架构演进必须逐步验证;每一步新增特性或重构,都要确认不破坏已有接口、行为和关键路径。
80+
81+
### Agent-Native 文档模式
82+
83+
- 适用:用户要求文档同时服务人类和 Agent,或明确要求 Agent-Native 文档体系。
84+
- 使用两层结构,避免重复:
85+
- canonical skill docs (.claude/docs/):保存详细、长期维护的正式正文,供人类和 Agent 共读。
86+
- agent-facing index(.claude/skills/):只负责把 Agent 路由到正确正文,不复述内容,也不要求与每份正文一一对应。
87+
- 文档应同时对人类和 Agent 可读:写清能力、前提、边界、依赖、接口和典型用法,避免只对单一读者成立的隐式上下文。
88+
- 仅 skill 文档必须包含 frontmatter:type、tags、requires(仅写硬依赖)。
89+
- Agent 读取文档时,先通过索引定位主题,再进入正文;仅递归读取 requires 指向的硬依赖文档,其他相邻或参考文档按需读取。
90+
- 本地文档是当前仓库契约;外部资料、外部 skills 或示例实现只作参考,不覆盖本地约定。
91+
- 量化结论必须带测试条件和适用范围。
92+
93+
### 严格验证模式
94+
95+
- 适用:改动高风险、回归代价高,或用户要求每一步都经过验证。
96+
- 将工作拆成可独立验证的小步;每一步完成后先验证,再继续下一步。
97+
- 新增特性、重构或修复都要确认不破坏已有功能、接口和关键路径。
98+
- 若当前无法完成必要验证,应暂停继续扩展,并明确说明阻塞、风险和未覆盖范围。
99+
100+
## 项目简介
101+
102+
Apache bRPC —— 工业级 C++ RPC 框架。仓库同时包含若干可独立复用的基础库(`butil``bthread``bvar``json2pb``mcpack2pb`),RPC 层构建于其之上。权威架构文档位于 `docs/en/``docs/cn/`,重点参考 `io.md``threading_overview.md``memory_management.md``bthread.md``iobuf.md``load_balancing.md``new_protocol.md`
103+
104+
## 构建
105+
106+
仓库同时支持三套构建系统,日常开发以 CMake 为主。
107+
108+
```sh
109+
# CMake(推荐,外部构建目录 + 并行编译)
110+
cmake -B build && cmake --build build -j6
111+
112+
# 启用单元测试需显式开启(详见下文 "测试" 章节)
113+
cmake -B build -DCMAKE_POLICY_VERSION_MINIMUM=3.5 -DBUILD_UNIT_TESTS=ON && cmake --build build -j6
114+
115+
# 常用 CMake 选项
116+
-DCMAKE_EXPORT_COMPILE_COMMANDS=ON # 生成 compile_commands.json,供 clangd/LSP 使用
117+
-DWITH_DEBUG_SYMBOLS=OFF # 不带调试符号,二进制更小
118+
-DWITH_GLOG=ON # 用 glog 替换 butil/logging
119+
-DWITH_THRIFT=ON # 启用 thrift 协议
120+
-DWITH_RDMA=ON # 启用 RDMA 传输
121+
-DWITH_BTHREAD_TRACER=ON # 链接 libunwind,启用 bthread tracer
122+
-DLINK_SO=ON # example 链接共享库版本
123+
```
124+
125+
替代方案:`sh config_brpc.sh --headers=<inc> --libs=<lib> [--with-glog --with-thrift --cxx=clang++ --cc=clang]` 后执行 `make`。也支持 Bazel(`bazel build //...`),细节见 `docs/en/bazel_support.md`
126+
127+
如果需要快速查看最新编译产物,可优先复用仓库里已存在的 `cmake-build-debug/``cmake-build-debug-gdr/` 构建目录(确认未过期即可)。
128+
129+
## 测试
130+
131+
启用 `-DBUILD_UNIT_TESTS=ON` 后,测试二进制会生成在 `build/test/` 下。命名遵循以下模式:`test_butil``test_bvar``bthread_*_unittest``brpc_*_unittest`
132+
133+
```sh
134+
# 通过 CTest 跑全部测试
135+
cmake --build build -j6 && (cd build && ctest --output-on-failure)
136+
137+
# 或使用旧版驱动脚本(要求二进制在当前目录,需 cd 到 build/test 下)
138+
cd build/test && bash ../../test/run_tests.sh
139+
140+
# 只跑单个测试二进制(支持 gtest filter)
141+
./build/test/brpc_channel_unittest --gtest_filter=ChannelTest.connection_failed
142+
```
143+
144+
测试编译时带有 `-Dprivate=public -Dprotected=public -DUNIT_TEST -DBVAR_NOT_LINK_DEFAULT_VARIABLES`,并强制 include `test/sstream_workaround.h` —— 单测会主动访问类的内部状态,这是有意为之。仓库已接入 ASan,`run_tests.sh` 默认设置 `ASAN_OPTIONS="detect_leaks=0:detect_stack_use_after_return=1"`
145+
146+
## 架构总览(big picture)
147+
148+
代码呈清晰的分层结构。改动时请把新增逻辑放在已经持有相关概念的最低层:
149+
150+
- **`src/butil/`** —— Chromium 衍生的基础库(日志、字符串、文件、时间、原子、容器、`IOBuf`、FlatMap、ResourcePool/ObjectPool、Arena)。不依赖 bthread 与 RPC。RPC 层最常用的部件是 `IOBuf`(贯穿全链路的零拷贝缓冲区)、`ResourcePool`/`ObjectPool`(无锁的强类型对象池,支撑大量内部对象)、以及 `endpoint`/`fd_utility`
151+
- **`src/bthread/`** —— M:N 用户态线程库。一个 bthread 是带栈协程,被一组 pthread worker 调度执行;`butex.{h,cpp}` 是类似 futex 的同步原语,所有阻塞机制(mutex、条件变量、`bthread_id`、ExecutionQueue)都构建于其上。`task_group.cpp` 是每个 worker 的调度器(含 work-stealing),`task_control.cpp` 管理 worker 池。在改调度相关代码前,请先阅读 `docs/en/bthread.md``docs/en/threading_overview.md`
152+
- **`src/bvar/`** —— 无锁计数器/Gauge/Recorder + 采样。框架内部统计与 `/vars` builtin 服务都依赖它。
153+
- **`src/json2pb/``src/mcpack2pb/`** —— protobuf 与 JSON / mcpack 之间的编解码。HTTP/JSON 与 Baidu 历史协议会用到。
154+
- **`src/brpc/`** —— RPC 框架本体,关键模块:
155+
- `Server` / `Acceptor` / `InputMessenger` —— 接受连接、切分输入、分派处理。`EventDispatcher`(epoll/kqueue)只负责"通知可读",真正的读取动作发生在新启动的 bthread 中,且 EDISP 会主动让出当前 worker 给该 bthread,以获得更好的 cache locality。详见 `docs/en/io.md`
156+
- `Socket` —— 通用连接抽象(覆盖 TCP、UDS、RDMA、流式)。发送侧使用 wait-free 的 MPSC 单链表;当一次 syscall 写不完时,会 fork 出 `KeepWrite` bthread 接管剩余数据。Socket 通过 `SocketId` 引用计数,由 `ResourcePool` 回收复用。
157+
- `Channel` / `Controller` —— 客户端入口。`Controller` 是贯穿每次调用的状态对象,超时、重试、attachment、请求/响应编解码、鉴权等几乎所有客户端特性都从它穿过。
158+
- **协议是可插拔的**。每个协议位于 `src/brpc/policy/*_protocol.{h,cpp}`,通过向 `InputMessenger` 注册 `Parse` + `Process` 回调接入。新增协议请按 `docs/en/new_protocol.md` 操作;**不要**把协议特有的逻辑塞进 `server.cpp` / `channel.cpp` / `controller.cpp`(这一点在 `CONTRIBUTING.md` 中已明确指出)。
159+
- **命名服务与负载均衡同样可插拔**,位于 `src/brpc/policy/``*_naming_service.cpp``*_load_balancer.cpp`)。`LocalityAwareLoadBalancer` 是其中最微妙的一类 —— 近期 commit 表明它对所使用的时间源很敏感。
160+
- `src/brpc/builtin/` —— HTTP 调试页(`/status``/vars``/flags``/rpcz``/connections`、各类 profiler)。新增 server 端调试接口请放在这里。
161+
162+
横切关注点:gflags 在全仓库被广泛用于运行时调参,`grep` `DEFINE_int32` / `DEFINE_bool` 即可定位。许多看似隐晦的行为其实是某个 flag 控制的(例如 `event_dispatcher_edisp_unsched`)。
163+
164+
## Examples
165+
166+
`example/<name>_c++/` 下每个目录都是独立可构建的 demo,自带 `Makefile``CMakeLists.txt` 链接 bRPC。它们是几乎所有特性的标准用法示范 —— 在回答"X 怎么用?"之前,先在 example 里找一找,不要自行臆造用法。构建示例:
167+
168+
```sh
169+
cd example/echo_c++ && cmake -B build && cmake --build build -j4
170+
./build/echo_server & ./build/echo_client
171+
```
172+
173+
## 代码规范
174+
175+
- 遵循 Google C++ 风格,**缩进 4 空格**(见 `CONTRIBUTING.md`)。新增功能必须配单测。
176+
- 头文件统一用 `.h`;实现文件 `.cpp``.cc` 混用(Chromium 来源代码保留 `.cc`,bRPC 自身代码使用 `.cpp`)。
177+
- 仓库部分代码早于 C++17,整体最低标准是 C++11。引入更新标准的特性前,先检查 `CMakeLists.txt``Makefile` 的编译选项。
178+
- 编译没有开启 `-Werror``Makefile` 中有显式注释说明),不要用"修复警告以通过 -Werror"为由做掩盖问题的改动。
179+
180+
# PR
181+
182+
按照 .github/pull_request_template.md 模板,提供英文 PR 描述。

src/brpc/global.cpp

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -83,6 +83,7 @@
8383
#include "brpc/policy/nshead_mcpack_protocol.h"
8484
#include "brpc/policy/rtmp_protocol.h"
8585
#include "brpc/policy/esp_protocol.h"
86+
#include "brpc/policy/rdma_handshake_fallback_protocol.h"
8687
#ifdef ENABLE_THRIFT_FRAMED_PROTOCOL
8788
# include "brpc/policy/thrift_protocol.h"
8889
#endif
@@ -617,6 +618,15 @@ static void GlobalInitializeOrDieImpl() {
617618
exit(1);
618619
}
619620

621+
Protocol rdma_handshake_fallback_protocol = {
622+
ParseRdmaHandshake, NULL, NULL,
623+
ProcessRdmaHandshake, NULL,
624+
NULL, NULL, NULL,
625+
CONNECTION_TYPE_ALL, "rdma_handshake" };
626+
if (RegisterProtocol(PROTOCOL_RDMA_HANDSHAKE, rdma_handshake_fallback_protocol) != 0) {
627+
exit(1);
628+
}
629+
620630
std::vector<Protocol> protocols;
621631
ListProtocols(&protocols);
622632
for (size_t i = 0; i < protocols.size(); ++i) {

src/brpc/input_messenger.cpp

Lines changed: 0 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -79,7 +79,6 @@ DECLARE_uint64(max_body_size);
7979
const size_t MSG_SIZE_WINDOW = 10; // Take last so many message into stat.
8080
const size_t MIN_ONCE_READ = 4096;
8181
const size_t MAX_ONCE_READ = 524288;
82-
const size_t PROTO_DUMMY_LEN = 4;
8382

8483
ParseResult InputMessenger::CutInputMessage(
8584
Socket* m, size_t* index, bool read_eof) {
@@ -107,16 +106,6 @@ ParseResult InputMessenger::CutInputMessage(
107106
<< " bytes, the connection will be closed."
108107
" Set max_body_size to allow bigger messages";
109108
return result;
110-
} else {
111-
if (m->_read_buf.size() >= 4) {
112-
// The length of `data' must be PROTO_DUMMY_LEN + 1 to store extra ending char '\0'
113-
char data[PROTO_DUMMY_LEN + 1];
114-
m->_read_buf.copy_to_cstr(data, PROTO_DUMMY_LEN);
115-
if (strncmp(data, "RDMA", PROTO_DUMMY_LEN) == 0) {
116-
// To avoid timeout when client uses RDMA but server uses TCP
117-
return MakeParseError(PARSE_ERROR_TRY_OTHERS);
118-
}
119-
}
120109
}
121110

122111
if (m->CreatedByConnect()) {

src/brpc/options.proto

Lines changed: 22 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -43,28 +43,29 @@ enum ProtocolType {
4343
PROTOCOL_SOFA_PBRPC = 4;
4444
PROTOCOL_RTMP = 5;
4545
PROTOCOL_THRIFT = 6;
46-
PROTOCOL_HTTP = 7;
47-
PROTOCOL_PUBLIC_PBRPC = 8;
48-
PROTOCOL_NOVA_PBRPC = 9;
49-
PROTOCOL_REDIS = 10;
50-
PROTOCOL_NSHEAD_CLIENT = 11; // implemented in baidu-rpc-ub
51-
PROTOCOL_NSHEAD = 12;
52-
PROTOCOL_HADOOP_RPC = 13;
53-
PROTOCOL_HADOOP_SERVER_RPC = 14;
54-
PROTOCOL_MONGO = 15; // server side only
55-
PROTOCOL_UBRPC_COMPACK = 16;
56-
PROTOCOL_DIDX_CLIENT = 17; // Client side only
57-
PROTOCOL_MEMCACHE = 18; // Client side only
58-
PROTOCOL_ITP = 19;
59-
PROTOCOL_NSHEAD_MCPACK = 20;
60-
PROTOCOL_DISP_IDL = 21; // Client side only
61-
PROTOCOL_ERSDA_CLIENT = 22; // Client side only
62-
PROTOCOL_UBRPC_MCPACK2 = 23; // Client side only
46+
PROTOCOL_RDMA_HANDSHAKE = 7;
47+
PROTOCOL_HTTP = 8;
48+
PROTOCOL_PUBLIC_PBRPC = 9;
49+
PROTOCOL_NOVA_PBRPC = 10;
50+
PROTOCOL_REDIS = 11;
51+
PROTOCOL_NSHEAD_CLIENT = 12; // implemented in baidu-rpc-ub
52+
PROTOCOL_NSHEAD = 13;
53+
PROTOCOL_HADOOP_RPC = 14;
54+
PROTOCOL_HADOOP_SERVER_RPC = 15;
55+
PROTOCOL_MONGO = 16; // server side only
56+
PROTOCOL_UBRPC_COMPACK = 17;
57+
PROTOCOL_DIDX_CLIENT = 18; // Client side only
58+
PROTOCOL_MEMCACHE = 19; // Client side only
59+
PROTOCOL_ITP = 20;
60+
PROTOCOL_NSHEAD_MCPACK = 21;
61+
PROTOCOL_DISP_IDL = 22; // Client side only
62+
PROTOCOL_ERSDA_CLIENT = 23; // Client side only
63+
PROTOCOL_UBRPC_MCPACK2 = 24; // Client side only
6364
// Reserve special protocol for cds-agent, which depends on FIFO right now
64-
PROTOCOL_CDS_AGENT = 24; // Client side only
65-
PROTOCOL_ESP = 25; // Client side only
66-
PROTOCOL_H2 = 26;
67-
PROTOCOL_COUCHBASE = 27;
65+
PROTOCOL_CDS_AGENT = 25; // Client side only
66+
PROTOCOL_ESP = 26; // Client side only
67+
PROTOCOL_H2 = 27;
68+
PROTOCOL_COUCHBASE = 28;
6869
}
6970

7071
enum CompressType {

src/brpc/parse_result.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@
1919
#ifndef BRPC_PARSE_RESULT_H
2020
#define BRPC_PARSE_RESULT_H
2121

22+
#include <stddef.h>
2223

2324
namespace brpc {
2425

0 commit comments

Comments
 (0)