在这里插入图片描述
Kroto+是一个面向Kotlin的gRPC库
https://github.com/marcoferrer/kroto-plus

以往,想在Kotlin上使用protobuf协议进行grpc通信,需要基于java生成本地stub代码然后在kotlin中调用,现在通过Kroto+可以直接生成更加kotlin范儿的本地stub,包括对Coroutine的支持、支持DSL等,非常方便!

// before
val message1 = MessageRequest.newBuilder()
    .setMessage("hello")
    .build()
// Kroto+
val message2 = MessageRequest { 
    message = "hello"

接下来通过一个例子了解一下=Kproto+的使用

1.build.gradle

gradle.properties定义版本号

krotoplus_version=0.5.0
protobuf_version=3.10.0
coroutines_version=1.3.2
grpc_version=1.25.0

添加protobuf的plugin

plugins {
    id 'org.jetbrains.kotlin.jvm' version '1.3.61'
    id 'com.google.protobuf' version '0.8.10' // 添加protobuf
    id 'application'

添加dependencies依赖

dependencies {
    implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk8"
    implementation "org.jetbrains.kotlinx:kotlinx-coroutines-core:$coroutines_version"
    implementation "com.github.marcoferrer.krotoplus:kroto-plus-coroutines:$krotoplus_version"
    implementation "com.github.marcoferrer.krotoplus:kroto-plus-message:$krotoplus_version"
    implementation "com.google.protobuf:protobuf-java:$protobuf_version"
    implementation "io.grpc:grpc-protobuf:$grpc_version"
    implementation "io.grpc:grpc-stub:$grpc_version"
    implementation "io.grpc:grpc-netty:$grpc_version"

配置compileKotlin

compileKotlin {
    kotlinOptions {
        jvmTarget = "1.8"
        freeCompilerArgs += [
                "-Xuse-experimental=kotlinx.coroutines.ObsoleteCoroutinesApi"
compileTestKotlin {
    kotlinOptions.jvmTarget = "1.8"

配置protobuf的task

protobuf {
    protoc {
        artifact = "com.google.protobuf:protoc:$protobuf_version"
    plugins {
        grpc {
            artifact = "io.grpc:protoc-gen-grpc-java:$grpc_version"
        coroutines {
            artifact = "com.github.marcoferrer.krotoplus:protoc-gen-grpc-coroutines:$krotoplus_version:jvm8@jar"
        kroto {
            artifact = "com.github.marcoferrer.krotoplus:protoc-gen-kroto-plus:$krotoplus_version:jvm8@jar"
    generateProtoTasks {
        def krotoConfig = file("krotoPlusConfig.asciipb")
        all().each { task ->
            task.inputs.files krotoConfig
            task.plugins {
                grpc {}
                coroutines {}
                kroto {
                    outputSubDir = "java"
                    option "ConfigPath=$krotoConfig"

generateProtoTasks.krotoConfig中设置的krotoPlusConfig.asciipb是生成stub代码的配置,本例子需要支持DSL,所以配置如下

//krotoPlusConfig.ascilibp
proto_builders {
    filter { exclude_path: "google/*" }
    unwrap_builders: true
    use_dsl_markers: true

最后,为了让生成代码参与编译,配置sourceSets指定codegen的目录

sourceSets {
    main {
        java {
            srcDir("$buildDir/generated/source/proto/main/java")
            srcDir("$buildDir/generated/source/proto/main/grpc")
            srcDir("$buildDir/generated/source/proto/main/coroutines")

2.protobuf定义

分别定义了四种服务类型的rpc

api.proto:

syntax = "proto3";
option java_multiple_files = true;
option java_package = "com.my.krotosample.protobuf";
option java_outer_classname = "KrotoSample";
package api;
message MessageRequest {
    string message = 1;
message MessageResponse {
    string message = 1;
service MessageService {
    rpc Unary (MessageRequest) returns (MessageResponse);
    rpc ClientStream (stream MessageRequest) returns (MessageResponse);
    rpc ServerStream (MessageRequest) returns (stream MessageResponse);
    rpc BidirectionalStream (stream MessageRequest) returns (stream MessageResponse);

3.codegen

gradle命令执行 generateProto

 ./gradlew generateProto

4.Server端实现

实现MessageServiceImpl,继承自MessageServiceCoroutineGrpc.MessageServiceImplBase

@ExperimentalCoroutinesApi
class MessageServiceImpl : MessageServiceCoroutineGrpc.MessageServiceImplBase() {
    override val initialContext: CoroutineContext
        get() = Dispatchers.Default + SupervisorJob()
    override suspend fun unary(
        request: MessageRequest
    ): MessageResponse {
        return MessageResponse {
            message = request.message.toUpperCase()
    override suspend fun clientStream(
        requestChannel: ReceiveChannel<MessageRequest>
    ): MessageResponse {
        val requestList = requestChannel.toList()
        return MessageResponse {
            message = requestList.joinToString("\n") {
                it.message.toUpperCase()
    override suspend fun serverStream(
        request: MessageRequest,
        responseChannel: SendChannel<MessageResponse>
        val response = MessageResponse {
            message = request.message.toUpperCase()
        repeat(2) {
            responseChannel.send(response)
        responseChannel.close()
    override suspend fun bidirectionalStream(
        requestChannel: ReceiveChannel<MessageRequest>,
        responseChannel: SendChannel<MessageResponse>
        requestChannel.consumeEach { request ->
            val response = MessageResponse {
                message = request.message.toUpperCase()
            responseChannel.send(response)

作为例子,业务比较简单,将request携带的message进行upperCase后返回。
stream中使用了Coroutine的Channel,相对于回调的写法更加方便

5. 启动server

gradle启动server

./gradlew run

Main.kt:

@ExperimentalCoroutinesApi
fun main() {
    val port = 6565
    val server = ServerBuilder.forPort(port)
        .addService(MessageServiceImpl())
        .build()
        .start()
    Runtime.getRuntime().addShutdownHook(Thread() {
        server.shutdown()
    server.awaitTermination()

build.gradle中配置main

application {
    mainClassName = "MainKt"

6.Client端实现

Client端的stub也可以支持Coroutine

Main.kt:

@ExperimentalCoroutinesApi
fun main() {
    val port = 6565
    val server = ServerBuilder.forPort(port)
        .addService(MessageServiceImpl())
        .build()
        .start()
    val channel = ManagedChannelBuilder.forAddress("localhost", 6565)
        .usePlaintext()
        .build()
    val client = MessageServiceCoroutineGrpc
        .MessageServiceCoroutineStub.newStub(channel)
    println("--- Bidirectional Stream start ---")
    runBlocking {
        val (requestChannel, responseChannel) = client.bidirectionalStream()
        listOf("hello", "kotlin", "proto").forEach {
            requestChannel.send {
                message = it
        requestChannel.close()
        responseChannel.consumeEach {
            println(it.message)
    println("--- Bidirectional Stream finish ---")
    println("--- Client Stream start ---")
    runBlocking {
        val (requestChannel, response) = client.clientStream()
        listOf("hello", "kotlin", "proto").forEach {
            requestChannel.send {
                message = it
        requestChannel.close()
        println(response.await().message)
    println("--- Client Stream finish ---")
    println("--- Server Stream start ---")
    runBlocking {
        val request = MessageRequest {
            message = "kproto"
        client.serverStream(request).consumeEach {
            println(it.message)
    println("--- Server Stream finish ---")
    server.shutdown()

Kproto+生成的stub都是suspend函数,所以可以在Coroutine中执行,同样使用Channel甚至Flow方便的进行IPC通信

--- Bidirectional Stream start ---
HELLO
KOTLIN
PROTO
--- Bidirectional Stream finish ---
--- Client Stream start ---
HELLO
KOTLIN
PROTO
--- Client Stream finish ---
--- Server Stream start ---
KPROTO
KPROTO
--- Server Stream finish ---
                    Kroto+(Kroto-plus)Kroto+是一个面向Kotlin的gRPC库https://github.com/marcoferrer/kroto-plus以往,想在Kotlin上使用protobuf协议进行grpc通信,需要基于java生成本地stub代码然后在kotlin中调用,现在通过Kroto+可以直接生成更加kotlin范儿的本地stub,包括对Coroutine的支持、支持DSL等,非常方便!// beforeval message1 = MessageRequest.n.
 gRPC Kotlin是一个插件,用于使用服务的生成本地Kotlin绑定。
 gRPC中双向流式rpc调用的异步特性使它们难以实现和读取。 StreamObserver<T>让您绕开StreamObserver<T>可能有些棘手。 特别是方法参数是响应观察者,而返回值是请求观察者,这一切都与处理程序的普通旧同步版本的外观有些倒退。
 如果您想在一个呼叫中协调多个请求和响应消息,则将不得不管理一些棘手的状态以及观察者之间的同步。 gRPC有,这使此操作更加容易。 但是我认为我们可以做得更好!
 输入Kotlin协程! 通过
				
kotlin写了个小玩意,数据打算用protobuf存储,结果各种Unresolved reference: proto,折腾半天终于搞定。 根目录下build.gradle: buildscript { ext.kotlin_version = '1.2.71' ext.grpc_version = '1.15.1' repositories { ma...
序列化探索之Protobuf Protobuf是谷歌提出的一种高压缩比的序列化格式,二进制,不可读,语言无关,平台无关。拥有自己的语法规则,压缩编码算法,并提供主流语言的API生成器(即Protobuf编译器),其序列化结果很小,能够有效节省带宽。 掌握Protobuf,需要比较了解三个方面,其中,如果只是单纯滴使用,前两个方面即可。 proto语法规则,即proto文件的语法规则 具体语言的API生成及使用规则,即通过proto文件生成对应语言的代码 序列化和反序列化算法 截止目前,proto有
下面的题目都是在Android交流群大家在面试时遇到的,如果大家有好的题目或者好的见解欢迎分享,楼主将长期维护此帖。 参考解析:郭霖、鸿洋、玉刚、极客时间、腾讯课堂… 内容特点:条理清晰,含图像化表示更加易懂。 内容概要:包括 Handler、Activity相关、Fragment、service、布局优化、AsyncTask相关 、Android 事件分发机制、 Binder、Android 高级必备 :AMS,WMS,PMS、Glide、 Android 组件化与插件化等面试题和技术栈!
android studio Kotlin使用 GRPCprotobuf 出现一些问题总结 Kotlin中配置 GRPCprotobuf 出现一些问题总结如下: 3rd-party Gradle plug-ins may be the cause Codegen plugin grpc not defined Execution failed for task ‘:app:gener...
<groupId>io.grpc</groupId> <artifactId>grpc-netty-shaded</artifactId> <version>1.28.0</version> 在之前分享过一篇 Jetpack 综合实战应用 神奇宝贝(PokemonGo) 眼前一亮的 Jetpack + MVVM 极简实战 ,这个项目主要包了以下功能: 自定义 RemoteMediator 实现 network + db 的混合使用 ( RemoteMediator 是 Paging3 当中重要成员 ) 使用 Data Mapper 分离数据源 和 UI Kotlin Flow 结合 Retrofit2 + Room 的混合使用 Kotlin Flow 与 LiveData 的使用 override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ArticleViewHolder { return ArticleViewHolder(LayoutInflater.from(parent.context).inflate(R.layout.list_item,parent,false)) class ArticleVi
grpc-kotlin测试 测试:用kotlin编写的简单grpc服务器/客户端,protobuf生成的java文件,gradle multiproject,IDEA CE 为gRPC-Test项目启动gradle任务“ build”,以生成与protobuf相关的Java代码等。 使用Gradle任务“运行”启动服务器项目的服务器 使用Gradle任务“运行”为客户端项目启动客户端 grpc-kotlin-hello
Protobuf,类似于json和xml,是一种序列化结构数据机制,可以用于数据通讯等场景,相对于xml而言更小,相对于json而言解析更快,支持多语言。Protobuf使用 文件来定义数据格式,所以我们首先新建立一个文件,并在文件中填下如下内容: 这样我们就定义好了一个基本的Person对象,下面我们对文件中的关键字进行一一说明:syntax:指定proto的版本,protobuf目前有proto2和proto3两个常用版本,如果没有声明,则默认是proto2.package:指定包名。import:导
Kotlin 的构建过程中,出现 NoClassDefFoundError: org/gradle/api/services/BuildService 错误的原因可能是在编译时缺少了 Gradle 插件或相关的依赖。 具体的解决方案可能需要根据您的项目结构和构建设置来确定。这里列出一些可能有帮助的建议: - 确保在项目的 build.gradle 文件中正确地引用了 Kotlin Gradle 插件,例如: plugins { id 'org.jetbrains.kotlin.jvm' version '1.4.20' - 确保在项目的 build.gradle 文件中正确地声明了所需的依赖,例如: dependencies { implementation 'org.jetbrains.kotlin:kotlin-stdlib-jdk8:1.4.20' - 如果您正在使用 Android Studio,请确保在您的 app 模块的 build.gradle 文件中引用了 Android 插件,例如: apply plugin: 'com.android.application' - 在项目中使用了其他的 Gradle 插件,请确保这些插件也正确地配置并引用了相关的依赖。 如果这些建议都无法解决您遇到的问题,建议您检查项目中其他可能与构建过程有关的配置文件,并查看构建时输出的日志信息以了解更多的细节。