SpringBoot + Kotlin 中使用 GRPC 进行服务通信

示例项目见: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 下生成对应的 grpcgrpcktjava 文件
在程序中可以直接导包引入

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

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


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

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

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

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/37275.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

idea连接远程MySQL数据库

填写URL&#xff0c;以mysql为例 格式 jdbc:mysql://ip地址:端口号/数据库名 jdbc:mysql://127.0.0.1:3306/ldentification _Information

软件测试基础知识

软件测试的生命周期 软件测试的生命周期和软件的生命周期是不一样的&#xff0c;软件包括需求分析和规划&#xff0c;设计和编码&#xff0c;测试和验证&#xff0c;部署和维护&#xff0c;退役和回收等等&#xff0c;而软件测试的生命周期则是需求分析-测试计划-测试设计&…

对 Jenkins+ANT+Jmeter 接口测试的实践

目录 1、前言 2、框架与数据准备 3、脚本设计 4、整理测试报告 1、前言 JenkinsANTJMeter是一种常见的接口测试实践方案&#xff0c;可以实现自动化的接口测试和持续集成。Jenkins是一个流行的持续集成工具&#xff0c;ANT是一个构建工具&#xff0c;而JMeter是一个功能强大…

maven配置java outofmemory选项

在maven之中选择Add VM options&#xff0c;这样命令就多出来一个关于VM options配置的属性&#xff0c;此时就可以输入对于VM的设置

Ubuntu 放弃了战斗向微软投降

导读这几天看到 Ubuntu 放弃 Unity 和 Mir 开发&#xff0c;转向 Gnome 作为默认桌面环境的新闻&#xff0c;作为一个Linux十几年的老兵和Linux桌面的开发者&#xff0c;内心颇感良多。Ubuntu 做为全世界Linux界的桌面先驱者和创新者&#xff0c;突然宣布放弃自己多年开发的Uni…

jenkins实现easyswoole 持续集成/持续部署

jenkins环境jenkins需要使用root用户启动可通过修改 vim /etc/sysconfig/jenkins改为root,也可直接命令行root启动新增流水线项目安装远程构建插件Generic Webhook Trigger勾选触发远程构建保存之后,访问 /generic-webhook-trigger/invoke?tokeneasyswoole-test,即可自动bui…

【网络】协议的定制与Json序列化和反序列化

文章目录 应用层初识TCP协议通讯流程定制协议再谈协议网络版本计算器Protocal.hppCalServerCalClient Json的安装 应用层 我们程序员写的一个个解决我们实际问题, 满足我们日常需求的网络程序, 都是在应用层 初识TCP协议通讯流程 建立链接和断开链接 基于TCP协议&#xff0c…

nginx相关

1、nginx无默认配置文件 参考文章&#xff1a;nginx配置失败&#xff0c;卸载后重装无 nginx.conf文件_haojuntu的博客-CSDN博客 2、nginx更改服务器的端口号 参考文章&#xff1a;https://www.cnblogs.com/chaosfe/p/16123585.html#:~:text%E6%88%91%E4%BB%AC%E6%9F%A5%E7%…

Java----使用eureka进行注册连接(微服务简单实现)

当采用微服务架构时&#xff0c;各个业务流程被逐一分解&#xff0c;虽说是分解&#xff0c;但还是要进行连接的&#xff0c;最简单的就是使用http请求&#xff0c;将他们联系起来&#xff0c;通过给容器注入restTemplate&#xff0c;然后使用内置的方法进行请求&#xff0c;但…

Java虚拟机(JVM)介绍

JVM是什么 JVM是Java Virtual Machine的缩写。它是一种基于计算设备的规范&#xff0c;是一台虚拟机&#xff0c;即虚构的计算机。 JVM屏蔽了具体操作系统平台的信息&#xff08;显然&#xff0c;就像是我们在电脑上开了个虚拟机一样&#xff09;&#xff0c;当然&#xff0c;J…

支付宝接入

支付宝接入 python-alipay-sdk pycryptodome一、电脑网站支付 1.1 获取支付宝密钥 沙箱网址 1.APPID 2.应用私钥 3.支付宝公钥1.2 存放密钥 在与 settings.py 的同级目录下创建 pem 文件夹pem 文件夹下创建 app_private_key.pem 和 alipay_public_key.pem app_private_key…

pdf文档加水印怎么弄?用这款软件很方便

在工作中&#xff0c;我们经常需要将PDF文件发送给他人&#xff0c;但无法保证文件内容不被窃取&#xff0c;因此需要添加水印来保证文件的安全性。如果你不知道如何给PDF文件添加水印&#xff0c;以下两款软件可以帮助你轻松实现&#xff0c;一起来看看吧&#xff01; 方法一&…

爬虫之Scrapy

一 介绍 Scrapy一个开源和协作的框架&#xff0c;其最初是为了页面抓取 (更确切来说, 网络抓取 )所设计的&#xff0c;使用它可以以快速、简单、可扩展的方式从网站中提取所需的数据。但目前Scrapy的用途十分广泛&#xff0c;可用于如数据挖掘、监测和自动化测试等领域&#x…

Python实现Excel文件拷贝图片到另一个的Excel文件(保持原有图片比例)

Python实现Excel文件拷贝图片到另一个的Excel文件&#xff08;保持原有图片比例&#xff09; 1、前言1.1 成功拷贝但是比例错误1.2 直接报错 2、解决办法3、号外 1、前言 今天朋友给我一个需求&#xff0c;需要把xlsx文件中的图片拷贝到另一个xlsx中&#xff0c;但是试过网上比…

[Lesson 01] TiDB数据库架构概述

目录 一 章节目标 二 TiDB 体系结构 1 TiDB Server 2.1 TiKV 2.2 TiFlash 3 PD 参考 一 章节目标 理解TiDB数据库整体架构了解TiDB Server TiKV tiFlash 和 PD的主要功能 二 TiDB 体系结构 了解这些体系结构是如何实现TiDB的核心功能的 1 TiDB Server TiDB Serve…

若依官方前端手册 小笔记

提供确认窗体信息 this.$modal.confirm(确认信息).then(function() {... }).then(() > {... }).catch(() > {}); 提供遮罩层信息 // 打开遮罩层 this.$modal.loading("正在导出数据&#xff0c;请稍后...");// 关闭遮罩层 this.$modal.closeLoading(); 验证…

pythonocc进阶学习:faces的inner wire与outer wire

总目录 >> PythonOCC入门进阶到实战(目前已更新入门篇、基础篇和进阶篇) 我们在这篇文章中绘制了带有holes的面&#xff0c; 本篇是在读取到外部文件如brep&#xff0c;igs&#xff0c;stp后获取面的性质&#xff0c;寻找面中的wires的正向与逆向 只显示外wire from O…

【LeetCode热题100】打卡第37天:岛屿数量反转链表

文章目录 【LeetCode热题100】打卡第37天&#xff1a;岛屿数量&反转链表⛅前言 岛屿数量&#x1f512;题目&#x1f511;题解 反转链表&#x1f512;题目&#x1f511;题解 【LeetCode热题100】打卡第37天&#xff1a;岛屿数量&反转链表 ⛅前言 大家好&#xff0c;我是…

【Spring core学习二】创建Spring 项目 Spring的存

目录 &#x1f31f;一、创建最原始的Spring-core项目。 &#x1f31f;二、怎么往Spring中存取对象&#xff1f; &#x1f337;1、在Spring中存对象 &#x1f337;2、通过getBean获取对象的三种方式 &#x1f337;3、通过factory方式获取对象 &#x1f31f;三、对存对象的…

用github的copilot;tmux中进去了> 怎么退出

1、首先要学籍认证 &#xff08;前提&#xff1a;(241条消息) Copilot使用的关卡——GitHub教育认证方法和注意事项_github教师认证_石去皿的博客-CSDN博客&#xff09; 网址&#xff1a;Are you a student? - GitHub Education (241条消息) GitHub学生认证&#xff0c;可…