배경
- 마이크로서비스 기반으로 구축되는 시스템은 서비스 간 네트워크 통신 연결이 급증
- 마이크로서비스용 통신을 구축할 때에 주로 사용하는 RESTful 서비스는 프로세스 간 통신을 구축할 때 부피가 크고, 비효율적
설명
- Google에서 만든 오픈 소스 원격 프로시저 호출(RPC) 시스템
- gRPC는 성능이 좋고, 전송 효율이 높은 서비스를 제공하는데 사용
- gRPC는 여러 언어로 구현되어 있으며, 일반적으로 HTTP/2 프로토콜을 기반으로 구현되어 있음.
사용 순서
- 프로토콜 버퍼를 IDL(Interface Definition Language)로 사용해 서비스 인터페이스 정의 (메서드 종류, 호출 파라미터, 메시지 형식 등)
- 서비스 정의를 사용해 서버 스켈레톤(server skeleton)이라는 서버 측 코드 생성
- 서비스 정의를 사용해 클라이언트 스텁(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()
실행결과
참고
https://grpc.io/docs/what-is-grpc/introduction/
https://www.digihunch.com/2022/10/graphql-and-grpc/
https://www.baeldung.com/kotlin/grpc
'게으른개발자 > 공부' 카테고리의 다른 글
DB three-valued logic (NULL의 의미) (0) | 2023.03.26 |
---|---|
DB(PostgreSQL) 인덱스와 활용방법 (컴포지트 인덱스, 커버링 인덱스) (0) | 2023.03.13 |
OSI 7Layer와 L1, L2, L3, L4, L7 스위치 (0) | 2022.10.30 |
REST vs. RESTful (0) | 2022.09.13 |
JPA OneToMany 중복조회 (0) | 2022.07.31 |