示例项目见: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.(Metadata.java:762)

at io.grpc.Metadata$Key.(Metadata.java:671)

at io.grpc.Metadata$AsciiKey.(Metadata.java:971)

at io.grpc.Metadata$AsciiKey.(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.(GrpcUtil.java:99)

at io.grpc.netty.Utils.(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是发现不了具体的问题的,可以注意一下,在项目中查看下是否有别的com.google的依赖。

上面的依赖版本都是基于对应 spring boot 和 kotlin 版本的,如果你的版本不适配,可能也需要折腾一下,但问题应该不是很大。使用阿里云的镜像仓库,可以直接在这里搜索镜像,查看支持的版本。

gradle 打包出现问题,问题原因是Bintray/jcenter 已停用,要依赖这些 kotlin 插件可以使用阿里云的镜像中的依赖。

A problem occurred configuring root project 'yybs-backend'.

> Could not resolve all artifacts for configuration ':classpath'.

> Could not resolve org.jetbrains.kotlin:kotlin-gradle-plugin-api:1.6.0.

Required by:

project : > org.jetbrains.kotlin.jvm:org.jetbrains.kotlin.jvm.gradle.plugin:1.6.0 > org.jetbrains.kotlin:kotlin-gradle-plugin:1.6.0

project : > org.jetbrains.kotlin.plugin.spring:org.jetbrains.kotlin.plugin.spring.gradle.plugin:1.6.0 > org.jetbrains.kotlin:kotlin-allopen:1.6.0

> Could not resolve org.jetbrains.kotlin:kotlin-gradle-plugin-api:1.6.0.

..........

> sun.security.validator.ValidatorException: PKIX path building failed: sun.security.provider.certpath.SunCertPathBuilderException: unable to find valid certification path to requested target

找到对应的~/.gradle 目录,创建或修改 init.gradle 内容为下面的内容

allprojects {

repositories {

maven {

url 'https://maven.aliyun.com/repository/public/'

}

maven {

url 'https://maven.aliyun.com/repository/jcenter/'

}

all { ArtifactRepository repo ->

if (repo instanceof MavenArtifactRepository) {

def url = repo.url.toString()

if (

url.startsWith('https://repo.maven.apache.org/maven2/')

|| url.startsWith('https://repo.maven.org/maven2')

|| url.startsWith('https://repo1.maven.org/maven2')

|| url.startsWith('https://jcenter.bintray.com/')

|| url.startsWith('https://plugins.gradle.org/')

) {

//project.logger.lifecycle "Repository ${repo.url} replaced by $REPOSITORY_URL."

remove repo

}

}

}

}

buildscript {

repositories {

maven{ url 'https://maven.aliyun.com/repository/public/'}

maven {

url 'https://maven.aliyun.com/repository/jcenter/'

}

maven {

url 'https://maven.aliyun.com/repository/gradle-plugin/'

}

all { ArtifactRepository repo ->

if (repo instanceof MavenArtifactRepository) {

def url = repo.url.toString()

if (

url.startsWith('https://repo1.maven.org/maven2')

|| url.startsWith('https://jcenter.bintray.com/')

|| url.startsWith('https://plugins.gradle.org/')

) {

//project.logger.lifecycle "Repository ${repo.url} replaced by $REPOSITORY_URL."

remove repo

}

}

}

}

}

}

--grpckt_out: protoc-gen-grpckt: Plugin failed with status code 1

Execution failed for task ':generateProto'.

> protoc: stdout: . stderr: Exception in thread "main" java.lang.VerifyError: Uninitialized object exists on backward branch 71

Exception Details:

Location:

com/squareup/kotlinpoet/TypeSpec.(Lcom/squareup/kotlinpoet/TypeSpec$Builder;Lcom/squareup/kotlinpoet/TagMap;Lcom/squareup/kotlinpoet/OriginatingElementsHolder;ILkotlin/jvm/internal/DefaultConstructorMarker;)V @118: goto

Reason:

Error exists in the bytecode

Bytecode:

0000000: 1504 057e 9900 0b2b c003 44b8 034a 4d15

0000010: 0407 7e99 007e 2bb6 00de c000 9a2b b600

0000020: c4c0 009c 3a06 3a0f 0336 0719 063a 08bb

0000030: 0152 59b7 01c3 c000 9a3a 0903 360a 1908

0000040: b901 5c01 003a 0b19 0bb9 0161 0100 9900

0000050: 2b19 0bb9 0165 0100 3a0c 190c 3a0d 0336

0000060: 0e19 0dc0 0002 b600 dfc0 009c 3a0d 1909

0000070: 190d b800 a257 a7ff d119 09c0 016e 3a10

0000080: 190f 1910 c000 9cb8 0172 b803 50c0 0008

0000090: 4e2a 2b2c 2db7 0352 b1

Stackmap Table:

same_frame(@15)

full_frame(@71,{UninitializedThis,Object[#144],Object[#218],Object[#8],Integer,Object[#857],Object[#156],Integer,Object[#156],Object[#154],Integer,Object[#350],Top,Top,Top,Object[#154]},{})

same_frame(@121)

full_frame(@145,{UninitializedThis,Object[#144],Object[#218],Object[#8],Integer,Object[#857]},{})

at io.grpc.kotlin.generator.GrpcClientStubGenerator.generateStub(GrpcClientStubGenerator.kt:111)

at io.grpc.kotlin.generator.GrpcClientStubGenerator.generate(GrpcClientStubGenerator.kt:100)

at io.grpc.kotlin.generator.ProtoFileCodeGenerator.generateCodeForFile(ProtoFileCodeGenerator.kt:50)

at io.grpc.kotlin.generator.GeneratorRunner.generateCodeForFile(GeneratorRunner.kt:44)

at io.grpc.kotlin.generator.protoc.AbstractGeneratorRunner.mainAsProtocPlugin(AbstractGeneratorRunner.kt:56)

at io.grpc.kotlin.generator.protoc.AbstractGeneratorRunner.doMain(AbstractGeneratorRunner.kt:87)

at io.grpc.kotlin.generator.GeneratorRunner.main(GeneratorRunner.kt:28)

--grpckt_out: protoc-gen-grpckt: Plugin failed with status code 1.

升级 java 版本,我升级到了最新的 jdk-8u221

如有问题,欢迎留言,能帮到的地方,我不会吝啬。

推荐文章

评论可见,请评论后查看内容,谢谢!!!
 您阅读本篇文章共花了: