| # FlatBuffers RPC example | |
| [English guide](../../docs/en/flatbuffers.md) | | |
| [δΈζζε](../../docs/cn/flatbuffers.md) | |
| Independent client and server programs using the `fb_rpc` protocol, a generated | |
| FlatBuffers service, and `brpc::flatbuffers::Message`. By default the client | |
| runs a **bounded functional check**. A timed mode compares `fb_rpc` with an | |
| equivalent protobuf service over `baidu_std` (see | |
| [Timed comparison](#timed-comparison)). | |
| The schema declares `BenchmarkService.Echo` with stable wire method ID **7**. | |
| The generated stub calls `Channel::FBCallMethod`, and the server registers the | |
| generated service with `Server::AddFlatBuffersService`. The same server also | |
| registers `benchmark_fb_pb.BenchmarkService` from `echo_pb.proto`, a protobuf | |
| service with the same fields, and serves both `fb_rpc` and `baidu_std`. | |
| ## Build | |
| Dependencies, the FlatBuffers-enabled runtime install, the generator and the | |
| example build are described in the guide's | |
| [verification section](../../docs/en/flatbuffers.md#verify-with-the-clientserver-example). | |
| `BRPC_ROOT` must be an install prefix (or a Make `output/` directory) of a bRPC | |
| built with FlatBuffers ON; the example reads its `include/butil/config.h` and | |
| rejects an OFF build. The example compiles as C++17, which FlatBuffers support | |
| requires. Both `flatc` and `brpc_flatc` write generated code only to | |
| the build directory; do not commit `*_generated.h` or `*.brpc.fb.*`. | |
| To link the shared library, build and install the runtime with | |
| `-DBUILD_SHARED_LIBS=ON` and the `brpc-shared` target, then configure the | |
| example with `-DLINK_SO=ON`. Extra runtime dependencies can be passed with | |
| `-DBRPC_EXTRA_LIBRARIES='library1;library2'`. | |
| ## Run separately | |
| By default the server and client accept only IPv4 loopback endpoints. In the | |
| build terminal: | |
| ```sh | |
| "$WORK/example/benchmark_fb_server" --listen_addr=127.0.0.1:0 --duration_s=300 | |
| ``` | |
| The server prints `BRPC_FB_READY 127.0.0.1:<port>` after `Server::Start` | |
| succeeds. Use that port in another terminal (shell variables are not shared, so | |
| set `WORK` again): | |
| ```sh | |
| WORK=/absolute/path/printed/by/the/build | |
| SERVER=127.0.0.1:PORT_FROM_READY | |
| "$WORK/example/benchmark_fb_client" --server="$SERVER" \ | |
| --request_count=16 --thread_num=2 --request_size=8193 --attachment_size=257 | |
| "$WORK/example/benchmark_fb_client" --server="$SERVER" \ | |
| --request_count=2 --thread_num=1 --request_size=0 --omit_message=true | |
| "$WORK/example/benchmark_fb_client" --server="$SERVER" \ | |
| --request_count=2 --thread_num=1 --corrupt_request=true | |
| "$WORK/example/benchmark_fb_client" --server="$SERVER" \ | |
| --request_count=2 --thread_num=1 | |
| ``` | |
| All four commands exit 0. The first prints | |
| `{"completed":16,"successes":16,"expected_rejections":0,"failures":0}`. The | |
| corrupt-request run prints `{"completed":2,"successes":0,"expected_rejections":2,"failures":0}`, | |
| and the last one checks recovery on the same server (`completed=2`, | |
| `successes=2`). | |
| The client verifies the response schema before reading it, then compares the | |
| request ID, every string byte, optional-string presence, and the attachment. | |
| `--corrupt_request=true` damages the FlatBuffers root offset but keeps the FRPC | |
| framing valid. It succeeds only if every request gets the generated service's | |
| schema rejection (error code -1); timeouts and connection errors are failures. | |
| `failures` is a 0/1 flag, not a count, and no throughput is reported. | |
| ### Bounded controls | |
| | Client option | Default | Allowed | | |
| | --- | --- | --- | | |
| | `server` | required | `127.0.0.1:<nonzero-port>`, any IPv4 with `allow_remote` | | |
| | `protocol` | `fb_rpc` | `fb_rpc`, `baidu_std` (protobuf control service) | | |
| | `request_count` | 16 | 1..1000 total, not per thread | | |
| | `thread_num` | 2 | 1..16 synchronous callers (1..1024 when timed) | | |
| | `request_size` | 64 | 0..1048576 string bytes | | |
| | `attachment_size` | 0 | 0..1048576 bytes | | |
| | `timeout_ms` | 1500 | 1..5000 per RPC | | |
| | `deadline_ms` | 30000 | 1..120000 for the client run | | |
| | `connection_type` | `single` | `single`, `pooled`, `short` (`short` only in count mode) | | |
| | `omit_message` | false | Preserve absent rather than empty string | | |
| | `corrupt_request` | false | Require schema rejection (`fb_rpc`, count mode only) | | |
| | `duration_s` | 0 | 0 for count mode, 1..3600 for timed mode | | |
| | `warmup_s` | 0 | 0..600 unmeasured seconds before a timed run | | |
| | `allow_remote` | false | Accept a non-loopback `server` | | |
| | `use_bthread` | false | Run callers as bthreads, like `multi_threaded_echo_c++ --use_bthread` | | |
| | `fail_worker_start_at` | -1 | Tests only: fail to start caller N (0-based), -1 disables | | |
| The server defaults to `127.0.0.1:0`, `duration_s=60` (1..86400) and | |
| `max_concurrency=32` (1..4096), and stops earlier on SIGINT/SIGTERM. When | |
| `duration_s` runs out it prints a line to stderr and stops, so give it longer | |
| than `warmup_s + duration_s` of every client run. `max_concurrency` must be at | |
| least the total `thread_num` of the clients, or calls fail with ELIMIT. | |
| `--reply_delay_ms` (0..10000, default 0) delays every reply and is meant for | |
| tests only. Use | |
| `--allow_remote` with a non-loopback `listen_addr`. Builtin | |
| services are disabled. FRPC has no authentication, compression, checksums or | |
| streaming, so this is not a production or public-network configuration. | |
| ## Timed comparison | |
| With `--duration_s` the client keeps one synchronous RPC in flight per caller | |
| thread, verifies every reply, and prints one JSON line with QPS, payload | |
| throughput, latency percentiles and the client's CPU cores. Only calls that | |
| finish inside the window are counted, and the window closes when the timer | |
| expires, not when the last in-flight call returns. `started_threads` reports | |
| how many callers actually ran; if any caller fails to start, the client still | |
| prints the line but exits 1. Latency is the RPC alone, without building the | |
| request or checking the reply. Percentiles come from a fixed-size histogram and | |
| are at most 1/128 above the recorded value; `avg_us` and `max_us` are exact. | |
| `payload_mib_per_s` counts the request string and attachment once, although | |
| the server echoes both back. SIGINT or SIGTERM closes the window early, and | |
| the client prints the result but exits 1. Short connections are rejected in | |
| timed mode, because closed connections would exhaust local ports in | |
| TIME_WAIT. Run each protocol | |
| with identical sizes and concurrency against the same server: | |
| ```sh | |
| "$WORK/example/benchmark_fb_server" --listen_addr=SERVER_IP:8901 --allow_remote \ | |
| --duration_s=3600 --max_concurrency=4096 | |
| for p in fb_rpc baidu_std; do | |
| "$WORK/example/benchmark_fb_client" --server=SERVER_IP:8901 --allow_remote \ | |
| --protocol=$p --duration_s=30 --warmup_s=3 --thread_num=64 --request_size=8192 | |
| done | |
| ``` | |
| Both services echo the request string and attachment; the FlatBuffers path | |
| builds and verifies a FlatBuffers response, while the protobuf path parses and | |
| serializes the equivalent message. The results are only comparable when | |
| binaries, hosts, concurrency, sizes and duration are the same. | |
| ## Smoke test | |
| CTest runs the standard-library-only `smoke.py`, which starts its own server on | |
| an ephemeral loopback port and checks: | |
| 1. finite RPCs with binary attachments, empty and absent strings, and | |
| single/pooled/short connections (13 verified replies); | |
| 2. two malformed-schema requests are rejected; | |
| 3. normal requests still work on the same server afterwards; | |
| 4. a timed run where one of two callers fails to start (pthread and bthread) | |
| exits non-zero and reports `started_threads=1`, and a timed run over short | |
| connections is rejected; | |
| 5. a timed run against a second server with `--reply_delay_ms=600` reports a | |
| window of about `--duration_s` and a median latency of at least 600 ms, | |
| although its last call ends after the window closes; | |
| 6. the servers it started are stopped and reaped. | |
| The orchestration deadline is 40 seconds and CTest's timeout is 50 seconds. | |
| Needing the SIGKILL fallback fails the test. The smoke test can also run | |
| without CTest: | |
| ```sh | |
| python3 "$REPO/example/benchmark_fb/smoke.py" \ | |
| --server "$WORK/example/benchmark_fb_server" \ | |
| --client "$WORK/example/benchmark_fb_client" | |
| ``` | |
| For common failures see the guide's | |
| [troubleshooting table](../../docs/en/flatbuffers.md#troubleshooting). |