Uso de GRPC en SpringBoot + Kotlin para comunicación de servicios

Para ejemplos de proyectos, consulte: kotlin-grpc

1. Dependencias de importación:

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")  
            }  
        }    }}

2. Establecer prototipo

Pon protoel archivo src/mian/protoen el directorio.

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;  
}

implementar./gradlew clean build

Si la compilación es exitosa build/generated/source/proto/main, se generarán los archivos correspondientes en el programa y el paquete se puede importar directamente en el programa grpc.grpcktjava

3. Lado del servidor

escribe unservice

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()  
    }  
  
}

mainIntroducir inicio en la entrada

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")  
}

4. Lado del cliente

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)  
}

Cinco, algunos hoyos

Algunas dependencias de io.grpc y com.google están relacionadas. Si hay una gran diferencia entre las versiones dependientes, provocará un error de ejecución. Por ejemplo, antes usé una dependencia muy antigua de Google: com.google.code.google-collections:google-collect:snapshot-20080530, lo que provocó el mensaje cuando mi programa se estaba ejecutando:

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)

No puede encontrar problemas específicos en Google, puede prestar atención para ver si hay otras dependencias de com.google en el proyecto.

Las versiones de dependencia anteriores se basan en las versiones correspondientes spring booty kotlin. Si su versión no coincide, es posible que deba descartarla, pero el problema no debería ser demasiado grande. Usando el almacén espejo de Alibaba Cloud, puede buscar directamente imágenes espejo aquí para verificar las versiones compatibles.


Hay un problema con el empaquetado de Gradle. La razón del problema es que Bintray/jcenterse ha deshabilitado. Para confiar en estos complementos de Kotlin, puede usar las dependencias en la imagen de Alibaba Cloud .

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

Busque el ~/.gradledirectorio correspondiente, cree o modifique init.gradleel contenido de la siguiente manera

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.<init>(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.

versión de actualización java, actualicé a la últimajdk-8u221

Si tiene alguna pregunta, deje un mensaje, no seré tacaño si puedo ayudar.

Supongo que te gusta

Origin blog.csdn.net/w_monster/article/details/131645025
Recomendado
Clasificación