1. gRPC๋?
gRPC๋ฅผ ์์๋ณด๊ธฐ ์์ RPC๋ Remote Procedure Call์ ์ฝ์๋ก ๋ค๋ฅธ ์ปดํจํฐ์ ์๋ ์ด๋ค ๊ธฐ๋ฅ์ ์๊ธฐ ๊ธฐ๋ฅ์ธ ๊ฒ ์ฒ๋ผ ์คํํ ์ ์๋ ํ๋กํ ์ฝ์ด๋ค.
RPC๋ ์ธ์ด ๋ ๋ฆฝ์ ์ด๋ฏ๋ก ์๋ก ๋ค๋ฅธ ํ๋ก๊ทธ๋๋ฐ ์ธ์ด๋ฅผ ์ฌ์ฉํ๋ ์๋ฒ์ ํด๋ผ์ด์ธํธ ์ฌ์ด์์๋ ์ฌ์ฉ๋ ์ ์๋ค.
๊ฐ๋ฐ์๋ ์๋ฒ๋ ํต์ ๊ณผ์ ์ ๋ํด ํ๋ํ๋ ์์๋๊ฑฐ๋ ๊ณ ๋ คํ ํ์ ์์ด ๋ง์น ๋ก์ปฌ ํจ์๋ฅผ ๊ฐ์ ธ๋ค ์ฐ๋ฏ ๊ธฐ๋ฅ๋ค์ ํ์์ ๋ฐ๋ผ ์ฌ์ฉํ๋๋ฐ ์ง์คํ ์ ์๋ค.
gRPC๋ ๊ตฌ๊ธ์์ ๊ฐ๋ฐํ ์คํ์์ค ๊ธฐ์ ๋ก ๋ค์ํ ํ๋ซํผ์์ ์๋ก ๋ค๋ฅธ ์ธ์ด๋ก ์์ฑ๋ ์ ํ๋ฆฌ์ผ์ด์ ๊ฐ ํต์ ์ ์ํ ํจ์จ์ ์ด๊ณ ๊ฐํธํ ๋ฐฉ๋ฒ์ ์ ๊ณตํ๋ค. gRPC๋ ์ด์ ์ RPC ์์คํ ๊ณผ ๋ฌ๋ฆฌ ๋์ฉ๋ ๋ฐ์ดํฐ ์ ์ก ๋ฐ ๋ฉํฐํ๋ซํผ ์ง์ ๋ฑ์ ๋ฌธ์ ๋ฅผ ํด๊ฒฐํ ์ ์๋ค. ํ์ฌ๊น์ง๋ ์ง์์ ์ผ๋ก ์ ๋ฐ์ดํธ์ ๊ฐ์ ์ด ์ด๋ฃจ์ด์ง๋ฉฐ, ํด๋ผ์ฐ๋ ๋ค์ดํฐ๋ธ ์ํคํ ์ฒ์์ ํธํ์ฑ๋ ๊ฐํ๋๊ณ ์๋ค.
2. gRPC ๋ฑ์ฅ ๋ฐฐ๊ฒฝ
gRPC ์ด์ ์ RPC, Rest API ํต์ ์ด ์กด์ฌํ๋ค.
RPC
RPC๋ IDL์ ์ฌ์ฉํ์ฌ ์ธํฐํ์ด์ค๋ฅผ ๋ช ์ํ๋ค. IDL์ด๋ Interface Definition Language ์ฝ์๋ก ์ํํธ์จ์ด ์ปดํฌ๋ํธ์ ์ธํฐํ์ด์ค๋ฅผ ๋ฌ์ฌํ๊ธฐ ์ํ ๋ช ์ธ ์ธ์ด์ด๋ค.
์ด๋ ํ ์ธ์ด์ ๊ตญํ๋์ง ์๋ ์ธ์ด ์ค๋ฆฝ์ ์ธ ๋ฐฉ๋ฒ์ผ๋ก ์ธํฐํ์ด์ค๋ฅผ ๋ฌ์ฌํจ์ผ๋ก์จ, ๊ฐ์ ์ธ์ด๋ฅผ ์ฌ์ฉํ์ง ์๋ ์ํํธ์จ์ด ์ปดํฌ๋ํธ ์ฌ์ด์ ํต์ ์ ๊ฐ๋ฅํ๊ฒ ํ๋ค.
RPC์ ๋์ ํ๋ฆ์ ๋ค์๊ณผ ๊ฐ๋ค.
- IDL์ ํตํด ํธ์ถ์ ๋ํ ์ธํฐํ์ด์ค๋ฅผ ์ ์
- ์ ์๋ IDL์ ๊ธฐ๋ฐ์ผ๋ก rpcgen์ด๋ผ๋ rpc ํ๋กํ ์ฝ ์ปดํ์ผ๋ฌ๋ฅผ ํตํด ์ฝ๋(stub)๊ฐ ์์ฑ๋๋ค. stub์ ํตํด Client๋ procedure ํธ์ถ์ ์ํ ์ฐธ์กฐ์๊ฐ ์๊ฒผ๊ณ , Server๋ procedure ์ดํด๋ฅผ ์ํ ์ฐธ์กฐ๊ฐ ์๊ธฐ๊ฒ ๋จ.
- Client๋ ์์ ์ ํ๋ก๊ทธ๋จ์์ ํจ์๋ฅผ ํธ์ถํ๋ ๊ฒ์ฒ๋ผ stub์ ์ ์๋ ํจ์๋ฅผ ์ฌ์ฉ.
- Client์์ Stub์ ์ ์๋ ํจ์๋ฅผ ์ฌ์ฉํ ๋ Client stub์ RPC ๋ฐํ์์ ํตํด ํจ์ ํธ์ถ.
- Server๋ ์์ ๋ procedure ํธ์ถ์ ๋ํ ์ฒ๋ฆฌ ํ ๊ฒฐ๊ณผ ๊ฐ ๋ฐํ.
- ์ต์ข ์ ์ผ๋ก Client ํ๋ก๊ทธ๋จ์ ์๋ฒ์ ๊ฒฐ๊ณผ ๊ฐ์ ๋ฐํ ๋ฐ์.
RPC๋ฅผ ํตํด ํ๋ถ ๋คํธ์ํฌ ํ๋กํ ์ฝ์ ๋ํด ์ ๊ฒฝ ์ฐ์ง ์์๋ ๋๊ณ , ํ๋ก๊ทธ๋จ ๊ฐ๋ฐ์๋ง ์ง์คํ ์ ์์์ง๋ง ๋จ์ ๋ ์กด์ฌํ๋ค.
RPC ์ฝ๋๋ฅผ ์ดํดํ๊ธฐ ์ด๋ ค์ธ ๋ฟ๋๋ฌ ์๋ฌ ๋ฐ์ ์ ๋๋ฒ๊น ์ด ์ด๋ ค์ด ๋จ์ ์ด ์กด์ฌํ๋ค.
์ด๋ก ์ธํด ๋ฐ์ดํฐ ํต์ ์ Web์ ํ์ฉํด ํด๋ณด๋ ค ํ๊ณ ํ์ํ ๊ฒ์ด REST API ์ด๋ค.
REST
REST(REpresentational State Transfer)๋ HTTP1.1 ๊ธฐ๋ฐ URI๋ฅผ ํตํด ์์์ ๋ช ์ํ๊ณ , HTTP Method(POST, GET, PUT, DELETE)๋ฅผ ํตํด ํด๋น ์์์ ๋ํ CRUD๋ฅผ ์ ์ฉํ๋ ๊ฒ์ ์๋ฏธํ๋ค.
REST๋ HTTP ํ๋กํ ์ฝ์ ์ธํ๋ผ๋ฅผ ๊ทธ๋๋ก ์ฌ์ฉํ๊ธฐ ๋๋ฌธ์ ๋ณ๋์ ์ธํ๋ผ๋ฅผ ๊ตฌ์ถํ ํ์๋ ์๊ณ , REST API ๋ฉ์์ง๊ฐ ์๋ํ๋ ๋ฐ๋ฅผ ๋ช ํํ๊ฒ ๋ํ๋ด๊ธฐ ๋๋ฌธ์ ์ฝ๊ฒ ํ์ ํ ์ ์๋ค.
ํ์ง๋ง, REST๋ ํ์ค ์์ฒด๊ฐ ์กด์ฌํ์ง ์์ ์ ์๊ฐ ํ์ํ๊ณ , ์ฌ์ฉํ ์ ์๋ ๋ฉ์๋๋ 4๊ฐ์ง ๋ฐ์ ์์๋ค.
REST๊ฐ ๋ณดํธ์ ์ผ๋ก ์ฌ์ฉ๋๋ ์ค์, 2016๋ ๊ตฌ๊ธ์์ ๊ฐ๋ฐํ ์คํ์์ค RPC ํ๋ ์์ํฌ์ธ gRPC๊ฐ ๋ฑ์ฅํ๋ค.
gRPC
google์ 1์ฃผ์ผ ๋์ ๋์ฐ๋ ์ผ์ฃผ์ผ ๋์ ๋์ฐ๋ ์ปจํ ์ด๋๊ฐ ์ฝ 20์ต๊ฐ, 1์ด ๋์ ๋์ง๋ ์๊ฒฉ ํธ์ถ์ ์๋ 100์ต ๋ฒ์ด๋ผ๊ณ ํ๋ค. ๊ตฌ๊ธ์ ์ด๋ฌํ ๋๊ท๋ชจ ์๋น์ค๋ฅผ ์ด์ํ๊ธฐ ์ํด gRPC๋ฅผ ๊ฐ๋ฐํ๋ค.
gPRC๋ IDL๋ก PB(Protocol Buffer)๋ฅผ ์ฌ์ฉํ๋ค.
3. gRPC์ ์ฅ์
gRPC๋ HTTP/1.1 ์ด ์๋ HTTP/2 ๋ฐฉ์์ ์ฌ์ฉํ๋ค. ๋ ๊ฐ์ง ์ฐจ์ด์ ์ ๋ญ๊น??
HTTP/1.1 vs HTTP/2
HTTP1.1์ ๊ธฐ๋ณธ์ ์ผ๋ก ํ Connection์ ํ๋์ ์์ฒญ๊ณผ ์๋ต์ ์ฒ๋ฆฌํ๊ธฐ ๋๋ฌธ์ ๋์ ์ ์ก, ๋ค์์ ๋ฆฌ์์ค๋ฅผ์ฒ๋ฆฌํ๊ธฐ์ ์ฑ๋ฅ ์ด์๊ฐ ์๋ค.
๋ํ ๋ฌด๊ฑฐ์ด Header๊ฐ ์์ฒญ ๋ง๋ค ์ค๋ณต ์ ๋ฌ๋์ด ๋นํจ์จ ์ ์ด์๋ค.
HTTP/2๋ ์ฑ๋ฅ ๋ฟ๋ง ์๋๋ผ ์๋ ๋ฉด์์๋ ์ฐ์ํ๋ฐ ํ Connection์ ์ฌ๋ฌ ๊ฐ์ ๋ฉ์์ง๋ฅผ ์ฃผ๊ณ ๋ฐ์ ์ ์๊ณ , ๋ฌด๊ฑฐ์ ๋ Header๋ฅผ ์์ถํ์ฌ ์ค๋ณต์ ์ ๊ฑฐํ๊ณ ์ ๋ฌํ๊ธฐ ๋๋ฌธ์ HTTP/1.1์ ๋นํด์ ํจ์ฌ ํจ์จ์ ์ด์๋ค.
๋ํ ๋ฐ๋์ ํด๋ผ์ด์ธํธ์ ์์ฒญ์ด ๋ค์ด์์ผ ์๋ต์ ํ ์ ์๋ HTTP/1.1๊ณผ ๋ฌ๋ฆฌ, ์์ฒญ ์์ด๋ ์๋ฒ๊ฐ ๋ฆฌ์์ค๋ฅผ ์ ๋ฌํ ์ ์์ด ํด๋ผ์ด์ธํธ์ ์์ฒญ์ ์ต์ํ ํ ์ ์๋ค.
4. Protobuf ๋?
์์์ ์ธ๊ธํ๋ฏ์ด gRPC๋ IDL๋ก PB(Protocol Buffer)๋ฅผ ์ฌ์ฉํ๋ค. ํ๋กํ ์ฝ ๋ฒํผ๋ ๊ตฌ์กฐํ๋ ๋ฐ์ดํฐ๋ฅผ ์ง๋ ฌํ ํ๋ ๋ฐฉ์์ ์ฌ์ฉํ๋ค.
์ฌ๊ธฐ์ ์ง๋ ฌํ๋ Java ์์คํ ๋ด์์ ์ฌ์ฉํ๋ ๊ฐ์ฒด ๋๋ ๋ฐ์ดํฐ๋ฅผ Java ์์คํ ์ธ์์๋ ์ฌ์ฉํ ์ ์๋๋ก Byte ํํ๋ก ๋ฐ์ดํฐ๋ฅผ ๋ณํํ๋ ๊ธฐ์ ์ด๋ค.
Protocol Buffer๋ RPC์ IDL๊ณผ ๋์ผํ๊ฒ ์ค๋ฆฝ์ ์ธ ํํ๋ก ๋ฐ์ดํฐ ํ์ ์ ์ ์ํ๊ธฐ ์ํด์ proto File์ ์ ์ํ๋ค. ์ ์ํ Proto File์ ์ฌ์ฉํ๋ ค๋ฉด ๊ฐ ์ธ์ด์ ๋ง๊ฒ ๋ฐ์ดํฐ ํด๋์ค๋ก ์์ฑํด์ผ ํ๊ธฐ ๋๋ฌธ์ protoc ์ปดํ์ผ๋ฌ๊ฐ ํ์ํ๋ค. (RPC์์ rpcgen)
protoc ์ปดํ์ผ๋ฌ๋ก Profo File์ ์ปดํ์ผํ๋ฉด ๊ฐ ์ธ์ด์ ๋ง๊ฒ ๋ฐ์ดํฐ ํด๋์ค ํ์ผ์ ์์ฑํด์ฃผ๊ธฐ ๋๋ฌธ์ด๋ค.
Proto File ์ ์ => protoc ์ปดํ์ผ๋ฌ๋ฅผ ์ด์ฉํ์ฌ ์ปดํ์ผ => ์ํ๋ ์์ค ํ์ผ ์์ฑ
5. Spring Boot ์์ gRPC ์ฌ์ฉํ๊ธฐ
gRPC - Client
- build.gradle
// grpc
buildscript {
ext {
protobufVersion = '3.25.1'
protobufPluginVersion = '0.8.14'
grpcVersion = '1.58.1'
}
}
plugins {
id 'java'
id 'org.springframework.boot' version '3.3.5'
id 'io.spring.dependency-management' version '1.1.6'
// grpc
id 'com.google.protobuf' version '0.9.4'
}
group = 'com.example'
version = '0.0.1-SNAPSHOT'
java {
toolchain {
languageVersion = JavaLanguageVersion.of(21)
}
}
configurations {
compileOnly {
extendsFrom annotationProcessor
}
}
repositories {
mavenCentral()
}
dependencies {
implementation 'org.springframework.boot:spring-boot-starter-web'
compileOnly 'org.projectlombok:lombok'
annotationProcessor 'org.projectlombok:lombok'
testImplementation 'org.springframework.boot:spring-boot-starter-test'
testRuntimeOnly 'org.junit.platform:junit-platform-launcher'
// swagger
implementation 'org.springdoc:springdoc-openapi-starter-webmvc-ui:2.0.2'
// grpc
implementation "com.google.protobuf:protobuf-java-util:3.25.1"
implementation 'com.google.protobuf:protobuf-java:3.25.1'
implementation 'net.devh:grpc-client-spring-boot-starter:2.15.0.RELEASE' // ํด๋ผ์ด์ธํธ ์์กด์ฑ ์ถ๊ฐ
runtimeOnly "io.grpc:grpc-netty-shaded:${grpcVersion}"
implementation "io.grpc:grpc-protobuf:${grpcVersion}"
implementation "io.grpc:grpc-stub:${grpcVersion}"
compileOnly 'org.apache.tomcat:annotations-api:6.0.53'
implementation 'org.springframework.cloud:spring-cloud-starter-openfeign'
}
// grpc
protobuf {
protoc {
artifact = "com.google.protobuf:protoc:${protobufVersion}"
}
clean {
delete generatedFilesBaseDir
}
plugins {
grpc {
artifact = "io.grpc:protoc-gen-grpc-java:${grpcVersion}"
}
}
generateProtoTasks {
all()*.plugins {
grpc{}
}
}
}
ext {
set('springCloudVersion', "2023.0.2")
}
dependencyManagement {
imports {
mavenBom "org.springframework.cloud:spring-cloud-dependencies:${springCloudVersion}"
}
}
tasks.named('test') {
useJUnitPlatform()
}
Proto file์ ์ ์ํด์ค๋ค. ์ด ํ์ผ์ server์ client ๋ชจ๋ ๊ฐ์ง๊ณ ์์ด์ผ ํ๋ค. ํ์ฅ์ ๋ช ์ .proto ์ด๋ค.
๐ฆsrc
โฃ ๐main
โ โฃ ๐java
โ โ โ ๐com
โ โ โ โ ๐example
โ โ โ โ โ ๐grpcclient
โ โฃ ๐proto
โ โ โ ๐test.proto
โ โ ๐resources
โ โ โ ๐application.yml
- proto ํ์ผ
syntax = "proto3";
option java_multiple_files = true;
option java_outer_classname = "SampleProto";
option java_package = "com.example.grpc.proto";
package com.example.grpc;
// req ๋ฉ์์ง ์ ์
message SampleRequest {
string userId = 1; // userId ํ๋์ ์๋ณ์: 1
string message = 2; // messagee ํ๋์ ์๋ณ์: 2
}
// res ๋ฉ์์ง ์ ์
message SampleResponse {
string message = 1;
}
// ์๋น์ค ์ ์
service SampleService {
rpc SampleCall (SampleRequest) returns (SampleResponse) {}
}
- application.yml (client)
grpc:
client:
local-grpc-server: # ํด๋ผ์ด์ธํธ ์ด๋ฆ
address: "static://localhost:9090" # ์๋ฒ ์ฃผ์ (๋ก์ปฌ)
negotiationType: "plaintext"
server:
port: 8082
gRPC - Server
- build.gradle
client์ ๋์ผํ์ง๋ง ์๋ ๋ถ๋ถ๋ง ๋ณ๊ฒฝ
implementation 'net.devh:grpc-client-spring-boot-starter:2.15.0.RELEASE' // ํด๋ผ์ด์ธํธ ์์กด์ฑ ์ถ๊ฐ
=>
implementation 'net.devh:grpc-server-spring-boot-starter:2.15.0.RELEASE' // ์๋ฒ ์์กด์ฑ ์ถ๊ฐ
- proto ํ์ผ
client ์ ๋๊ฐ์ ํ์ผ ์์ฑ
- application.yml (server)
grpc:
server:
port: 9090 # gRPC ์๋ฒ ํฌํธ
server:
port: 8081
์์ฑ ํ build ํด์ฃผ๋ฉด build.generated ๋๋ ํ ๋ฆฌ ์์ proto ํ์ผ์์ ์์ฑํ service ํด๋์ค๊ฐ ์์ฑ๋๋ค.
- gRPC ์์ฒญ ์ฒ๋ฆฌ: SampleServiceGrpc ์ค๋ฒ๋ผ์ด๋ฉ (server)
@GrpcService
@Slf4j
public class GrpcServer extends SampleServiceGrpc.SampleServiceImplBase {
@Override
public void sampleCall(SampleRequest request, StreamObserver<SampleResponse> responseObserver) {
String userId = request.getUserId();
String message = request.getMessage();
// ๋น์ฆ๋์ค ๋ก์ง ์ฒ๋ฆฌ
String responseMessage = "Received message from userId: " + userId + ", message: " + message;
log.info("[gRPC server] message: {}", responseMessage);
// Response ์์ฑ
SampleResponse response = SampleResponse.newBuilder()
.setMessage(responseMessage)
.build();
// ์๋ต ๋ฐํ
responseObserver.onNext(response);
responseObserver.onCompleted();
}
}
์ฌ๊ธฐ์ SampleResponse์ SampleRequest๋ proto ํ์ผ์ buildํ๋ฉด์ ์์ฑ๋ ํด๋์ค๋ค์ด๋ค.
- gRPC ํธ์ถ (client)
@Service
@Slf4j
@RequiredArgsConstructor
public class GrpcClientService {
@GrpcClient("local-grpc-server")
private SampleServiceGrpc.SampleServiceBlockingStub sampleServiceBlockingStub;
private final SampleClient sampleClient;
public String sendMessage(String userId, String message) {
SampleRequest request = SampleRequest.newBuilder()
.setUserId(userId)
.setMessage(message)
.build();
SampleResponse response = sampleServiceBlockingStub.sampleCall(request);
log.info("[gRPC client] message: {}", response.getMessage());
return response.getMessage();
}
}
์คํ๋ง์์๋ @GrpcClient ์ด๋ ธํ ์ด์ ์ ์ฌ์ฉํ์ฌ gRPC Stub์ Spring์ DI(Dependency Injection) ๋ฐฉ์์ผ๋ก ์ฃผ์ ๋ฐ์ ์ฌ์ฉํ ์ ์๋ค. gRPC ํด๋ผ์ด์ธํธ๋ฅผ ๊ตฌ์ฑํ ๋ ํ์ํ ์ฑ๋ ์์ฑ, Stub ์์ฑ ๋ฑ์ ๊ณผ์ ์ ์๋ํํ์ฌ ๊ฐ๋ฐ ํธ์์ฑ์ ๋์ฌ์ค๋ค.
@GrpcClient ์ด๋ ธํ ์ด์ ์ ์ค์ ํ์ผ(application.yml)์ ์ ์๋ gRPC ์๋ฒ ์ ๋ณด์ ์ฐ๊ฒฐํ๋ค.
6. ์ฑ๋ฅ ๋น๊ต
Thread Group ์ค์ ์ ๋ค์๊ณผ ๊ฐ๋ค.
gRPC ์์ฒญ
HTTP ์์ฒญ
gRPC์ ๊ฒฝ์ฐ 1175 TPS
HTTP์ ๊ฒฝ์ฐ 154 TPS
๋ก ์ธก์ ๋๊ณ gRPC๊ฐ HTTP๋ณด๋ค ์ฝ 663%์ ๋น ๋ฅธ ์ฑ๋ฅ์ ๋ณด์ฌ์คฌ๋ค. ์ค์ ๋ก 2๋ฐฐ~10๋ฐฐ ๊ฐ๋ ๋น ๋ฅด๋ค๊ณ ํ๋ค.