diff --git a/docs/cn/bazel_support.md b/docs/cn/bazel_support.md index 0ff86119cb..3a138cfbb7 100644 --- a/docs/cn/bazel_support.md +++ b/docs/cn/bazel_support.md @@ -1,20 +1,100 @@ -## bRPC 作为Bazel第三方依赖 -1. bRPC 依赖于一些开源库, 但这些库并没有提供bazel支持, 所以需要你手动将一部分依赖加入到你的构建项目中. -2. 将 /example/build_with_bazel/*.BUILD 和 brpc_workspace.bzl 该文件移动到你的项目根目录下, 将 -```c++ - load("@//:brpc_workspace.bzl", "brpc_workspace") - brpc_workspace(); +## bRPC 作为 Bazel 第三方依赖 + +推荐在 Bazel 项目中使用 bzlmod(`MODULE.bazel`)依赖本地 bRPC 源码。 +`example/build_with_bazel_module` 中有一个包含 server 和 client 的可运行示例: + +```shell +$ cd example/build_with_bazel_module +$ bazel build //:echo_c++_server //:echo_c++_client +$ bazel run //:echo_c++_server & +$ bazel run //:echo_c++_client +``` + +先在你的 `.bazelrc` 中添加 bRPC 使用的 registry: + +```shell +common --registry=https://bcr.bazel.build +common --registry=https://baidu.github.io/babylon/registry +common --registry=https://raw.githubusercontent.com/apache/brpc/master/registry ``` -内容添加到你的WORKSPACE中. -3. 链接请使用 - ```c++ - ... - deps = [ +然后在 `MODULE.bazel` 中添加 bRPC,并指向本地 bRPC 源码: + +```python +module( + name = "my_brpc_app", + version = "0.1.0", +) + +bazel_dep(name = "protobuf", version = "27.3", repo_name = "com_google_protobuf") +bazel_dep(name = "brpc", version = "1.17.0", repo_name = "apache_brpc") + +local_path_override( + module_name = "brpc", + path = "/path/to/brpc", +) + +single_version_override( + module_name = "leveldb", + registry = "https://raw.githubusercontent.com/secretflow/bazel-registry/main", +) + +single_version_override( + module_name = "openssl", + version = "3.3.2.bcr.1", + registry = "https://raw.githubusercontent.com/secretflow/bazel-registry/main", +) +``` + +`bazel_dep` 用来声明模块名和仓库映射,`local_path_override` 让 Bazel 使用本地源码,而不是从 registry 解析 bRPC。 + +bzlmod 不会传递依赖模块中的 override。把 bRPC 当依赖时,需要在自己项目的根 +`MODULE.bazel` 里重复上面的 `single_version_override`;它们在 aarch64 上是必需的,否则 leveldb toolchain 解析会失败。 + +之后在目标中链接 bRPC: + +```python +cc_binary( + name = "server", + srcs = ["server.cpp"], + deps = [ + "@apache_brpc//:brpc", + ], +) +``` + +如果服务使用 protobuf,可以从 bRPC 加载 `brpc_proto_library`: + +```python +load("@apache_brpc//bazel/tools:brpc_proto_library.bzl", "brpc_proto_library") + +brpc_proto_library( + name = "cc_echo_proto", + srcs = ["echo.proto"], +) +``` + +## 旧版 WORKSPACE 用法 + +仍在使用 `WORKSPACE` 的项目可以参考 `example/build_with_bazel`。 + +1. 将 `example/build_with_bazel/*.BUILD` 和 + `example/build_with_bazel/brpc_workspace.bzl` 移动到你的项目根目录下。 +2. 在 `WORKSPACE` 中添加: + +```python +load("@//:brpc_workspace.bzl", "brpc_workspace") + +brpc_workspace() +``` + +3. 在目标中链接 `apache_brpc`: + +```python +deps = [ "@apache_brpc//:bthread", "@apache_brpc//:brpc", "@apache_brpc//:butil", "@apache_brpc//:bvar", - ] - ... - ``` +] +``` diff --git a/docs/cn/getting_started.md b/docs/cn/getting_started.md index 334906a125..51e9b9759e 100644 --- a/docs/cn/getting_started.md +++ b/docs/cn/getting_started.md @@ -121,6 +121,19 @@ $ ./echo_client $ mkdir build && cd build && cmake -DBUILD_UNIT_TESTS=ON .. && make && make test ``` +### 使用 Bazel 编译 brpc + +bRPC 也支持 Bazel 构建。`example/build_with_bazel_module` 下的 bzlmod 示例会基于本地 +bRPC 源码构建 server 和 client: + +```shell +$ cd example/build_with_bazel_module +$ bazel build //:echo_c++_server //:echo_c++_client +``` + +如果要把 bRPC 作为 Bazel 依赖使用,包括需要配置的 registry 和 `MODULE.bazel`, +请参考 [Bazel 支持](bazel_support.md)。 + ## Fedora/CentOS ### 依赖准备 diff --git a/docs/en/bazel_support.md b/docs/en/bazel_support.md index 607cf9c462..d1497606ce 100644 --- a/docs/en/bazel_support.md +++ b/docs/en/bazel_support.md @@ -1,20 +1,105 @@ ## bRPC as a Bazel third-party dependency -1. bRPC relies on a number of open source libraries that do not provide bazel support, so you will need to manually add some of these dependencies to your build project. -2. Move the BUILD file /example/build_with_bazel/*.BUILD and brpc_workspace.bzl to the root of your project, and add the contents of -```c++ - load("@//:brpc_workspace.bzl", "brpc_workspace") - brpc_workspace(); + +The recommended way to depend on a local bRPC checkout from a Bazel project is +to use bzlmod (`MODULE.bazel`). See `example/build_with_bazel_module` for a +runnable example with both a server and a client: + +```shell +$ cd example/build_with_bazel_module +$ bazel build //:echo_c++_server //:echo_c++_client +$ bazel run //:echo_c++_server & +$ bazel run //:echo_c++_client +``` + +Add the registries used by bRPC to your `.bazelrc`: + +```shell +common --registry=https://bcr.bazel.build +common --registry=https://baidu.github.io/babylon/registry +common --registry=https://raw.githubusercontent.com/apache/brpc/master/registry ``` -to your WORKSPACE -3. link apache_brpc like: - ```c++ - ... - deps = [ +Add bRPC to your `MODULE.bazel`, and point it to your local bRPC checkout: + +```python +module( + name = "my_brpc_app", + version = "0.1.0", +) + +bazel_dep(name = "protobuf", version = "27.3", repo_name = "com_google_protobuf") +bazel_dep(name = "brpc", version = "1.17.0", repo_name = "apache_brpc") + +local_path_override( + module_name = "brpc", + path = "/path/to/brpc", +) + +single_version_override( + module_name = "leveldb", + registry = "https://raw.githubusercontent.com/secretflow/bazel-registry/main", +) + +single_version_override( + module_name = "openssl", + version = "3.3.2.bcr.1", + registry = "https://raw.githubusercontent.com/secretflow/bazel-registry/main", +) +``` + +The `bazel_dep` keeps the module name and repository mapping, while +`local_path_override` makes Bazel use the local checkout instead of resolving +bRPC from a registry. + +bzlmod does not propagate overrides from dependency modules. When using bRPC as +a dependency, repeat the `single_version_override` entries above in your root +`MODULE.bazel`; they are required on aarch64 because leveldb toolchain +resolution otherwise fails. + +Then link bRPC from your targets: + +```python +cc_binary( + name = "server", + srcs = ["server.cpp"], + deps = [ + "@apache_brpc//:brpc", + ], +) +``` + +If your service uses protobuf, load `brpc_proto_library` from bRPC: + +```python +load("@apache_brpc//bazel/tools:brpc_proto_library.bzl", "brpc_proto_library") + +brpc_proto_library( + name = "cc_echo_proto", + srcs = ["echo.proto"], +) +``` + +## Legacy WORKSPACE usage + +For projects that still use `WORKSPACE`, see `example/build_with_bazel`. + +1. Move `example/build_with_bazel/*.BUILD` and + `example/build_with_bazel/brpc_workspace.bzl` to the root of your project. +2. Add the following to your `WORKSPACE`: + +```python +load("@//:brpc_workspace.bzl", "brpc_workspace") + +brpc_workspace() +``` + +3. Link `apache_brpc` from your targets: + +```python +deps = [ "@apache_brpc//:bthread", "@apache_brpc//:brpc", "@apache_brpc//:butil", "@apache_brpc//:bvar", - ] - ... - ``` +] +``` diff --git a/docs/en/getting_started.md b/docs/en/getting_started.md index 32d4733766..9acadc75e0 100644 --- a/docs/en/getting_started.md +++ b/docs/en/getting_started.md @@ -108,6 +108,20 @@ Examples link brpc statically, if you need to link the shared version, remove `C $ mkdir build && cd build && cmake -DBUILD_UNIT_TESTS=ON .. && make && make test ``` +### Compile brpc with Bazel + +bRPC also supports Bazel builds. The bzlmod example under +`example/build_with_bazel_module` builds a server and a client against the local +bRPC checkout: + +```shell +$ cd example/build_with_bazel_module +$ bazel build //:echo_c++_server //:echo_c++_client +``` + +For using bRPC as a Bazel dependency, including the required registries and +`MODULE.bazel` setup, see [Bazel support](bazel_support.md). + ### Compile brpc with vcpkg [vcpkg](https://github.com/microsoft/vcpkg) is a package manager that supports all platforms, diff --git a/example/build_with_bazel_module/BUILD.bazel b/example/build_with_bazel_module/BUILD.bazel index 8dbc38012c..340a1ba014 100644 --- a/example/build_with_bazel_module/BUILD.bazel +++ b/example/build_with_bazel_module/BUILD.bazel @@ -30,3 +30,14 @@ cc_binary( "@apache_brpc//:brpc", ], ) + +cc_binary( + name = "echo_c++_client", + srcs = [ + "client.cpp", + ], + deps = [ + ":cc_echo_c++_proto", + "@apache_brpc//:brpc", + ], +) diff --git a/example/build_with_bazel_module/MODULE.bazel b/example/build_with_bazel_module/MODULE.bazel index e5d66a8e10..44e6f30c17 100644 --- a/example/build_with_bazel_module/MODULE.bazel +++ b/example/build_with_bazel_module/MODULE.bazel @@ -27,4 +27,15 @@ bazel_dep(name = 'brpc', version = '1.17.0', repo_name = 'apache_brpc') local_path_override( module_name = "brpc", path = "../..", -) \ No newline at end of file +) + +single_version_override( + module_name = "leveldb", + registry = "https://raw.githubusercontent.com/secretflow/bazel-registry/main", +) + +single_version_override( + module_name = "openssl", + version = "3.3.2.bcr.1", + registry = "https://raw.githubusercontent.com/secretflow/bazel-registry/main", +) diff --git a/example/build_with_bazel_module/client.cpp b/example/build_with_bazel_module/client.cpp new file mode 100644 index 0000000000..2ce335073b --- /dev/null +++ b/example/build_with_bazel_module/client.cpp @@ -0,0 +1,89 @@ +// Licensed to the Apache Software Foundation (ASF) under one +// or more contributor license agreements. See the NOTICE file +// distributed with this work for additional information +// regarding copyright ownership. The ASF licenses this file +// to you under the Apache License, Version 2.0 (the +// "License"); you may not use this file except in compliance +// with the License. You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License. + +// A client sending requests to server every 1 second. + +#include +#include +#include +#include "echo.pb.h" + +DEFINE_string(attachment, "", "Carry this along with requests"); +DEFINE_string(protocol, "baidu_std", "Protocol type. Defined in src/brpc/options.proto"); +DEFINE_string(connection_type, "", "Connection type. Available values: single, pooled, short"); +DEFINE_string(server, "0.0.0.0:8002", "IP Address of server"); +DEFINE_int32(timeout_ms, 100, "RPC timeout in milliseconds"); +DEFINE_int32(max_retry, 3, "Max retries(not including the first RPC)"); +DEFINE_int32(interval_ms, 1000, "Milliseconds between consecutive requests"); + +int main(int argc, char* argv[]) { + // Parse gflags. We recommend you to use gflags as well. + GFLAGS_NAMESPACE::ParseCommandLineFlags(&argc, &argv, true); + + // A Channel represents a communication line to a Server. Notice that + // Channel is thread-safe and can be shared by all threads in your program. + brpc::Channel channel; + + // Initialize the channel, NULL means using default options. + brpc::ChannelOptions options; + options.protocol = FLAGS_protocol; + options.connection_type = FLAGS_connection_type; + options.timeout_ms = FLAGS_timeout_ms/*milliseconds*/; + options.max_retry = FLAGS_max_retry; + if (channel.Init(FLAGS_server.c_str(), &options) != 0) { + LOG(ERROR) << "Fail to initialize channel"; + return -1; + } + + // Normally, you should not call a Channel directly, but instead construct + // a stub Service wrapping it. stub can be shared by all threads as well. + example::EchoService_Stub stub(&channel); + + // Send a request and wait for the response every 1 second. + int log_id = 0; + while (!brpc::IsAskedToQuit()) { + // We will receive response synchronously, safe to put variables + // on stack. + example::EchoRequest request; + example::EchoResponse response; + brpc::Controller cntl; + + request.set_message("hello world"); + + cntl.set_log_id(log_id ++); // set by user + // Set attachment which is wired to network directly instead of + // being serialized into protobuf messages. + cntl.request_attachment().append(FLAGS_attachment); + + // Because `done'(last parameter) is NULL, this function waits until + // the response comes back or error occurs(including timedout). + stub.Echo(&cntl, &request, &response, NULL); + if (!cntl.Failed()) { + LOG(INFO) << "Received response from " << cntl.remote_side() + << " to " << cntl.local_side() + << ": " << response.message() << " (attached=" + << cntl.response_attachment() << ")" + << " latency=" << cntl.latency_us() << "us"; + } else { + LOG(WARNING) << cntl.ErrorText(); + } + usleep(FLAGS_interval_ms * 1000L); + } + + LOG(INFO) << "EchoClient is going to quit"; + return 0; +}