본문 바로가기

게으른개발자/공부

gRPC 간단 정리와 예제

배경

  • 마이크로서비스 기반으로 구축되는 시스템은  서비스 간 네트워크 통신 연결이 급증
  • 마이크로서비스용 통신을 구축할 때에 주로 사용하는 RESTful 서비스는 프로세스 간 통신을 구축할 때 부피가 크고, 비효율적

설명

  • Google에서 만든 오픈 소스 원격 프로시저 호출(RPC) 시스템
  • gRPC는 성능이 좋고, 전송 효율이 높은 서비스를 제공하는데 사용
  • gRPC는 여러 언어로 구현되어 있으며, 일반적으로 HTTP/2 프로토콜을 기반으로 구현되어 있음.

사용 순서

  1. 프로토콜 버퍼를 IDL(Interface Definition Language)로 사용해 서비스 인터페이스 정의 (메서드 종류, 호출 파라미터, 메시지 형식 등)
  2. 서비스 정의를 사용해  서버 스켈레톤(server skeleton)이라는 서버 측 코드 생성
  3. 서비스 정의를 사용해  클라이언트 스텁(client stub)이라는 클라이언트 측 코드 생성


장점

  • 프로세스 간 통신 효율성
    • Json이나 XML과 같은 텍스트 형식을 사용하는 대신 프로토콜 버퍼 기반 바이너리 프로토콜 사용
    • HTTP/2 위에 프로토콜 버퍼로 구현되어프로세스 간 통식 속도가 빠르고, 효율적
  • 간단 명확한 서비스 인터페이스와 스키마
    • 서비스 인터페이스를 정의한 후 나중에 세부 사항을 작업함에 따라, 안정적
  • 폴리그랏
    • 여러 프로그래밍 언어로 작동하도록 설계되어, 특정 언어에 구애 받지 않음
  •  
  • 이중 스트리밍 지원
  • 유용한 내장 기능 지원
    • 인증, 암호화, 압축, 로드밸런싱 등 기본적으로 지원

단점

  • 외부 서비스 부적합
    • 계약 기반 (contract-driven) & 강력한 타입 속성을 갖기 때문에, 외부에 노출 되는 서비스에 유연성이 낮음
  • 서비스 정의의 급격한 변경에 따른 개발 프로세스 복잡성
    • 변경 발생시 일반적으로 클라이언트  서버 코드 모두 생성 해야함
  • 상대적으로 작은 생태계
    • HTTP 프로토콜 대비 작은 생태계 및 브라우저&모바일 gRPC 지원은 초기단계

 


예제

Kotlin 기반 gRPC 서버 Python 기반 gRPC Client 를 구성하는 예제 안내

 

 

 

 

Kotlin 기반 gRPC 서버 (gradle 기반)

 

1. gradle 설정

import org.jetbrains.kotlin.gradle.tasks.KotlinCompile
import com.google.protobuf.gradle.*

plugins {
    kotlin("jvm") version "1.8.0"
    id("com.google.protobuf") version "0.8.19" // protobuf 관련 plugin 추가
    application
}

group = "me.goslim"
version = "1.0-SNAPSHOT"

repositories {
    mavenCentral()
}

val grpcVersion = "3.20.3"
val grpcKotlinVersion = "1.2.1"
val grpcProtoVersion = "1.44.1"


dependencies {
    testImplementation(kotlin("test"))

    // protobuf 관리와 stub을 자동으로 생성해주는 라이브러리 의존성 주입
    implementation("io.grpc:grpc-kotlin-stub:$grpcKotlinVersion")
    implementation("io.grpc:grpc-stub:$grpcProtoVersion")
    implementation("io.grpc:grpc-protobuf:$grpcProtoVersion")
    implementation("io.grpc:grpc-netty:$grpcProtoVersion")
    implementation("com.google.protobuf:protobuf-kotlin:$grpcVersion")
}

tasks.test {
    useJUnitPlatform()
}

tasks.withType<KotlinCompile> {
    kotlinOptions.jvmTarget = "17"
}

application {
    mainClass.set("MainKt")
}


// build 이후에 Stub 클래스가 생성되는 directory에 대해서 target으로 추가하기 위한 설정 추가
sourceSets{
    getByName("main"){
        java {
            srcDirs(
                "build/generated/source/proto/main/java",
                "build/generated/source/proto/main/kotlin",
                "build/generated/source/proto/main/grpckt",
                "build/generated/source/proto/main/grpc"
            )
        }
    }
}


//build 시점에 protobuf를 생성하기 위한 task를 추가. 해당 설정을 통해 Java Stub 파일과 Kotlin Stub파일을 생성할 수 있음.
protobuf {
    protoc {
        artifact = "com.google.protobuf:protoc:$grpcVersion"
    }
    plugins {
        id("grpc") {
            artifact = "io.grpc:protoc-gen-grpc-java:$grpcProtoVersion"
        }
        id("grpckt") {
            artifact = "io.grpc:protoc-gen-grpc-kotlin:$grpcKotlinVersion:jdk7@jar"
        }
    }
    generateProtoTasks {
        all().forEach {
            it.plugins {
                id("grpc")
                id("grpckt")
            }
            it.builtins {
                id("kotlin")
            }
        }
    }
}

 

 

2.서비스 인터페이스 정의

syntax = "proto3";


service YoungOldService { //서비스 정의
  rpc isYoung (Person) returns (YouthResponse) {} // 서비스 함수 정의
}

message Person{ //서비스 요청값
  string name = 1;
  int32 age = 2;
}

message  YouthResponse{ //서비스 응답값
  bool result = 1;
}

 

3. gRPC 서버 구현

class YoungOldService : YoungOldServiceGrpcKt.YoungOldServiceCoroutineImplBase() {
    private val youngAge: Int = 30

    override suspend fun isYoung(request: Person): YouthResponse {
    	println("request = ${request}")
        return YouthResponse.newBuilder().setResult(request.age < youngAge).build()
    }
}

 

4. gRPC 서버 실행

import io.grpc.ServerBuilder


fun main(args: Array<String>) {
    println("gRPC 서버시작")
    gRPCServer()
    println("gRPC 서버끝")
}

fun gRPCServer() {
    val youngOldService = YoungOldService()
    val server = ServerBuilder
        .forPort(50051)
        .addService(youngOldService)
        .build()

    Runtime.getRuntime().addShutdownHook(Thread {
        server.shutdown()
        server.awaitTermination()
    })

    server.start()
    server.awaitTermination()
}

 

 

 

Python 기반 gRPC Client 

 

1. gRPC 모듈 설치

python3 -m pip install --user grpcio-tools

 

2. gRPC 모듈을 이용해 stub파일 생성

python3 -m grpc_tools.protoc -I. --python_out=. --grpc_python_out=. person.proto

 

3. gRPC Client 구현

from __future__ import print_function

import grpc

import person_pb2
import person_pb2_grpc


def run():
    channel = grpc.insecure_channel('localhost:50051')
    stub = person_pb2_grpc.YoungOldServiceStub(channel)
    response = stub.isYoung(person_pb2.Person(name="Khan", age = 34))
    print("client received: " + str(response.result))


if __name__ == '__main__':
    run()

 

 

실행결과

 

 

gRPC Server 실행결과

 

 

gRPC Client 실행결과

참고

https://grpc.io/docs/what-is-grpc/introduction/

 

Introduction to gRPC

An introduction to gRPC and protocol buffers.

grpc.io

https://www.digihunch.com/2022/10/graphql-and-grpc/

 

GraphQL and gRPC

REST API has been successful. However, GraphQL and gRPC are competing with it. GraphQL has query capability, and gRPC is performant on HTTP/2

www.digihunch.com

https://www.baeldung.com/kotlin/grpc

https://cla9.tistory.com/178