gRPC 双向函数 returns RequestType 而不是 ResponseType
gRPC bidirectional function returns RequestType instead of ResponseType
我正在使用官方文档学习 gRPC,但发现 client-streaming 和 bidirectional-streaming 的方法签名非常混乱(两者相同)。
根据文档here,该函数将StreamObserver<ResponseType>
作为输入参数和returns一个StreamObserver<ResponseType>
实例,如下所示:
public StreamObserver<RequestType> bidirectionalStreamingExample(
StreamObserver<ResponseType> responseObserver)
但在我看来,它应该将 RequestType
类型作为输入,returns ResponseType
类型:
public StreamObserver<ResponseType> bidirectionalStreamingExample(
StreamObserver<RequestType> responseObserver)
这让我很困惑,当我在 google 中搜索时,答案没有提示,我其实有点惊讶,我想很多人都会有同样的问题。我在这里遗漏了一些明显的东西吗?为什么 gRPC 会这样定义签名?
您的困惑可能源于习惯于 REST 或非流式框架,其中请求-响应通常映射到函数的参数-return。这里的范式转变是您不再提供请求-响应,而是 channels 来丢弃请求和响应。如果你学过 C 或 C++,这很像从
int get_square_root(int input);
至
void get_square_root(int input, int& output);
看看 output
现在如何成为 参数 ?但如果这完全没有意义(我的错 :-),这里有一条更自然的路径:
服务器流
让我们从 服务器流存根 开始,即使您的最终目标是客户端流。
public void serverStreamingExample(
RequestType request,
StreamObserver<ResponseType> responseObserver)
问:为什么参数列表中是“response”?答:参数列表中的不是 response,而是 channel 以提供最终响应。例如:
public void serverStreamingExample(
RequestType request,
StreamObserver<ResponseType> responseObserver) {
ResponseType response = processRequest(request);
responseObserver.onNext(response); // this is the "return"
responseObserver.onCompleted();
}
为什么?因为,streaming 的要点是保持 channel 的活力,响应可以在其上持续流动。如果您只能 return 1 个响应,仅此而已,功能已完成,那么这不是流。通过提供 频道 ,作为开发人员,您可以选择根据需要传递它,通过 onNext()
提供您想要的尽可能多的响应,直到您满意为止,并且呼叫 onCompleted()
.
客户端流
现在,让我们继续讨论客户端流存根:
public StreamObserver<RequestType> clientStreamingExample(
StreamObserver<ResponseType> responseObserver)
问:等等,什么!我们知道为什么 response 现在在参数列表中,但是它对 return 一个 request 有什么意义呢? A:同样,我们实际上并不是 return 发出 请求 ,而是 通道 供客户端丢弃请求!为什么?因为 client streaming 的要点是允许客户端 分段 提供请求。它不能通过对服务器的单个传统调用来做到这一点。所以这是可以实现的一种方法:
class ClientStreamingExample {
int piecesRcvd = 0;
public StreamObserver<RequestType> myClientStreamingEndpoint(
StreamObserver<ResponseType> responseObserver) {
return new StreamObserver<RequestType>() {
@Override
public void onNext(RequestType requestPiece) {
// do whatever you want with the request pieces
piecesRcvd++;
}
@Override
public void onCompleted() {
// when the client says they're done sending request pieces,
// send them a response back (but you don't have to! or it can
// be conditional!)
ResponseType response =
new ResponseType("received " + piecesRcvd + " pieces");
responseObserver.onNext(response);
responseObserver.onCompleted();
piecesRcvd = 0;
}
@Override
public void onError() {
piecesRcvd = 0;
}
};
}
}
您可能需要花一点时间研究它才能完全理解,但基本上,由于客户端现在可能会发送请求流,因此您必须为每个请求定义 handlers请求片段,以及客户端的处理程序,说明它已完成或出错。 (在我的例子中,我让服务器只在客户端说完成时响应,但你可以自由地做任何你想做的事。你甚至可以让服务器响应甚至 在 客户端之前说已经完成或根本没有回应。)
双向流媒体
这不是真的! :-) 我的意思是,教程只是想指出没有什么能阻止你完全实现上述内容,只是 双方。因此,您最终会得到 2 个应用程序,它们分段发送和接收请求,并发送和接收响应。他们将此设置称为 双向流式传输 ,他们的说法是正确的,但它只是有点误导,因为它在技术上与客户端流式传输没有任何不同。这正是签名相同的原因。恕我直言,教程应该像我在这里提到的那样,而不是重复存根。
可选:只是为了“好玩”...
我们从从
开始的 C++ 类比开始
int get_square_root(int input); // "traditional" request-response
至
void get_square_root(int input, int& output); // server streaming
我们要不要继续这个比喻?当然有。
你好,C++函数指针,我的老朋友...
void (*fnPtr)(int) get_square_root_fn(int& output); // client streaming
及其使用演示(无):
int main() { // aka the client
int result;
void (*fnPtr)(int) = server.get_square_root_fn(result);
fnPtr(2);
std::cout << result << std::endl; // 1.4142 assuming the fn actually does sqrt
}
我正在使用官方文档学习 gRPC,但发现 client-streaming 和 bidirectional-streaming 的方法签名非常混乱(两者相同)。
根据文档here,该函数将StreamObserver<ResponseType>
作为输入参数和returns一个StreamObserver<ResponseType>
实例,如下所示:
public StreamObserver<RequestType> bidirectionalStreamingExample(
StreamObserver<ResponseType> responseObserver)
但在我看来,它应该将 RequestType
类型作为输入,returns ResponseType
类型:
public StreamObserver<ResponseType> bidirectionalStreamingExample(
StreamObserver<RequestType> responseObserver)
这让我很困惑,当我在 google 中搜索时,答案没有提示,我其实有点惊讶,我想很多人都会有同样的问题。我在这里遗漏了一些明显的东西吗?为什么 gRPC 会这样定义签名?
您的困惑可能源于习惯于 REST 或非流式框架,其中请求-响应通常映射到函数的参数-return。这里的范式转变是您不再提供请求-响应,而是 channels 来丢弃请求和响应。如果你学过 C 或 C++,这很像从
int get_square_root(int input);
至
void get_square_root(int input, int& output);
看看 output
现在如何成为 参数 ?但如果这完全没有意义(我的错 :-),这里有一条更自然的路径:
服务器流
让我们从 服务器流存根 开始,即使您的最终目标是客户端流。
public void serverStreamingExample(
RequestType request,
StreamObserver<ResponseType> responseObserver)
问:为什么参数列表中是“response”?答:参数列表中的不是 response,而是 channel 以提供最终响应。例如:
public void serverStreamingExample(
RequestType request,
StreamObserver<ResponseType> responseObserver) {
ResponseType response = processRequest(request);
responseObserver.onNext(response); // this is the "return"
responseObserver.onCompleted();
}
为什么?因为,streaming 的要点是保持 channel 的活力,响应可以在其上持续流动。如果您只能 return 1 个响应,仅此而已,功能已完成,那么这不是流。通过提供 频道 ,作为开发人员,您可以选择根据需要传递它,通过 onNext()
提供您想要的尽可能多的响应,直到您满意为止,并且呼叫 onCompleted()
.
客户端流
现在,让我们继续讨论客户端流存根:
public StreamObserver<RequestType> clientStreamingExample(
StreamObserver<ResponseType> responseObserver)
问:等等,什么!我们知道为什么 response 现在在参数列表中,但是它对 return 一个 request 有什么意义呢? A:同样,我们实际上并不是 return 发出 请求 ,而是 通道 供客户端丢弃请求!为什么?因为 client streaming 的要点是允许客户端 分段 提供请求。它不能通过对服务器的单个传统调用来做到这一点。所以这是可以实现的一种方法:
class ClientStreamingExample {
int piecesRcvd = 0;
public StreamObserver<RequestType> myClientStreamingEndpoint(
StreamObserver<ResponseType> responseObserver) {
return new StreamObserver<RequestType>() {
@Override
public void onNext(RequestType requestPiece) {
// do whatever you want with the request pieces
piecesRcvd++;
}
@Override
public void onCompleted() {
// when the client says they're done sending request pieces,
// send them a response back (but you don't have to! or it can
// be conditional!)
ResponseType response =
new ResponseType("received " + piecesRcvd + " pieces");
responseObserver.onNext(response);
responseObserver.onCompleted();
piecesRcvd = 0;
}
@Override
public void onError() {
piecesRcvd = 0;
}
};
}
}
您可能需要花一点时间研究它才能完全理解,但基本上,由于客户端现在可能会发送请求流,因此您必须为每个请求定义 handlers请求片段,以及客户端的处理程序,说明它已完成或出错。 (在我的例子中,我让服务器只在客户端说完成时响应,但你可以自由地做任何你想做的事。你甚至可以让服务器响应甚至 在 客户端之前说已经完成或根本没有回应。)
双向流媒体
这不是真的! :-) 我的意思是,教程只是想指出没有什么能阻止你完全实现上述内容,只是 双方。因此,您最终会得到 2 个应用程序,它们分段发送和接收请求,并发送和接收响应。他们将此设置称为 双向流式传输 ,他们的说法是正确的,但它只是有点误导,因为它在技术上与客户端流式传输没有任何不同。这正是签名相同的原因。恕我直言,教程应该像我在这里提到的那样,而不是重复存根。
可选:只是为了“好玩”...
我们从从
开始的 C++ 类比开始int get_square_root(int input); // "traditional" request-response
至
void get_square_root(int input, int& output); // server streaming
我们要不要继续这个比喻?当然有。
你好,C++函数指针,我的老朋友...
void (*fnPtr)(int) get_square_root_fn(int& output); // client streaming
及其使用演示(无):
int main() { // aka the client
int result;
void (*fnPtr)(int) = server.get_square_root_fn(result);
fnPtr(2);
std::cout << result << std::endl; // 1.4142 assuming the fn actually does sqrt
}