示例项目见:kotlin-grpc
一、导入依赖:
import com.google.protobuf.gradle.*
plugins {
id("org.springframework.boot") version "2.3.1.RELEASE"
id("io.spring.dependency-management") version "1.0.9.RELEASE"
id("org.asciidoctor.convert") version "1.5.9.2"
kotlin("jvm") version "1.6.0"
kotlin("plugin.spring") version "1.6.0"
id("com.google.protobuf") version "0.9.2"
}
repositories {
mavenCentral()
}
group = "com.whrss.kotlin-grpc"
version = "0.0.1-SNAPSHOT"
java.sourceCompatibility = JavaVersion.VERSION_1_8
java.targetCompatibility = JavaVersion.VERSION_1_8
sourceSets.main {
java.srcDirs("src/main/kotlin")
}
extra["spring-restdocs.version"] = "2.0.5.BUILD-SNAPSHOT"
val snippetsDir by extra { file("build/generated-snippets") }
dependencies {
implementation("com.google.protobuf:protobuf-java:3.22.2")
implementation("io.grpc:grpc-protobuf:1.53.0")
implementation("com.google.protobuf:protobuf-kotlin:3.22.2")
implementation("io.grpc:grpc-kotlin-stub:1.3.0")
implementation("io.grpc:grpc-netty:1.56.1")
implementation("io.grpc:grpc-all:1.56.1")
}
protobuf {
protoc {
artifact = "com.google.protobuf:protoc:3.19.4"
}
plugins {
id("grpc") {
artifact = "io.grpc:protoc-gen-grpc-java:1.40.1"
}
id("grpckt") {
artifact = "io.grpc:protoc-gen-grpc-kotlin:1.3.0:jdk8@jar"
}
}
// Enable Kotlin generation
generateProtoTasks {
all().forEach {
it.plugins {
id("grpc")
id("grpckt")
}
} }}
二、设置Proto
将 proto
文件放在 src/mian/proto
目录下
syntax = "proto3";
import "google/api/annotations.proto";
option java_multiple_files = true;
package hello_world.v1;
service HelloWorldService{
rpc GetUserInfo (GetUserRequest) returns (GetUserReply) {
option (google.api.http) = {
get: "/api/v1/users"
};
}
}
message GetUserRequest{
// 用户showId
string userId = 1;
}
message GetUserReply{
// 用户showId
string userId = 1;
}
执行 ./gradlew clean build
build成功则会在 build/generated/source/proto/main
下生成对应的 grpc
、 grpckt
、 java
文件
在程序中可以直接导包引入
三、Server端
写一个 service
import hello_world.v1.GetUserReply
import hello_world.v1.GetUserRequest
import hello_world.v1.HelloWorldServiceGrpcKt
class Service : HelloWorldServiceGrpcKt.HelloWorldServiceCoroutineImplBase() {
override suspend fun getUserInfo(request: GetUserRequest) : GetUserReply {
println("getItemStatistics exec")
return GetUserReply.newBuilder()
.setUserId(request.userId)
.build()
}
}
在 main
入口引入启动
import io.grpc.ServerBuilder
fun main() {
helloServer()
}
fun helloServer() {
val helloService = Service()
val server = ServerBuilder
.forPort(15001)
.addService(helloService)
.build()
Runtime.getRuntime().addShutdownHook(Thread {
server.shutdown()
server.awaitTermination()
})
server.start()
println("server start")
server.awaitTermination()
println("server restart")
}
四、Client 端
import hello_world.v1.GetUserRequest
import hello_world.v1.HelloWorldServiceGrpc
import io.grpc.ManagedChannelBuilder
fun main() {
val channel = ManagedChannelBuilder.forAddress("localhost", 15001).usePlaintext()
val stub = HelloWorldServiceGrpc.newBlockingStub(channel.build())
val response = stub.getUserInfo(GetUserRequest.newBuilder().setUserId("0").build())
println(response)
}
五、一些坑
io.grpc 和 com.google的一些依赖是有关联的,如果依赖版本之间有巨大差异,是会导致运行错误的。比如我之前使用到了一个google的一个特别老的依赖:com.google.code.google-collections:google-collect:snapshot-20080530,导致了我程序运行时提示:
Exception in thread "main" java.lang.NoSuchMethodError: 'void com.google.common.base.Preconditions.checkArgument(boolean, java.lang.String, char, java.lang.Object)'
at io.grpc.Metadata$Key.validateName(Metadata.java:754)
at io.grpc.Metadata$Key.<init>(Metadata.java:762)
at io.grpc.Metadata$Key.<init>(Metadata.java:671)
at io.grpc.Metadata$AsciiKey.<init>(Metadata.java:971)
at io.grpc.Metadata$AsciiKey.<init>(Metadata.java:966)
at io.grpc.Metadata$Key.of(Metadata.java:708)
at io.grpc.Metadata$Key.of(Metadata.java:704)
at io.grpc.internal.GrpcUtil.<clinit>(GrpcUtil.java:99)
at io.grpc.netty.Utils.<clinit>(Utils.java:83)
at io.grpc.netty.UdsNettyChannelProvider.isAvailable(UdsNettyChannelProvider.java:34)
at io.grpc.ManagedChannelRegistry$ManagedChannelPriorityAccessor.isAvailable(ManagedChannelRegistry.java:211)
at io.grpc.ManagedChannelRegistry$ManagedChannelPriorityAccessor.isAvailable(ManagedChannelRegistry.java:207)
at io.grpc.ServiceProviders.loadAll(ServiceProviders.java:68)
at io.grpc.ManagedChannelRegistry.getDefaultRegistry(ManagedChannelRegistry.java:101)
at io.grpc.ManagedChannelProvider.provider(ManagedChannelProvider.java:43)
at io.grpc.ManagedChannelBuilder.forAddress(ManagedChannelBuilder.java:39)
at com.ck567.kotlingrpc.ClientKt.main(Client.kt:9)
at com.ck567.kotlingrpc.ClientKt.main(Client.kt)
在google是发现不了具体的问题的,可以注意一下。
上面的依赖版本都是基于对应spring boot和 kotlin 版本的,如果你的版本不适配,可能也需要折腾一下,但问题应该不是很大。
如有问题,欢迎留言,能帮到的地方,我不会吝啬。