为了生成我们期望的 RPC 接口,我们开发了相应的 Protobuf 插件来生成相关的代码。
Protocol Buffers默认通过cc_generate_services = true;
生成的接口较为简陋,且没有对流式RPC直接提供支持。因此我们提供了自己的pb插件用于生成这些类。
我们参考trpc的trpc_proto_library
、grpc的cc_grpc_library
的实现,提供了必要的protoc
插件来生成适配于Flare的接口(*.flare.pb.{h,cc}
),下面列出了必要的修改。
与trpc/grpc默认会同时生成*.pb.{h,cc}
、*.(trpc|grpc).{h,cc}
不同,cc_flare_library
只会生成*.flare.{h,cc}
。按照grpc的注释,后续cc_grpc_library.grpc_only
会默认为True
,即默认只生成*.grpc.{h,cc}
。因此,长期而言,flare将和grpc保持一致。
BUILD
文件引用//flare/tools/build_rules.bld
:
load('//flare/tools/build_rules.bld', 'cc_flare_library')
需要注意的是,cc_flare_library
只会生成*.flare.pb.{h,cc}
,因此需要先通过proto_library
定义相关的*.pb.{h,cc}
的目标:
我们通常建议cc_flare_library
的目标的name
以_flare
结尾。
# Protocol Buffers message target.
proto_library(
name = 'echo_service_proto',
srcs = 'echo_service.proto'
)
# Flare-friendly target.
cc_flare_library(
name = 'echo_service_proto_flare',
srcs = 'echo_service.proto',
deps = [
':echo_service_proto',
],
)
如下代码给出了一个使用relay_service.flare.pb.h
的示例:
load('//flare/tools/build_rules.bld', 'cc_flare_library')
# Generates `relay_service.pb.{h,cc}`.
proto_library(
name = 'relay_service_proto',
srcs = 'relay_service.proto',
deps = [
'//flare/rpc:rpc_options_proto',
],
)
# Generates `relay_service.flare.pb.{h,cc}`.
cc_flare_library(
name = 'relay_service_flare_proto',
srcs = 'relay_service.proto',
deps = [
':relay_service_proto',
]
)
# Uses `relay_service.flare.pb.h`.
cc_binary(
name = 'relay_server',
srcs = 'relay_server.cc',
deps = [
':relay_service_proto_flare',
# ...
]
)
以如下proto为例:
syntax = "proto3";
package echo;
message EchoRequest {
string body = 1;
}
message EchoResponse {
string body = 2;
}
service EchoService {
rpc Echo(EchoRequest) returns(EchoResponse);
rpc EchoStreamRequest(stream EchoRequest) returns (EchoResponse);
rpc EchoStreamResponse(EchoRequest) returns (stream EchoResponse);
rpc EchoStreamBoth(stream EchoRequest) returns (stream EchoResponse);
}
Flare插件同样支持已有的gdt.streaming_response
选项,其作用于returns (stream Xxx)
相同。对于新代码,我们推荐使用Protocol Buffers内置的stream
关键字。
Flare插件会生成如下接口(有所简化,省略掉部分非对外的细节):
class SyncEchoService {
public:
virtual void Echo(const EchoRequest& request, EchoResponse* response,
flare::RpcServerController* controller);
virtual void EchoStreamRequest(flare::StreamReader<EchoRequest> reader,
EchoResponse* response,
flare::RpcServerController* controller);
virtual void EchoStreamResponse(const EchoRequest& request,
flare::StreamWriter<EchoResponse> writer,
flare::RpcServerController* controller);
virtual void EchoStreamBoth(flare::StreamReader<EchoRequest> reader,
flare::StreamWriter<EchoResponse> writer,
flare::RpcServerController* controller);
};
各个接口输入输出含义自明,不再赘述。
对于客户端,Flare的插件会分别生成同步的接口及异步的接口(基于Future<...>
)。
如果需要并发请求多个后端,无论是否需要同步等待所有后端返回,请参考“异步接口”一节。
我们生成的Stub
类增加了一个可以直接接受uri
(对应于RpcChannel::Open
的相应参数)的重载版本。这允许用户将“打开channel+构造stub”合为一步,简化开发:
EchoService_SyncStub stub("flare://some.polaris.address");
// Use `stub` here.
如果传入的地址打开失败,那么在这个stub
上后续进行的RPC均会以rpc::STATUS_INVALID_CHANNEL
失败。
当需要请求单个后端或串行请求多个后端时,可以使用同步接口。
Flare生成的接口如下(有所简化,省略了部分非对外的细节):
class EchoService_SyncStub {
public:
flare::Expected<EchoResponse, flare::Status> Echo(
const EchoRequest& request, flare::RpcClientController* controller);
std::pair<flare::StreamReader<EchoResponse>,
flare::StreamWriter<EchoRequest>>
EchoStreamRequest(flare::RpcClientController* controller);
flare::StreamReader<EchoResponse> EchoStreamResponse(
const EchoRequest& request, flare::RpcClientController* controller);
std::pair<flare::StreamReader<EchoResponse>,
flare::StreamWriter<EchoRequest>>
EchoStreamBoth(flare::RpcClientController* controller);
};
flare::Expected
与P0323R7 std::expected基本相同,出错时可直接通过flare::Expected<T, flare::Status>
得到错误码。
异步接口并不意味着只能用于异步场景。
我们的fiber运行时可以和Future<...>
配合使用,因此,实际上我们预期更多使用异步接口的场景通常是用于并发请求多个后端(并通过flare::fiber::BlockingGet(...)
+ flare::WhenAll
等待多个RPC完成)。
尽管可以通过flare::fiber::Async
配合同步接口来模拟异步调用,由于fiber本身是一种受vm.max_map_count
系统参数限制的资源,我们不建议这么做。另外,Flare插件生成的异步接口的性能应当也优于通过模拟的实现。
Flare生成的接口如下(有所简化,省略了部分非对外的细节):
class EchoService_AsyncStub {
public:
flare::Future<flare::Expected<EchoResponse, flare::Status>> Echo(
const EchoRequest& request, flare::RpcClientController* controller);
std::pair<flare::AsyncStreamReader<EchoResponse>,
flare::AsyncStreamWriter<EchoRequest>>
EchoStreamRequest(flare::RpcClientController* controller);
flare::AsyncStreamReader<EchoResponse> EchoStreamResponse(
const EchoRequest& request, flare::RpcClientController* controller);
std::pair<flare::AsyncStreamReader<EchoResponse>,
flare::AsyncStreamWriter<EchoRequest>>
EchoStreamBoth(flare::RpcClientController* controller);
};
可以注意到,除了通过Future<T>
代替T
之外,异步接口和同步基本相同。
另外,关于如何使用异步接口来同步并发调用多个后端,此处给出一个示例:
// Initialize stubs and open channels to different backends.
EchoService_AsyncStub stub1("flare://some.polaris.address-1");
EchoService_AsyncStub stub2("flare://some.polaris.address-2");
// Prepare requests.
EchoRequest req1, req2;
req1.set_body("body1");
req2.set_body("body2");
// Issue RPCs asynchronously.
flare::RpcClientController ctlr1, ctlr2;
auto f1 = stub1.Echo(req1, &ctlr1);
auto f2 = stub2.Echo(req2, &ctlr2);
// Wait for all RPCs.
auto&& [res1, res2] = flare::fiber::BlockingGet(flare::WhenAll(&f1, &f2));
// Now both `res1` and `res2` have finished (either successfully or with an
// error), and are ready for inspection.
cc_generate_services
的兼容性我们的插件生成类时会避免和cc_generate_services
生成的类重名。此外,我们的插件检测到未指定cc_generate_services = true;
时,会额外的*.flare.pb.h
中生成与cc_generate_services
接口相同的类以供使用。
gdt_future_rpc
的兼容性Flare的pb插件同样会生成和gdt_future_rpc
产出相同的接口。
Вы можете оставить комментарий после Вход в систему
Неприемлемый контент может быть отображен здесь и не будет показан на странице. Вы можете проверить и изменить его с помощью соответствующей функции редактирования.
Если вы подтверждаете, что содержание не содержит непристойной лексики/перенаправления на рекламу/насилия/вульгарной порнографии/нарушений/пиратства/ложного/незначительного или незаконного контента, связанного с национальными законами и предписаниями, вы можете нажать «Отправить» для подачи апелляции, и мы обработаем ее как можно скорее.
Опубликовать ( 0 )