LoginSignup
0
0

KotlinでgRPC通信

Last updated at Posted at 2024-06-05

gRPCとは

  • Googleが開発しているPRC (Remote Procedure Call) フレームワーク
  • HTTP/2
  • Protocol Buffers
    • IDL(インターフェース定義言語)を使用する
  • ハイパフォーマンス
  • マイクロサービスでのサービス間通信

build.gradle.ktsの設定(Java8)

  • 各ライブラリのバージョンの指定が難しいので、ここでは確実に動作するバージョンを指定している(下手に最新にすると動かない)
  • Intellij IDEAでのJavaのバージョンの指定は以下の3カ所変更する必要がある
    • JAVA_HOME
    • ファイル→プロジェクト構造→プロジェクト→SDK
    • ファイル→設定→ビルド、実行、デプロイメント→ビルドツール→Gradle→Gradle JVM
  • ./gradlew -v でのgradleバージョンは、6.6.1
build.gradle.kts
import com.google.protobuf.gradle.generateProtoTasks
import com.google.protobuf.gradle.id
import com.google.protobuf.gradle.plugins
import com.google.protobuf.gradle.protobuf
import com.google.protobuf.gradle.protoc

plugins {
    kotlin("jvm") version "1.4.30"
    // GradleでProtocal Buffersを使うためのプラグイン
    id("com.google.protobuf") version "0.8.15"
    id("idea")
}

repositories {
    gradlePluginPortal()
    jcenter()
    google()
}

dependencies {
    implementation(kotlin("stdlib"))
    implementation("org.jetbrains.kotlinx:kotlinx-coroutines-core:1.4.2")

    // gRPCでサーバーと通信するクライアント部分の実装をするKotlinのライブラリ
    implementation("io.grpc:grpc-kotlin-stub:1.0.0")
    // NettyでサーバーでgPRCアプリケーションを立ち上げるために必要なライブラリ
    implementation("io.grpc:grpc-netty:1.35.0")

    compileOnly("javax.annotation:javax.annotation-api:1.3.2")
}

// protocでのコード生成を実行するGradleタスク
protobuf {
    // protocのパッケージ、バージョンを指定
    protoc { artifact = "com.google.protobuf:protoc:3.15.1" }
    plugins {
        // それぞれJava、Kotlin用のgRPCプラグインを指定
        // Kotlinで生成したコードは、Javaで生成したコードを呼び出すため、Javaも必要
        id("grpc") {
            artifact = "io.grpc:protoc-gen-grpc-java:1.36.0"
        }
        id("grpckt") {
            artifact = "io.grpc:protoc-gen-grpc-kotlin:1.0.0:jdk7@jar"
        }
    }
    // コード生成を実行するタスク
    generateProtoTasks {
        all().forEach {
            it.plugins {
                id("grpc")
                id("grpckt")
            }
        }
    }
}

IDLの定義

  • .proto という拡張子
  • Kotlin、Java、Go、C#、Pythonなどへのコード生成には、protocというツールを使用する
src\main\proto\greeter.proto
// Protocol Buffersのバージョン指定(デフォルトは2系)
syntax = "proto3";

// 生成されるファイルのクラスのパッケージ
package example.greeter;

// 各種オプションを定義する
// ファイルを分割して出力する
option java_multiple_files = true;

// インタフェースを定義する(Spring Bootで例えると、Controllerのような部分)
service Greeter {
  // `rpc メソッド名 (リクエストの型) returns レスポンスの型`
  rpc Hello (HelloRequest) returns (HelloResponse);
}

// リクエスト、レスポンスのデータの定義
// `message リクエスト、レスポンスの型`
message HelloRequest {
  // フィールドの番号(ここでは1)は、そのフィールドの一意の識別子
  string name = 1;
}

message HelloResponse {
  string text = 1;
}

Protol Buffersによるコード生成

$ ./gradlew generateProto

生成されるファイル

image.png

  • grpc配下・・・ gRPC通信を実現するためのインタフェース
    • GreeterGrpc.kt
  • grpckt配下・・・データのシリアライズ、デシリアライズなど
    • GreeterOuterClassGrpcKt.kt
  • java配下・・・デフォルトで生成されるコード、リクエスト、レスポンスとそのビルダー
    • GreeterOuterClass.java
    • HelloRequest.java
    • HelloRequestOrBuilder.java
    • HelloResponse.java
    • HelloResponseOrBuilder.java

gRPCサーバーの実装

  • Hello関数を実装
src\main\kotlin\example\greeter\server\GreeterService.kt
package example.greeter.server

import example.greeter.GreeterGrpcKt
import example.greeter.HelloRequest
import example.greeter.HelloResponse

class GreeterService : GreeterGrpcKt.GreeterCoroutineImplBase() {
    override suspend fun hello(request: HelloRequest) = HelloResponse.newBuilder()
        // リクエストに対して、`Hello`を付けて返す
        .setText("Hello ${request.name}")
        .build()
}
  • サーバーの起動
src\main\kotlin\example\greeter\server\GreeterServer.kt
package example.greeter.server

import io.grpc.ServerBuilder

private const val PORT = 50051

fun main() {
    // サーバーのオブジェクトを生成
    val server = ServerBuilder
        .forPort(PORT)
        // 起動対象のサービスを登録
        .addService(GreeterService())
        .build()

    // サーバーの起動
    server.start()
    println("Started. port:$PORT")

    // アプリケーションが停止されるまでサーバーのリクエストを受け付ける
    server.awaitTermination()
}

gRPCクライアントの実装

src\main\kotlin\example\greeter\client\GreeterClient.kt
package example.greeter.client

import example.greeter.GreeterGrpcKt
import example.greeter.HelloRequest
import io.grpc.ManagedChannelBuilder
import kotlinx.coroutines.async
import kotlinx.coroutines.runBlocking
import java.util.concurrent.TimeUnit

private const val HOST = "localhost"
private const val PORT = 50051

fun main() = runBlocking {
    // Channelを生成して、gRPCサーバーに接続
    val channel = ManagedChannelBuilder.forAddress(HOST, PORT)
        // SSLを無効化(SSLが必要であればこの項目を削除する)
        .usePlaintext()
        .build()

    try {
        // サーバーに対してリクエストを送信するためのStubを生成
        // Coroutineで非同期処理を行う
        val stub = GreeterGrpcKt.GreeterCoroutineStub(channel)

        val name = "Kotlin"
        // リクエストを生成 HelloRequestのnameに`Kotlin`を設定
        val request = HelloRequest.newBuilder().setName(name).build()
        // helloはsuspend関数なので、runBlocking内でasyncを使って非同期処理を行う
        val response = async { stub.hello(request) }

        println("Response Text: ${response.await().text}")
    } finally {
        channel.shutdown().awaitTermination(5, TimeUnit.SECONDS)
    }
}

動作確認

サーバーの起動

  • GreeterServer.ktmain 関数を実行
Started. port:50051

クライアントの起動

  • GreeterClient.ktmain 関数を実行
Response Text: Hello Kotlin

ソースコード

参考

Kotlin サーバーサイドプログラミング実践開発

0
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
0