gRPC가 무엇인지 알아보기 전에 RPC가 무엇인지 알아보자.
RPC란?
RPC(Remote Procedure Call - 원격 프로시저 호출)는 다른 위치에서 클라이언트 요청을 통해 원격 프로세스를 실행하는 기술이다. RPC 사용의 여러가지 문제로 인해 개발자는 오늘날 대부분의 최신 분산 컴퓨팅 솔루션에서 RPC 사용을 피하고 REST 아키텍처를 선호하고 있다.
RPC 문제점
RPC의 문제점은 무엇이길래 이렇게 사용을 안하게 되었을까?
Sam Newman의 Building Microservices: Designing Fine-Grained Systems 는 마이크로서비스에서 RPC를 사용하는 3가지 이유가 여전히 의심스럽다고 언급했다. 주요 기술 결함으로는 다음과 같다.
- Java RMI와 같은 기술은 플랫폼에 크게 의존한다.
- 로컬 호출은 remort 호출과 다르다. 마샬링 및 비마샬링 비용이 상당하고 네트워크 종속성이 높다.
- 취약성: 서버-클라이언트 스텁의 종속성 문제는 서버 메서드를 변경할 때마다 클라이언트도 재생성해야 하는 번거로움이 있다.
gRPC란?
이러한 여러가지 문제점을 보완할 수 있도록 gRPC는 구글이 최초로 개발한 오픈 소스 원격 프로시저 호출 프레임워크이다. 로드 밸런싱, 추적, 상태 확인 및 인증을 위한 플러그형 지원을 통해 데이터 센터 안팎에서 서비스를 효율적으로 연결할 수 있고 또한 디바이스, 모바일 애플리케이션 및 브라우저를 백엔드 서비스에 연결하는 분산 컴퓨팅의 라스트 마일에도 적용할 수 있다.
gRPC 장점
하지만 밀리초를 중요시하는 서비스등 성능에 대한 장점으로 인하여 RPC에 대한 수요가 높은 이유가 있다.
간단한 서비스 정의
강력한 바이너리 직렬화 도구 세트 및 언어인 프로토콜 버퍼를 사용하여 서비스를 정의한다.
빠르게 시작하고 확장
단일 라인으로 런타임 및 개발 환경을 설치하고 프레임워크를 통해 초당 수백만 개의 RPC로 확장한다.
다양한 언어 및 플랫폼에서 작동
다양한 언어 및 플랫폼으로 서비스에 대한 관용적 클라이언트 및 서버 스텁 자동 생성한다.
gRPC 공식 문서에 따르면 이미 C#/.NET, C++, Dart, Go, Java, Kotlin/JVM, Node.js, Objective-C, PHP, Python, Ruby를 지원한다. 그리고 가장 중요한 것은 많은 프로그래밍 언어에 대해 즉시 사용 가능한 코드 생성을 제공한다는 것이다.
양방향 스트리밍 및 통합 인증
HTTP/2 기반 전송을 통한 양방향 스트리밍 및 완전히 통합된 플러그 가능 인증을 할 수 있다.
gRPC는 REST 아키텍처 솔루션에서 사용할 수 없는 스팀 데이터를 허용한다. 클라이언트와 서버가 많은 양의 데이터를 처리하거나 시간이 많이 걸리는 프로세스를 처리하는 경우 gRPC 스트리밍이 최상의 솔루션이다.
기계 지향 페이로드
가장 먼저 RESTfull 아키텍처를 사용하는 경우 서비스 간 통신조차도 기계가 아닌 인간과 더 호환되는 무거운 JSON/XML을 통해 발생한다. 그러나 gRPC는 경량 바이너리 형식을 사용하여 통신한다.
Hello World Server 구축
구축 환경
- Java 1.8
- Gradle 7.4.2
전체 소스
1. 인터페이스 정의 (Protocol Buffers)
서비스 인터페이스와 페이로드 메시지의 구조를 모두 정의하기 위해 gRPC는 프로토콜 버퍼라고 하는 Google에서 개발한 IDL(인터페이스 정의 언어)을 사용한다.
syntax = "proto3";
service HelloWorldService {
rpc hello(HelloRequest) returns (HelloResponse) {}
}
message HelloRequest {
string text = 1;
}
message HelloResponse {
string text = 1;
}
2. build.gradle 정의
apply plugin: 'java'
apply plugin: 'com.google.protobuf'
buildscript {
repositories {
mavenCentral()
}
dependencies {
classpath 'com.google.protobuf:protobuf-gradle-plugin:0.8.8'
}
}
// Note: for IntelliJ IDE to mark the generated files as source.
sourceSets {
src {
main {
java {
srcDirs 'build/generated/source/proto/main/grpc'
srcDirs 'build/generated/source/proto/main/java'
}
}
}
}
// Note: Make sure to modify the gRPC version if it is no
// longer compatible w/ the latest gradle version.
//
// Visit https://mvnrepository.com/artifact/io.grpc/grpc-protobuf
def grpcVersion = '1.29.0'
dependencies {
repositories {
mavenCentral()
}
testImplementation 'junit:junit:4.13'
compile "io.grpc:grpc-netty:${grpcVersion}"
compile "io.grpc:grpc-protobuf:${grpcVersion}"
compile "io.grpc:grpc-stub:${grpcVersion}"
compile group: 'com.google.protobuf', name: 'protobuf-java-util', version: '3.12.2'
compileOnly group: "javax.annotation", name: "javax.annotation-api", version: "1.3.2"
compileOnly group: "org.jetbrains", name: "annotations", version: "13.0"
}
protobuf {
protoc {
artifact = 'com.google.protobuf:protoc:3.12.2'
}
plugins {
grpc {
artifact = "io.grpc:protoc-gen-grpc-java:${grpcVersion}"
}
}
generateProtoTasks {
all()*.plugins {
grpc {}
}
}
}
Gradle Build 실행한다.
D:\workspace\HelloWorldServer>.\gradlew build
Deprecated Gradle features were used in this build, making it incompatible with Gradle 7.0.
Use '--warning-mode all' to show the individual deprecation warnings.
See https://docs.gradle.org/6.8/userguide/command_line_interface.html#sec:command_line_warnings
BUILD SUCCESSFUL in 4s
10 actionable tasks: 5 executed, 5 up-to-date
D:\workspace\HelloWorldServer>.\gradlew build
Deprecated Gradle features were used in this build, making it incompatible with Gradle 7.0.
Use '--warning-mode all' to show the individual deprecation warnings.
See https://docs.gradle.org/6.8/userguide/command_line_interface.html#sec:command_line_warnings
BUILD SUCCESSFUL in 2s
10 actionable tasks: 10 up-to-date
3. HelloWorldService Implement 클래스 작성
import io.grpc.stub.StreamObserver;
public class HelloWorldServiceImpl extends HelloWorldServiceGrpc.HelloWorldServiceImplBase {
@Override
public void hello(Hello.HelloRequest request, StreamObserver<Hello.HelloResponse> responseObserver) {
System.out.println("Handling hello endpoint: " + request.toString());
String text = request.getText() + " World";
Hello.HelloResponse response = Hello.HelloResponse.newBuilder().setText(text).build();
responseObserver.onNext(response);
responseObserver.onCompleted();
}
}
4. HelloWorldServer Implement 클래스 작성
import java.io.IOException;
import io.grpc.Server;
import io.grpc.ServerBuilder;
public class HelloWorldServer {
private static final int PORT = 5000;
private Server server;
public void start() throws IOException {
server = ServerBuilder.forPort(PORT).addService(new HelloWorldServiceImpl()).build().start();
}
public void blockUntilShutdown() throws InterruptedException {
if (server == null) {
return;
}
server.awaitTermination();
}
public static void main(String[] args) throws InterruptedException, IOException {
System.out.println("start HelloWorldServer...");
HelloWorldServer server = new HelloWorldServer();
server.start();
server.blockUntilShutdown();
}
}
5. HelloWorldServer 실행
HelloWorldServer를 Java Application으로 실행시켜본다.
start HelloWorldServer...
D:\workspace\HelloWorldServer>netstat -an | findstr 5000
TCP 0.0.0.0:5000 0.0.0.0:0 LISTENING
TCP [::]:5000 [::]:0 LISTENING
'백엔드 > 개발' 카테고리의 다른 글
OSGi에 대하여 알아보자 (0) | 2022.05.03 |
---|---|
Java OSGi Tutorial (0) | 2022.05.03 |
Spring Boot - Hello World 시작해보기 (0) | 2022.04.13 |
Spring Boot 환경설정 (0) | 2022.04.13 |
Django-DSL 동적 INDEX 생성 (0) | 2019.02.03 |