You are viewing a plain text version of this content. The canonical link for it is here.
Posted to dev@brpc.apache.org by ji...@apache.org on 2024/09/08 16:03:06 UTC

(brpc) branch master updated: Support arena rpc pb message factory (#2751)

This is an automated email from the ASF dual-hosted git repository.

jiashunzhu pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/brpc.git


The following commit(s) were added to refs/heads/master by this push:
     new 9643150b Support arena rpc pb message factory (#2751)
9643150b is described below

commit 9643150b8f40c06979cb3ba8b9a170ee326d20bd
Author: Bright Chen <ch...@foxmail.com>
AuthorDate: Mon Sep 9 00:03:00 2024 +0800

    Support arena rpc pb message factory (#2751)
    
    * Support arena rpc pb message factory
    
    * Update server document
    
    * Fix comment
---
 docs/cn/server.md                     |  20 ++++++-
 docs/en/server.md                     |  43 ++++++++++++++
 src/brpc/policy/http_rpc_protocol.cpp |  36 ++++++++----
 src/brpc/rpc_pb_message_factory.cpp   |   1 -
 src/brpc/rpc_pb_message_factory.h     |  87 ++++++++++++++++++++++++++-
 test/brpc_server_unittest.cpp         | 108 ++++++++++++++++++++++++++++------
 test/v3.proto                         |  38 ++++++++++++
 7 files changed, 298 insertions(+), 35 deletions(-)

diff --git a/docs/cn/server.md b/docs/cn/server.md
index 5e3fc42a..9606ab1d 100644
--- a/docs/cn/server.md
+++ b/docs/cn/server.md
@@ -1017,7 +1017,7 @@ public:
 
 Server默认使用`DefaultRpcPBMessageFactory`。它是一个简单的工厂类,通过`new`来创建请求/响应message和`delete`来销毁请求/响应message。
 
-如果用户希望自定义创建销毁机制,可以实现`RpcPBMessages`(请求/响应message的封装)和`RpcPBMessageFactory`(工厂类),并通过`ServerOptions.rpc_pb_message_factory`。
+如果用户希望自定义创建销毁机制,可以实现`RpcPBMessages`(请求/响应message的封装)和`RpcPBMessageFactory`(工厂类),并设置`ServerOptions.rpc_pb_message_factory`为自定义的`RpcPBMessageFactory`。注意:server启动后,server拥有了`RpcPBMessageFactory`的所有权。
 
 接口如下:
 
@@ -1027,7 +1027,9 @@ Server默认使用`DefaultRpcPBMessageFactory`。它是一个简单的工厂类
 class RpcPBMessages {
 public:
     virtual ~RpcPBMessages() = default;
+    // Get protobuf request message.
     virtual google::protobuf::Message* Request() = 0;
+    // Get protobuf response message.
     virtual google::protobuf::Message* Response() = 0;
 };
 
@@ -1035,12 +1037,26 @@ public:
 class RpcPBMessageFactory {
 public:
     virtual ~RpcPBMessageFactory() = default;
+
+    // Get `RpcPBMessages' according to `service' and `method'.
+    // Common practice to create protobuf message:
+    // service.GetRequestPrototype(&method).New() -> request;
+    // service.GetResponsePrototype(&method).New() -> response.
     virtual RpcPBMessages* Get(const ::google::protobuf::Service& service,
                                const ::google::protobuf::MethodDescriptor& method) = 0;
-    virtual void Return(RpcPBMessages* protobuf_message) = 0;
+    // Return `RpcPBMessages' to factory.
+    virtual void Return(RpcPBMessages* messages) = 0;
 };
 ```
 
+### Protobuf arena
+
+Protobuf arena是一种Protobuf message内存管理机制,有着提高内存分配效率、减少内存碎片、对缓存友好等优点。详细信息见[C++ Arena Allocation Guide](https://protobuf.dev/reference/cpp/arenas/)。
+
+如果用户希望使用protobuf arena来管理Protobuf message内存,可以设置`ServerOptions.rpc_pb_message_factory = brpc::GetArenaRpcPBMessageFactory();`,使用默认的`start_block_size`(256 bytes)和`max_block_size`(8192 bytes)来创建arena。用户可以调用`brpc::GetArenaRpcPBMessageFactory<StartBlockSize, MaxBlockSize>();`自定义arena大小。
+
+注意:从Protobuf v3.14.0开始,[默认开启arena](https://github.com/protocolbuffers/protobuf/releases/tag/v3.14.0https://github.com/protocolbuffers/protobuf/releases/tag/v3.14.0)。但是Protobuf v3.14.0之前的版本,用户需要再proto文件中加上选项:`option cc_enable_arenas = true;`,所以为了兼容性,可以统一都加上该选项。
+
 # FAQ
 
 ### Q: Fail to write into fd=1865 SocketId=8905@10.208.245.43:54742@8230: Got EOF是什么意思
diff --git a/docs/en/server.md b/docs/en/server.md
index 62480d28..1c2923a6 100644
--- a/docs/en/server.md
+++ b/docs/en/server.md
@@ -1008,6 +1008,49 @@ public:
         ...
 ```
 
+## RPC Protobuf message factory
+
+`DefaultRpcPBMessageFactory' is used at server-side by default. It is a simple factory class that uses `new' to create request/response messages and `delete' to destroy request/response messages. Currently, the baidu_std protocol and HTTP protocol support this feature.
+
+Users can implement `RpcPBMessages' (encapsulation of request/response message) and `RpcPBMessageFactory' (factory class) to customize the creation and destruction mechanism of protobuf message, and then set to `ServerOptions.rpc_pb_message_factory`. Note: After the server is started, the server owns the `RpcPBMessageFactory`.
+
+The interface is as follows:
+
+```c++
+// Inherit this class to customize rpc protobuf messages,
+// include request and response.
+class RpcPBMessages {
+public:
+    virtual ~RpcPBMessages() = default;
+    // Get protobuf request message.
+    virtual google::protobuf::Message* Request() = 0;
+    // Get protobuf response message.
+    virtual google::protobuf::Message* Response() = 0;
+};
+
+// Factory to manage `RpcPBMessages'.
+class RpcPBMessageFactory {
+public:
+    virtual ~RpcPBMessageFactory() = default;
+    // Get `RpcPBMessages' according to `service' and `method'.
+    // Common practice to create protobuf message:
+    // service.GetRequestPrototype(&method).New() -> request;
+    // service.GetResponsePrototype(&method).New() -> response.
+    virtual RpcPBMessages* Get(const ::google::protobuf::Service& service,
+                               const ::google::protobuf::MethodDescriptor& method) = 0;
+    // Return `RpcPBMessages' to factory.
+    virtual void Return(RpcPBMessages* protobuf_message) = 0;
+};
+```
+
+### Protobuf arena
+
+Protobuf arena is a Protobuf message memory management mechanism with the advantages of improving memory allocation efficiency, reducing memory fragmentation, and being cache-friendly. For more information, see [C++ Arena Allocation Guide](https://protobuf.dev/reference/cpp/arenas/).
+
+Users can set `ServerOptions.rpc_pb_message_factory = brpc::GetArenaRpcPBMessageFactory();` to manage Protobuf message memory,  with the default `start_block_size` (256 bytes) and `max_block_size` (8192 bytes). Alternatively, users can use `brpc::GetArenaRpcPBMessageFactory<StartBlockSize, MaxBlockSize>();` to customize the arena size.
+
+Note: Since Protocol Buffers v3.14.0, Arenas are now unconditionally enabled. However, for versions prior to Protobuf v3.14.0, users need to add the option `option cc_enable_arenas = true;` to the proto file. so for compatibility, this option can be added uniformly.
+
 # FAQ
 
 ### Q: Fail to write into fd=1865 SocketId=8905@10.208.245.43:54742@8230: Got EOF
diff --git a/src/brpc/policy/http_rpc_protocol.cpp b/src/brpc/policy/http_rpc_protocol.cpp
index 52fff2c9..f8820d44 100644
--- a/src/brpc/policy/http_rpc_protocol.cpp
+++ b/src/brpc/policy/http_rpc_protocol.cpp
@@ -710,30 +710,33 @@ public:
     HttpResponseSender()
         : HttpResponseSender(NULL) {}
     explicit HttpResponseSender(Controller* cntl/*own*/)
-        : _cntl(cntl), _method_status(NULL), _received_us(0), _h2_stream_id(-1) {}
+        : _cntl(cntl)
+        , _messages(NULL)
+        , _method_status(NULL)
+        , _received_us(0)
+        , _h2_stream_id(-1) {}
+
     HttpResponseSender(HttpResponseSender&& s) noexcept
         : _cntl(std::move(s._cntl))
-        , _req(std::move(s._req))
-        , _res(std::move(s._res))
+        , _messages(s._messages)
         , _method_status(s._method_status)
         , _received_us(s._received_us)
         , _h2_stream_id(s._h2_stream_id) {
+        s._messages = NULL;
         s._method_status = NULL;
         s._received_us = 0;
         s._h2_stream_id = -1;
     }
     ~HttpResponseSender();
 
-    void own_request(google::protobuf::Message* req) { _req.reset(req); }
-    void own_response(google::protobuf::Message* res) { _res.reset(res); }
+    void set_messages(RpcPBMessages* messages) { _messages = messages; }
     void set_method_status(MethodStatus* ms) { _method_status = ms; }
     void set_received_us(int64_t t) { _received_us = t; }
     void set_h2_stream_id(int id) { _h2_stream_id = id; }
 
 private:
     std::unique_ptr<Controller, LogErrorTextAndDelete> _cntl;
-    std::unique_ptr<google::protobuf::Message> _req;
-    std::unique_ptr<google::protobuf::Message> _res;
+    RpcPBMessages* _messages;
     MethodStatus* _method_status;
     int64_t _received_us;
     int _h2_stream_id;
@@ -743,7 +746,8 @@ class HttpResponseSenderAsDone : public google::protobuf::Closure {
 public:
     explicit HttpResponseSenderAsDone(HttpResponseSender* s) : _sender(std::move(*s)) {}
     void Run() override {
-        _sender._cntl->CallAfterRpcResp(_sender._req.get(), _sender._res.get());
+        _sender._cntl->CallAfterRpcResp(
+            _sender._messages->Request(), _sender._messages->Response());
         delete this;
     }
 
@@ -752,6 +756,12 @@ private:
 };
 
 HttpResponseSender::~HttpResponseSender() {
+    // Return messages to factory at the end.
+    BRPC_SCOPE_EXIT {
+        if (NULL != _messages) {
+            _cntl->server()->options().rpc_pb_message_factory->Return(_messages);
+        }
+    };
     Controller* cntl = _cntl.get();
     if (cntl == NULL) {
         return;
@@ -763,7 +773,7 @@ HttpResponseSender::~HttpResponseSender() {
     }
     ConcurrencyRemover concurrency_remover(_method_status, cntl, _received_us);
     Socket* socket = accessor.get_sending_socket();
-    const google::protobuf::Message* res = _res.get();
+    const google::protobuf::Message* res = NULL != _messages ? _messages->Response() : NULL;
     
     if (cntl->IsCloseConnection()) {
         socket->SetFailed();
@@ -1488,10 +1498,10 @@ void ProcessHttpRequest(InputMessageBase *msg) {
     google::protobuf::Service* svc = sp->service;
     const google::protobuf::MethodDescriptor* method = sp->method;
     accessor.set_method(method);
-    google::protobuf::Message* req = svc->GetRequestPrototype(method).New();
-    resp_sender.own_request(req);
-    google::protobuf::Message* res = svc->GetResponsePrototype(method).New();
-    resp_sender.own_response(res);
+    RpcPBMessages* messages = server->options().rpc_pb_message_factory->Get(*svc, *method);;
+    resp_sender.set_messages(messages);
+    google::protobuf::Message* req = messages->Request();
+    google::protobuf::Message* res = messages->Response();
 
     if (__builtin_expect(!req || !res, 0)) {
         PLOG(FATAL) << "Fail to new req or res";
diff --git a/src/brpc/rpc_pb_message_factory.cpp b/src/brpc/rpc_pb_message_factory.cpp
index 828a289d..27e2457e 100644
--- a/src/brpc/rpc_pb_message_factory.cpp
+++ b/src/brpc/rpc_pb_message_factory.cpp
@@ -16,7 +16,6 @@
 // under the License.
 
 #include "brpc/rpc_pb_message_factory.h"
-#include "butil/object_pool.h"
 
 namespace brpc {
 
diff --git a/src/brpc/rpc_pb_message_factory.h b/src/brpc/rpc_pb_message_factory.h
index 0da0ff2a..52787be1 100644
--- a/src/brpc/rpc_pb_message_factory.h
+++ b/src/brpc/rpc_pb_message_factory.h
@@ -21,6 +21,8 @@
 #include <google/protobuf/service.h>
 #include <google/protobuf/descriptor.h>
 #include <google/protobuf/message.h>
+#include <google/protobuf/arena.h>
+#include "butil/object_pool.h"
 
 namespace brpc {
 
@@ -29,7 +31,9 @@ namespace brpc {
 class RpcPBMessages {
 public:
     virtual ~RpcPBMessages() = default;
+    // Get protobuf request message.
     virtual google::protobuf::Message* Request() = 0;
+    // Get protobuf response message.
     virtual google::protobuf::Message* Response() = 0;
 };
 
@@ -37,9 +41,14 @@ public:
 class RpcPBMessageFactory {
 public:
     virtual ~RpcPBMessageFactory() = default;
+    // Get `RpcPBMessages' according to `service' and `method'.
+    // Common practice to create protobuf message:
+    // service.GetRequestPrototype(&method).New() -> request;
+    // service.GetResponsePrototype(&method).New() -> response.
     virtual RpcPBMessages* Get(const ::google::protobuf::Service& service,
                                const ::google::protobuf::MethodDescriptor& method) = 0;
-    virtual void Return(RpcPBMessages* protobuf_message) = 0;
+    // Return `RpcPBMessages' to factory.
+    virtual void Return(RpcPBMessages* messages) = 0;
 };
 
 class DefaultRpcPBMessageFactory : public RpcPBMessageFactory {
@@ -49,6 +58,80 @@ public:
     void Return(RpcPBMessages* messages) override;
 };
 
+namespace internal {
+
+// Allocate protobuf message from arena.
+// The arena is created with `StartBlockSize' and `MaxBlockSize' options.
+// For more details, see `google::protobuf::ArenaOptions'.
+template<size_t StartBlockSize, size_t MaxBlockSize>
+struct ArenaRpcPBMessages : public RpcPBMessages {
+    struct ArenaOptionsWrapper {
+    public:
+        ArenaOptionsWrapper() {
+            options.start_block_size = StartBlockSize;
+            options.max_block_size = MaxBlockSize;
+        }
+
+    private:
+    friend struct ArenaRpcPBMessages;
+        ::google::protobuf::ArenaOptions options;
+    };
+
+    explicit ArenaRpcPBMessages(ArenaOptionsWrapper options_wrapper)
+        : arena(options_wrapper.options)
+        , request(NULL)
+        , response(NULL) {}
+
+    ::google::protobuf::Message* Request() override { return request; }
+    ::google::protobuf::Message* Response() override { return response; }
+
+    ::google::protobuf::Arena arena;
+    ::google::protobuf::Message* request;
+    ::google::protobuf::Message* response;
+};
+
+template<size_t StartBlockSize, size_t MaxBlockSize>
+class ArenaRpcPBMessageFactory : public RpcPBMessageFactory {
+    typedef ::brpc::internal::ArenaRpcPBMessages<StartBlockSize, MaxBlockSize>
+        ArenaRpcPBMessages;
+public:
+    ArenaRpcPBMessageFactory() {
+        _arena_options.start_block_size = StartBlockSize;
+        _arena_options.max_block_size = MaxBlockSize;
+    }
+
+    RpcPBMessages* Get(const ::google::protobuf::Service& service,
+                       const ::google::protobuf::MethodDescriptor& method) override {
+        typename ArenaRpcPBMessages::ArenaOptionsWrapper options_wrapper;
+        auto messages = butil::get_object<ArenaRpcPBMessages>(options_wrapper);
+        messages->request = service.GetRequestPrototype(&method).New(&messages->arena);
+        messages->response = service.GetResponsePrototype(&method).New(&messages->arena);
+        return messages;
+    }
+
+    void Return(RpcPBMessages* messages) override {
+        auto arena_messages = static_cast<ArenaRpcPBMessages*>(messages);
+        arena_messages->request = NULL;
+        arena_messages->response = NULL;
+        butil::return_object(arena_messages);
+    }
+
+private:
+    ::google::protobuf::ArenaOptions _arena_options;
+};
+
+}
+
+template<size_t StartBlockSize, size_t MaxBlockSize>
+RpcPBMessageFactory* GetArenaRpcPBMessageFactory() {
+    return new ::brpc::internal::ArenaRpcPBMessageFactory<StartBlockSize, MaxBlockSize>();
+}
+
+BUTIL_FORCE_INLINE RpcPBMessageFactory* GetArenaRpcPBMessageFactory() {
+    // Default arena options, same as `google::protobuf::ArenaOptions'.
+    return GetArenaRpcPBMessageFactory<256, 8192>();
+}
+
 } // namespace brpc
 
-#endif //BRPC_RPC_PB_MESSAGE_FACTORY_H
+#endif // BRPC_RPC_PB_MESSAGE_FACTORY_H
diff --git a/test/brpc_server_unittest.cpp b/test/brpc_server_unittest.cpp
index 5f06887a..c740a5d5 100644
--- a/test/brpc_server_unittest.cpp
+++ b/test/brpc_server_unittest.cpp
@@ -56,6 +56,7 @@
 #include "echo.pb.h"
 #include "v1.pb.h"
 #include "v2.pb.h"
+#include "v3.pb.h"
 
 int main(int argc, char* argv[]) {
     testing::InitGoogleTest(&argc, argv);
@@ -350,6 +351,18 @@ public:
     butil::atomic<int> ncalled;
 };
 
+class EchoServiceV3 : public v3::EchoService {
+public:
+    void Echo(::google::protobuf::RpcController*,
+              const v3::EchoRequest* request,
+              v3::EchoResponse* response,
+              ::google::protobuf::Closure* done) override {
+        brpc::ClosureGuard done_guard(done);
+        ASSERT_EQ(request->message(), EXP_REQUEST);
+        response->set_message(EXP_RESPONSE);
+    }
+};
+
 TEST_F(ServerTest, empty_enabled_protocols) {
     butil::EndPoint ep;
     ASSERT_EQ(0, str2endpoint("127.0.0.1:8613", &ep));
@@ -1782,8 +1795,8 @@ public:
     brpc::RpcPBMessages* Get(const google::protobuf::Service& service,
                              const google::protobuf::MethodDescriptor& method) override {
         auto messages = butil::get_object<DefaultRpcPBMessages>();
-        auto request = butil::get_object<test::EchoRequest>();
-        auto response = butil::get_object<test::EchoResponse>();
+        auto request = butil::get_object<v1::EchoRequest>();
+        auto response = butil::get_object<v1::EchoResponse>();
         request->clear_message();
         response->clear_message();
         messages->request = request;
@@ -1793,8 +1806,8 @@ public:
 
     void Return(brpc::RpcPBMessages* messages) override {
         auto test_messages = static_cast<DefaultRpcPBMessages*>(messages);
-        butil::return_object(static_cast<test::EchoRequest*>(test_messages->request));
-        butil::return_object(static_cast<test::EchoResponse*>(test_messages->response));
+        butil::return_object(static_cast<v1::EchoRequest*>(test_messages->request));
+        butil::return_object(static_cast<v1::EchoResponse*>(test_messages->response));
         test_messages->request = NULL;
         test_messages->response = NULL;
         butil::return_object(test_messages);
@@ -1805,24 +1818,85 @@ TEST_F(ServerTest, rpc_pb_message_factory) {
     butil::EndPoint ep;
     ASSERT_EQ(0, str2endpoint("127.0.0.1:8613", &ep));
     brpc::Server server;
-    EchoServiceImpl service;
+    EchoServiceV1 service;
     ASSERT_EQ(0, server.AddService(&service, brpc::SERVER_DOESNT_OWN_SERVICE));
     brpc::ServerOptions opt;
     opt.rpc_pb_message_factory = new TestRpcPBMessageFactory;
     ASSERT_EQ(0, server.Start(ep, &opt));
 
-    brpc::Channel chan;
-    brpc::ChannelOptions copt;
-    copt.protocol = "baidu_std";
-    ASSERT_EQ(0, chan.Init(ep, &copt));
-    brpc::Controller cntl;
-    test::EchoRequest req;
-    test::EchoResponse res;
-    req.set_message(EXP_REQUEST);
-    test::EchoService_Stub stub(&chan);
-    stub.Echo(&cntl, &req, &res, NULL);
-    ASSERT_FALSE(cntl.Failed()) << cntl.ErrorText();
-    ASSERT_EQ(EXP_RESPONSE, res.message());
+    brpc::Channel baidu_chan;
+    brpc::ChannelOptions baidu_copt;
+    baidu_copt.protocol = "baidu_std";
+    ASSERT_EQ(0, baidu_chan.Init(ep, &baidu_copt));
+    for (int i = 0; i < 1000; ++i) {
+        brpc::Controller cntl;
+        v1::EchoRequest req;
+        v1::EchoResponse res;
+        req.set_message(EXP_REQUEST);
+        v1::EchoService_Stub stub(&baidu_chan);
+        stub.Echo(&cntl, &req, &res, NULL);
+        ASSERT_FALSE(cntl.Failed()) << cntl.ErrorText();
+        ASSERT_EQ(EXP_REQUEST + "_v1", res.message());
+    }
+
+    brpc::Channel http_chan;
+    brpc::ChannelOptions http_copt;
+    http_copt.protocol = "http";
+    ASSERT_EQ(0, http_chan.Init(ep, &http_copt));
+    for (int i = 0; i < 1000; ++i) {
+        brpc::Controller cntl;
+        cntl.request_attachment().append(
+            butil::string_printf(R"({"message":"%s"})", EXP_REQUEST.c_str()));
+        v1::EchoService_Stub stub(&http_chan);
+        stub.Echo(&cntl, NULL, NULL, NULL);
+        ASSERT_FALSE(cntl.Failed()) << cntl.ErrorText();
+        ASSERT_EQ(butil::string_printf(R"({"message":"%s_v1"})", EXP_REQUEST.c_str()),
+                  cntl.response_attachment().to_string());
+    }
+
+    ASSERT_EQ(0, server.Stop(0));
+    ASSERT_EQ(0, server.Join());
+}
+
+TEST_F(ServerTest, arena_rpc_pb_message_factory) {
+    butil::EndPoint ep;
+    ASSERT_EQ(0, str2endpoint("127.0.0.1:8613", &ep));
+    brpc::Server server;
+    EchoServiceV3 service;
+    ASSERT_EQ(0, server.AddService(&service, brpc::SERVER_DOESNT_OWN_SERVICE));
+    brpc::ServerOptions opt;
+    opt.rpc_pb_message_factory = brpc::GetArenaRpcPBMessageFactory();
+    ASSERT_EQ(0, server.Start(ep, &opt));
+
+    brpc::Channel baidu_chan;
+    brpc::ChannelOptions baidu_copt;
+    baidu_copt.protocol = "baidu_std";
+    ASSERT_EQ(0, baidu_chan.Init(ep, &baidu_copt));
+    for (int i = 0; i < 1000; ++i) {
+        brpc::Controller cntl;
+        v3::EchoRequest req;
+        v3::EchoResponse res;
+        req.set_message(EXP_REQUEST);
+        v3::EchoService_Stub stub(&baidu_chan);
+        stub.Echo(&cntl, &req, &res, NULL);
+        ASSERT_FALSE(cntl.Failed()) << cntl.ErrorText();
+        ASSERT_EQ(EXP_RESPONSE, res.message());
+    }
+
+    brpc::Channel http_chan;
+    brpc::ChannelOptions http_copt;
+    http_copt.protocol = "http";
+    ASSERT_EQ(0, http_chan.Init(ep, &http_copt));
+    for (int i = 0; i < 1000; ++i) {
+        brpc::Controller cntl;
+        cntl.request_attachment().append(
+            butil::string_printf(R"({"message":"%s"})", EXP_REQUEST.c_str()));
+        v3::EchoService_Stub stub(&http_chan);
+        stub.Echo(&cntl, NULL, NULL, NULL);
+        ASSERT_FALSE(cntl.Failed()) << cntl.ErrorText();
+        ASSERT_EQ(butil::string_printf(R"({"message":"%s"})", EXP_RESPONSE.c_str()),
+            cntl.response_attachment().to_string());
+    }
 
     ASSERT_EQ(0, server.Stop(0));
     ASSERT_EQ(0, server.Join());
diff --git a/test/v3.proto b/test/v3.proto
new file mode 100644
index 00000000..b920832a
--- /dev/null
+++ b/test/v3.proto
@@ -0,0 +1,38 @@
+// 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.
+
+syntax="proto3";
+
+package v3;
+
+option cc_generic_services = true;
+// https://github.com/protocolbuffers/protobuf/releases/tag/v3.14.0
+// Since Protocol Buffers v3.14.0, Arenas are now unconditionally enabled.
+// cc_enable_arenas no longer has any effect.
+option cc_enable_arenas = true;
+
+message EchoRequest {
+    string message = 1;
+};
+
+message EchoResponse {
+    string message = 1;
+};
+
+service EchoService {
+    rpc Echo(EchoRequest) returns (EchoResponse);
+};


---------------------------------------------------------------------
To unsubscribe, e-mail: dev-unsubscribe@brpc.apache.org
For additional commands, e-mail: dev-help@brpc.apache.org