Kotlin 如何实现消息中重复的gRPC/Proto端点(即请求和响应中的嵌套类型)
目标:我想编写一个微服务代码,公开一个端点,该端点接收并响应一条重复的消息。我试着应用我从中学到的知识,并编写了这个原型:Kotlin 如何实现消息中重复的gRPC/Proto端点(即请求和响应中的嵌套类型),kotlin,protocol-buffers,grpc,micronaut,grpc-java,Kotlin,Protocol Buffers,Grpc,Micronaut,Grpc Java,目标:我想编写一个微服务代码,公开一个端点,该端点接收并响应一条重复的消息。我试着应用我从中学到的知识,并编写了这个原型: syntax = "proto3"; option java_multiple_files = true; option java_package = "com.mybank.endpoint"; option java_outer_classname = "TransactionsProto"; option
syntax = "proto3";
option java_multiple_files = true;
option java_package = "com.mybank.endpoint";
option java_outer_classname = "TransactionsProto";
option objc_class_prefix = "HLW";
package com.mybank.endpoint;
import "google/protobuf/wrappers.proto";
service TransactionsService {
rpc PostTransactions(TransactionsRequest) returns (TransactionsReply);
}
message TransactionsRequest {
string transactionDesc = 1;
repeated Transaction transactions = 2;
}
message Transaction {
string id = 1;
string name = 2;
string description = 3;
}
message TransactionsReply {
string message = 1;
}
我可以进行梯度构建,并自动生成这个TransactionsServiceGrpcKt
package com.mybank.endpoint
import com.mybank.endpoint.TransactionsServiceGrpc.getServiceDescriptor
import io.grpc.CallOptions
import io.grpc.CallOptions.DEFAULT
import io.grpc.Channel
import io.grpc.Metadata
import io.grpc.MethodDescriptor
import io.grpc.ServerServiceDefinition
import io.grpc.ServerServiceDefinition.builder
import io.grpc.ServiceDescriptor
import io.grpc.Status.UNIMPLEMENTED
import io.grpc.StatusException
import io.grpc.kotlin.AbstractCoroutineServerImpl
import io.grpc.kotlin.AbstractCoroutineStub
import io.grpc.kotlin.ClientCalls.unaryRpc
import io.grpc.kotlin.ServerCalls.unaryServerMethodDefinition
import io.grpc.kotlin.StubFor
import kotlin.coroutines.CoroutineContext
import kotlin.coroutines.EmptyCoroutineContext
import kotlin.jvm.JvmOverloads
import kotlin.jvm.JvmStatic
/**
* Holder for Kotlin coroutine-based client and server APIs for
* com.mybank.endpoint.TransactionsService.
*/
object TransactionsServiceGrpcKt {
@JvmStatic
val serviceDescriptor: ServiceDescriptor
get() = TransactionsServiceGrpc.getServiceDescriptor()
val postTransactionsMethod: MethodDescriptor<TransactionsRequest, TransactionsReply>
@JvmStatic
get() = TransactionsServiceGrpc.getPostTransactionsMethod()
/**
* A stub for issuing RPCs to a(n) com.mybank.endpoint.TransactionsService service as suspending
* coroutines.
*/
@StubFor(TransactionsServiceGrpc::class)
class TransactionsServiceCoroutineStub @JvmOverloads constructor(
channel: Channel,
callOptions: CallOptions = DEFAULT
) : AbstractCoroutineStub<TransactionsServiceCoroutineStub>(channel, callOptions) {
override fun build(channel: Channel, callOptions: CallOptions): TransactionsServiceCoroutineStub
= TransactionsServiceCoroutineStub(channel, callOptions)
/**
* Executes this RPC and returns the response message, suspending until the RPC completes
* with [`Status.OK`][io.grpc.Status]. If the RPC completes with another status, a
* corresponding
* [StatusException] is thrown. If this coroutine is cancelled, the RPC is also cancelled
* with the corresponding exception as a cause.
*
* @param request The request message to send to the server.
*
* @return The single response from the server.
*/
suspend fun postTransactions(request: TransactionsRequest): TransactionsReply = unaryRpc(
channel,
TransactionsServiceGrpc.getPostTransactionsMethod(),
request,
callOptions,
Metadata()
)}
/**
* Skeletal implementation of the com.mybank.endpoint.TransactionsService service based on Kotlin
* coroutines.
*/
abstract class TransactionsServiceCoroutineImplBase(
coroutineContext: CoroutineContext = EmptyCoroutineContext
) : AbstractCoroutineServerImpl(coroutineContext) {
/**
* Returns the response to an RPC for com.mybank.endpoint.TransactionsService.PostTransactions.
*
* If this method fails with a [StatusException], the RPC will fail with the corresponding
* [io.grpc.Status]. If this method fails with a [java.util.concurrent.CancellationException],
* the RPC will fail
* with status `Status.CANCELLED`. If this method fails for any other reason, the RPC will
* fail with `Status.UNKNOWN` with the exception as a cause.
*
* @param request The request from the client.
*/
open suspend fun postTransactions(request: TransactionsRequest): TransactionsReply = throw
StatusException(UNIMPLEMENTED.withDescription("Method com.mybank.endpoint.TransactionsService.PostTransactions is unimplemented"))
final override fun bindService(): ServerServiceDefinition = builder(getServiceDescriptor())
.addMethod(unaryServerMethodDefinition(
context = this.context,
descriptor = TransactionsServiceGrpc.getPostTransactionsMethod(),
implementation = ::postTransactions
)).build()
}
}
我已经成功地创建了我的第一个grpc端点,它具有基于字符串的非常基本的请求/回复,现在我想通过创建一个消息列表继续前进。作为类比,假设我想要一个DTO/Pojo,其中包含一个实体列表
老实说,我完全被卡住了。所以,我的主要问题是:如何实现具有重复消息的proto服务
一个有用的注释是,为什么我在自动生成的存根中看到一个用“…,responseObserver:StreamObserver?”实现的方法,而不是我在proto中明确指定的简单的“TransactionsReply”?StreamObserver和重复消息之间有什么关系
这是整个项目
您将发现两个原型:一个成功的实现,具有简单的请求/应答,另一个失败,如上所述
***根据路易斯的第一个答案进行编辑
我很困惑。
用一个简单的proto作为
...
service Account {
rpc SendDebit (DebitRequest) returns (DebitReply) {}
}
message DebitRequest {
string name = 1;
}
message DebitReply {
string message = 1;
}
我可以用它来实现
override suspend fun sendDebit(request: DebitRequest): DebitReply {
return DebitReply.newBuilder().setMessage("teste").build()
}
尽管如此
...
service TransactionsService {
rpc PostTransactions(TransactionsRequest) returns (TransactionsReply);
}
message TransactionsRequest {
string transactionDesc = 1;
repeated Transaction transactions = 2;
}
message Transaction {
string id = 1;
string name = 2;
string description = 3;
}
message TransactionsReply {
string message = 1;
}
我无法使用相同类型的响应覆盖(请注意,响应完全相同)
这两种实现都不是由IntelliJ提出的
override fun postTransactions(request: TransactionsRequest?, responseObserver: StreamObserver<TransactionsReply>?) {
super.postTransactions(request, responseObserver)
return TransactionsReply.newBuilder().setMessage("testReply").build()
}
你在找什么
class TransactionsEndpoint : TransactionsServiceGrpcKt.TransactionsServiceCoroutineImplBase(){
override suspend fun postTransactions(request: TransactionsRequest) : TransactionsReply {
...
}
}
…使用suspend
修饰符,请求时不使用?
,使用Coroutine
扩展TransactionServiceCoroutineImplBase
请注意,这与重复的消息无关。您的RPC有一个单一的输入协议,只发送一次——这很可能是您想要的,除非您想要一个请求流,在这种情况下,您的协议文件应该是这样的
service TransactionsService {
rpc PostTransactions(stream TransactionsRequest) returns (TransactionsReply);
}
…在这种情况下,Kotlin生成的代码看起来会有所不同。Louis,谢谢。很容易看出你知道我做错了什么,但我还不能纠正它。我对返回流不感兴趣(这次不是)。我只想编写返回嵌套对象的第一个enpoint代码,就像我们在Rest中使用dto时通常做的那样。我删除了supsend,因为我知道这不是我的情况。我刚刚了解到“…挂起的函数只能由另一个挂起的函数或在一个协同程序中调用…”。我尝试了上面的方法(见我编辑的答案),但没有成功。我不明白为什么用一个非常简单的原型,包含单个字符串的请求和应答,我成功地用“override suspend fun sendDebit(request:DebitRequest):DebitReply{return DebitReply.newBuilder().setMessage(“teste”).build()实现了它但是,仅仅因为我更改了请求,但保持了回复的一致性,我就不能同样地实现响应。当然,它更改了请求,但我希望响应是相同的。gRPC框架正在调用您的方法。将
suspend
保留在那里,但您不必以任何不同的方式实现它,也不必执行任何有助于花费。但是你能用postTransactions
发布你用suspend
版本尝试过的确切代码吗?你确定你用Coroutine
扩展了正确的ImplBase
类吗?非常感谢。这就是问题所在:我在扩展“TransactionServicegrpc.TransactionServiceImplbase”正确的应该是“TransactionServiceGrpCkt.TransactionServiceCoroutineImplBase”。今天正好是我学习Micronaut+GRPC+Proto+Kotlin的一个月,我有很多东西要学。
override fun postTransactions(request: TransactionsRequest?, responseObserver: StreamObserver<TransactionsReply>?) :TransactionsReply {
super.postTransactions(request, responseObserver)
return TransactionsReply.newBuilder().setMessage("testReply").build()
}
override suspend fun postTransactions(request: TransactionsRequest): TransactionsReply {
return TransactionsReply.newBuilder().setMessage("testReply").build()
}
class TransactionsEndpoint : TransactionsServiceGrpcKt.TransactionsServiceCoroutineImplBase(){
override suspend fun postTransactions(request: TransactionsRequest) : TransactionsReply {
...
}
}
service TransactionsService {
rpc PostTransactions(stream TransactionsRequest) returns (TransactionsReply);
}