在线时间:8:00-16:00
迪恩网络APP
随时随地掌握行业动态
扫描二维码
关注迪恩网络微信公众号
开源软件名称(OpenSource Name):rouzwawi/grpc-kotlin开源软件地址(OpenSource Url):https://github.com/rouzwawi/grpc-kotlin开源编程语言(OpenSource Language):Kotlin 67.5%开源软件介绍(OpenSource Introduction):gRPC Kotlin - Coroutine based gRPC for KotlingRPC Kotlin is a protoc plugin for generating native Kotlin bindings using coroutine primitives for gRPC services.
Why?The asynchronous nature of bidirectional streaming rpc calls in gRPC makes them a bit hard to implement
and read. Getting your head around the In situations where you'd want to coordinate several request and response messages in one call, you'll and up having to manage some tricky state and synchronization between the observers. There are reactive bindings for gRPC which make this easier. But I think we can do better! Enter Kotlin Coroutines! By generating native Kotlin stubs that allows us to use Quick startNote: This has been tested with Add a gRPC service definition to your project
syntax = "proto3";
package org.example.greeter;
option java_package = "org.example.greeter";
option java_multiple_files = true;
message GreetRequest {
string greeting = 1;
}
message GreetReply {
string reply = 1;
}
service Greeter {
rpc Greet (GreetRequest) returns (GreetReply);
rpc GreetServerStream (GreetRequest) returns (stream GreetReply);
rpc GreetClientStream (stream GreetRequest) returns (GreetReply);
rpc GreetBidirectional (stream GreetRequest) returns (stream GreetReply);
} Run the protoc plugin to get the generated code, see build tool configuration ServerAfter compilation, you'll find the generated Kotlin code in the same package as the generated Java
code. A service base class named All functions have the All the server streaming calls return a All client streaming calls receive an argument of Here's an example server that demonstrates how each type of endpoint is implemented. import kotlinx.coroutines.*
import kotlinx.coroutines.channels.ReceiveChannel
import kotlinx.coroutines.channels.produce
import java.util.concurrent.Executors.newFixedThreadPool
class GreeterImpl : GreeterImplBase(
coroutineContext = newFixedThreadPool(4).asCoroutineDispatcher()
) {
// unary rpc
override suspend fun greet(request: GreetRequest): GreetReply {
return GreetReply.newBuilder()
.setReply("Hello " + request.greeting)
.build()
}
// server streaming rpc
override fun greetServerStream(request: GreetRequest) = produce<GreetReply> {
send(GreetReply.newBuilder()
.setReply("Hello ${request.greeting}!")
.build())
send(GreetReply.newBuilder()
.setReply("Greetings ${request.greeting}!")
.build())
}
// client streaming rpc
override suspend fun greetClientStream(requestChannel: ReceiveChannel<GreetRequest>): GreetReply {
val greetings = mutableListOf<String>()
for (request in requestChannel) {
greetings.add(request.greeting)
}
return GreetReply.newBuilder()
.setReply("Hi to all of $greetings!")
.build()
}
// bidirectional rpc
override fun greetBidirectional(requestChannel: ReceiveChannel<GreetRequest>) = produce<GreetReply> {
var count = 0
for (request in requestChannel) {
val n = count++
launch {
delay(1000)
send(GreetReply.newBuilder()
.setReply("Yo #$n ${request.greeting}")
.build())
}
}
}
} ClientExtensions functions for the original Java stubs are generated that use import io.grpc.ManagedChannelBuilder
import kotlinx.coroutines.delay
import kotlinx.coroutines.launch
import kotlinx.coroutines.runBlocking
fun main(args: Array<String>) {
val localhost = ManagedChannelBuilder.forAddress("localhost", 8080)
.usePlaintext()
.build()
val greeter = GreeterGrpc.newStub(localhost)
runBlocking {
// === Unary call =============================================================================
val unaryResponse = greeter.greet(req("Alice"))
println("unary reply = ${unaryResponse.reply}")
// === Server streaming call ==================================================================
val serverResponses = greeter.greetServerStream(req("Bob"))
for (serverResponse in serverResponses) {
println("server response = ${serverResponse.reply}")
}
// === Client streaming call ==================================================================
val manyToOneCall = greeter.greetClientStream()
manyToOneCall.send(req("Caroline"))
manyToOneCall.send(req("David"))
manyToOneCall.close()
val oneReply = manyToOneCall.await()
println("single reply = ${oneReply.reply}")
// === Bidirectional call =====================================================================
val bidiCall = greeter.greetBidirectional()
launch {
var n = 0
for (greetReply in bidiCall) {
println("r$n = ${greetReply.reply}")
n++
}
println("no more replies")
}
delay(200)
bidiCall.send(req("Eve"))
delay(200)
bidiCall.send(req("Fred"))
delay(200)
bidiCall.send(req("Gina"))
bidiCall.close()
}
} gRPC Context propagationgRPC has a thread-local First, all the generated service abstract class MyServiceImplBase(
coroutineContext: CoroutineContext = Dispatchers.Default
) Second, in the getter for Here's a simple example that makes calls to other services concurrently and expects an authenticated user to be present
in the gRPC Context. The two accesses to the context key may execute on different threads in the val authenticatedUser = Context.key<User>("authenticatedUser")
override suspend fun greet(request: GreetRequest): GreetReply {
val motd = async { messageOfTheDay.getMessage() }
val weatherReport = async { weather.getWeatherReport(authenticatedUser.get().location) }
val reply = buildString {
append("Hello ${authenticatedUser.get().fullName}")
append("---")
append("Today's weather report: ${weatherReport.await()}")
append("---")
append(motd.await())
}
return GreetReply.newBuilder()
.setReply(reply)
.build()
} For another example of gRPC Context usage, see the code in ContextBasedGreeterTest Thanks to Exception handlingThe generated server code follows the standard exception propagation for Kotlin coroutines as described
in the Exception handling documentation. This means that it's safe to throw exceptions from within
the server implementation code. These will propagate up the coroutine scope and be translated to
Note that you should not call Maven configurationAdd the <properties>
<kotlin.version>1.3.61</kotlin.version>
<kotlinx-coroutines.version>1.3.3</kotlinx-coroutines.version>
<grpc.version>1.25.0</grpc.version>
<protobuf.version>3.10.0</protobuf.version>
<grpc-kotlin.version>0.1.4</grpc-kotlin.version>
</properties>
<dependencies>
<dependency>
<groupId>org.jetbrains.kotlin</groupId>
<artifactId>kotlin-stdlib</artifactId>
<version>${kotlin.version}</version>
</dependency>
<dependency>
<groupId>org.jetbrains.kotlinx</groupId>
<artifactId>kotlinx-coroutines-core</artifactId>
<version>${kotlinx-coroutines.version}</version>
</dependency>
<dependency>
<groupId>io.grpc</groupId>
<artifactId>grpc-netty</artifactId>
<version>${grpc.version}</version>
</dependency>
<dependency>
<groupId>io.grpc</groupId>
<artifactId>grpc-protobuf</artifactId>
<version>${grpc.version}</version>
</dependency>
<dependency>
<groupId>io.grpc</groupId>
<artifactId>grpc-stub</artifactId>
<version>${grpc.version}</version>
</dependency>
</dependencies>
<build>
<extensions>
<extension>
<groupId>kr.motd.maven</groupId>
<artifactId>os-maven-plugin</artifactId>
<version>1.5.0.Final</version>
</extension>
</extensions>
<plugins>
<plugin>
<groupId>org.xolstice.maven.plugins</groupId>
<artifactId>protobuf-maven-plugin</artifactId>
<version>0.6.1</version>
<configuration>
<protocArtifact>com.google.protobuf:protoc:${protobuf.version}:exe:${os.detected.classifier}</protocArtifact>
</configuration>
<executions>
<execution>
<goals><goal>compile</goal></goals>
</execution>
<execution>
<id>grpc-java</id>
<goals><goal>compile-custom</goal></goals>
<configuration>
<pluginId>grpc-java</pluginId>
<pluginArtifact>io.grpc:protoc-gen-grpc-java:${grpc.version}:exe:${os.detected.classifier}</pluginArtifact>
</configuration>
</execution>
<execution>
<id>grpc-kotlin</id>
<goals><goal>compile-custom</goal></goals>
<configuration>
<pluginId>grpc-kotlin</pluginId>
<pluginArtifact>io.rouz:grpc-kotlin-gen:${grpc-kotlin.version}:exe:${os.detected.classifier}</pluginArtifact>
</configuration>
</execution>
</executions>
</plugin>
<!-- make sure to add the generated source directories to the kotlin-maven-plugin -->
<plugin>
<artifactId>kotlin-maven-plugin</artifactId>
<groupId>org.jetbrains.kotlin</groupId>
<version>${kotlin.version}</version>
<executions>
<execution>
<id>compile</id>
<goals><goal>compile</goal></goals>
<configuration>
<sourceDirs>
<sourceDir>${project.basedir}/src/main/kotlin</sourceDir>
<sourceDir>${project.basedir}/target/generated-sources/protobuf/grpc-kotlin</sourceDir>
<sourceDir>${project.basedir}/target/generated-sources/protobuf/grpc-java</sourceDir>
<sourceDir>${project.basedir}/target/generated-sources/protobuf/java</sourceDir>
</sourceDirs>
</configuration>
</execution>
</executions>
</plugin>
</plugins>
</build> Gradle configurationAdd the def protobufVersion = '3.10.0'
def grpcVersion = '1.25.0'
def grpcKotlinVersion = '0.1.4'
protobuf {
protoc {
// The artifact spec for the Protobuf Compiler
artifact = "com.google.protobuf:protoc:${protobufVersion}"
}
plugins {
grpc {
artifact = "io.grpc:protoc-gen-grpc-java:${grpcVersion}"
}
grpckotlin {
artifact = "io.rouz:grpc-kotlin-gen:${grpcKotlinVersion}"
}
}
generateProtoTasks {
all()*.plugins {
grpc {}
grpckotlin {}
}
}
} Add the kotlin dependencies def kotlinVersion = '1.3.61'
def kotlinCoroutinesVersion = '1.3.3'
dependencies {
compile "org.jetbrains.kotlin:kotlin-stdlib:$kotlinVersion"
compile "org.jetbrains.kotlinx:kotlinx-coroutines-core:$kotlinCoroutinesVersion"
} ExamplesThis is a list of example gRPC services and clients written using this project RPC method type referenceUnary call
ServiceA suspendable function which returns a single message. override suspend fun greet(request: GreetRequest): GreetReply {
// return GreetReply message
} ClientSuspendable call returning a single message. val response: GreetReply = stub.greet( /* GreetRequest */ ) Client streaming call
ServiceA suspendable function which returns a single message, and receives messages from a override suspend fun greetClientStream(requestChannel: ReceiveChannel<GreetRequest>): GreetReply {
// receive request messages
val firstRequest = requestChannel.receive()
// or iterate all request messages
for (request in requestChannel) {
// ...
}
// return GreetReply message
} ClientUsing val call: ManyToOneCall<GreetRequest, GreetReply> = stub.greetClientStream()
call.send( /* GreetRequest */ )
call.send( /* GreetRequest */ )
call.close() // don't forget to close the send channel
val responseMessage = call.await() Server streaming call
ServiceUsing override fun greetServerStream(request: GreetRequest) = produce<GreetReply> {
send( /* GreetReply message */ )
send( /* GreetReply message */ )
// ...
} Note that In ClientUsing val responses: ReceiveChannel<GreetReply> = stub.greetServerStream( /* GreetRequest */ )
// await individual responses
val responseMessage = serverResponses.receive()
// or iterate all responses
for (responseMessage in responses) {
// ...
} Full bidirectional streaming call
ServiceUsing |
2023-10-27
2022-08-15
2022-08-17
2022-09-23
2022-08-13
请发表评论