Spring Boot - grpc client
1. 프로젝트 생성
2. build.gradle
buildscript {
// 24.07.21 기준 최신 버전
ext {
protobufVersion = '4.28.2'
protobufPluginVersion = '0.9.4'
grpcVersion = '1.65.1'
}
}
plugins {
id 'java'
id 'org.springframework.boot' version '3.3.4'
id 'io.spring.dependency-management' version '1.1.6'
// Protobuf 플러그인을 적용하여 .proto 파일을 컴파일할 수 있다. 여기서 버전은 ext에 정의된 protobufPluginVersion을 사용한다.
id 'com.google.protobuf' version "${protobufPluginVersion}"
}
group = 'com.example'
version = '0.0.1-SNAPSHOT'
java {
toolchain {
languageVersion = JavaLanguageVersion.of(17)
}
}
configurations {
compileOnly {
extendsFrom annotationProcessor
}
}
repositories {
mavenCentral()
}
dependencies {
implementation 'org.springframework.boot:spring-boot-starter-thymeleaf'
implementation 'org.springframework.boot:spring-boot-starter-web'
implementation 'org.springframework.boot:spring-boot-starter-websocket'
compileOnly 'org.projectlombok:lombok'
annotationProcessor 'org.projectlombok:lombok'
testImplementation 'org.springframework.boot:spring-boot-starter-test'
testRuntimeOnly 'org.junit.platform:junit-platform-launcher'
// grpc 프로토콜 버퍼를 사용하기 위한 핵심 라이브러리 (Protobuf 메시지의 직렬화 및 역직렬화를 지원합니다.)
implementation "com.google.protobuf:protobuf-java-util:${protobufVersion}"
implementation "com.google.protobuf:protobuf-java:${protobufVersion}"
// grpc 서버, 클라이언트 설정
implementation 'net.devh:grpc-spring-boot-starter:3.1.0.RELEASE' // Spring Boot와 gRPC의 통합을 간편하게 도와주는 스타터
implementation "io.grpc:grpc-netty-shaded:${grpcVersion}" // Netty Shaded 사용(gRPC 서버와 클라이언트의 Netty 전송 계층을 제공)
implementation "io.grpc:grpc-protobuf:${grpcVersion}" // Protobuf 메시지와 gRPC의 통합을 지원
implementation "io.grpc:grpc-stub:${grpcVersion}" // gRPC 클라이언트 스텁을 생성
compileOnly 'org.apache.tomcat:annotations-api:6.0.53' // 이걸 추가해야 gRPC 컴파일시 javax 어노테이션 오류가 발생하지 않는다.
}
protobuf {
// Protobuf 컴파일러를 지정하여 .proto 파일을 컴파일합니다.
protoc {
artifact = "com.google.protobuf:protoc:${protobufVersion}"
}
// gRPC 플러그인을 설정하여 Protobuf 파일로부터 gRPC 관련 코드를 생성합니다.
plugins {
grpc {
artifact = "io.grpc:protoc-gen-grpc-java:${grpcVersion}"
}
}
// 모든 프로토콜 버퍼 작업에 대해 gRPC 플러그인을 적용합니다.
generateProtoTasks {
all()*.plugins {
grpc {}
}
}
}
tasks.named('test') {
useJUnitPlatform()
}
implementation "com.google.protobuf:protobuf-java-util:${protobufVersion}"
implementation "com.google.protobuf:protobuf-java:${protobufVersion}"
역할: Protobuf 메시지의 직렬화 및 역직렬화를 지원한다. Protobuf는 데이터를 효율적으로 직렬화하기 위한 Google의 데이터 인터페이스 언어다.
protobuf-java-util: Protobuf 메시지의 직렬화 및 역직렬화를 지원하는 유틸리티 라이브러리다.
protobuf-java: Protobuf 메시지의 기본 직렬화 및 역직렬화를 지원하는 라이브러리다.
implementation 'net.devh:grpc-spring-boot-starter:3.1.0.RELEASE'
역할: Spring Boot와 gRPC의 통합을 간편하게 도와주는 starter 라이브러리다.
이 라이브러리는 gRPC 서버와 클라이언트를 쉽게 설정할 수 있도록 Spring Boot와 gRPC의 자동 구성을 제공한다.
implementation "io.grpc:grpc-netty-shaded:${grpcVersion}"
역할: gRPC 서버와 클라이언트의 Netty 전송 계층을 제공한다. Netty는 비동기 이벤트 주도 네트워크 애플리케이션 프레임워크로, 높은 성능과 유연성을 제공한다.
주의사항: 여기서 grpc-netty-shaded: 는 모든 종속성이 포함된 Netty 버전으로, 종속성 충돌 문제를 줄이는 데 도움이 된다. (최신 버전에서 shaded가 아닌 그냥 netty 의존성을 사용했더니 오류가 발생했다.)
implementation "io.grpc:grpc-protobuf:${grpcVersion}"
역할: Protobuf 메시지와 gRPC의 통합을 지원한다. Protobuf를 사용하여 gRPC 서비스 정의 파일(.proto 파일)을 기반으로 하는 메시지와 서비스를 생성할 수 있다.
implementation "io.grpc:grpc-stub:${grpcVersion}"
역할: gRPC 클라이언트 스텁을 생성한다. 클라이언트 스텁은 클라이언트가 서버의 gRPC 서비스를 호출할 수 있게 하는 간편한 인터페이스를 제공한다. (추후 스텁을 활용해서 편리하게 gRPC 클라이언트에서 요청을 보낼 수 있다.)
compileOnly 'org.apache.tomcat:annotations-api:6.0.53'
역할: gRPC 컴파일 시 javax 어노테이션 오류를 방지한다. 일부 gRPC 및 Protobuf 코드 생성기는 javax 어노테이션을 사용하며, 이 의존성이 필요하다.
사용 이유: 컴파일 시 발생하는 어노테이션 관련 오류를 방지하기 위해 추가한다. compileOnly로 지정하여 컴파일 타임에만 필요하고, 런타임에는 포함되지 않도록 한다.
protobuf {
// Protobuf 컴파일러를 지정하여 .proto 파일을 컴파일합니다.
protoc {
artifact = "com.google.protobuf:protoc:${protobufVersion}"
}
// 생성된 파일을 정리합니다.
clean {
delete generatedFilesBaseDir
}
// gRPC 플러그인을 설정하여 Protobuf 파일로부터 gRPC 관련 코드를 생성합니다.
plugins {
grpc {
artifact = "io.grpc:protoc-gen-grpc-java:${grpcVersion}"
}
}
// 모든 프로토콜 버퍼 작업에 대해 gRPC 플러그인을 적용합니다.
generateProtoTasks {
all()*.plugins {
grpc {}
}
}
}
Protobuf 설정의 역할 :
build.gradle 파일의 protobuf 블록은 Protobuf 파일을 컴파일하고, gRPC 코드를 생성하는 작업을 정의한다.
이 작업은 IntelliJ IDEA의 Gradle Tasks에서 extractProto, generateProto 등과 관련이 있다.
이 설정은 Gradle 빌드 프로세스의 일부로, 빌드할 때 자동으로 실행된다.
protoc {
artifact = "com.google.protobuf:protoc:${protobufVersion}"
}
1. Protobuf 컴파일러 설정 (protoc)
역할: Protobuf 컴파일러를 지정한다. 이 컴파일러는 .proto 파일을 읽고 Java 소스 코드로 변환한다.
실행 시점: 빌드 스크립트가 로드될 때 설정된다. 이 설정은 컴파일러가 어떤 것인지 명시하는 단계다.
clean {
delete generatedFilesBaseDir
}
2. 생성된 파일 정리 (clean)
역할: Protobuf 컴파일 시 생성된 파일을 정리한다. 이전 빌드에서 생성된 파일들을 삭제하여 빌드 환경을 깨끗하게 유지하기 위해 사용된다.
실행 시점: Gradle의 clean 작업이 실행될 때 작동한다. 즉, ./gradlew clean 명령이 실행될 때만 이 블록이 실행되어 생성된 파일을 삭제한다. 일반적인 빌드 작업(./gradlew build)에서는 실행되지 않는다.
plugins {
grpc {
artifact = "io.grpc:protoc-gen-grpc-java:${grpcVersion}"
}
}
3. gRPC 플러그인 설정 (plugins)
역할: .proto 파일로부터 gRPC 관련 코드를 생성할 gRPC 플러그인을 설정한다.
실행 시점: 빌드 스크립트가 로드될 때 설정된다. 이 설정은 어떤 플러그인을 사용할지 명시하는 단계다.
generateProtoTasks {
all()*.plugins {
grpc {}
}
}
4. 모든 Protobuf 작업에 gRPC 플러그인 적용 (generateProtoTasks)
역할: 모든 Protobuf 컴파일 작업에 대해 gRPC 플러그인을 적용한다. 실제로 코드를 컴파일하여 생성하는 단계다.
실행 시점: ./gradlew build 명령이 실행될 때 실제로 .proto 파일을 컴파일하여 gRPC 관련 코드를 생성한다.
2. application.properties 포트설정
spring.application.name=gRPC
#spring 서버 포트 설정
server.port=8080
#gRPC 서버 포트 설정
grpc.server.port=9090
3.proto 파일 생성
└── src
├── main
│ ├── java
│ ├── proto
│ └── DataService.proto
│ ├── resources
├── test
해당 위치에 DataSercice.proto 생성
syntax = "proto3";
package example;
service DataService {
rpc SendData(DataRequest) returns (DataResponse);
}
message DataRequest {
bytes data = 1; // 바이너리 데이터
}
message DataResponse {
string message = 1; // 응답 메시지
}
syntax = "proto3";
Protocol Buffers의 버전 3을 사용하고 있음을 나타냅니다.
package example;
이 proto 파일이 포함된 패키지의 이름을 정의합니다.
다른 proto 파일이나 서비스와의 충돌을 피하기 위해 사용됩니다.
service DataService {
rpc SendData(DataRequest) returns (DataResponse);
}
DataService라는 이름의 서비스를 정의하고 있습니다.
SendData라는 원격 프로시저 호출(RPC) 메서드를 정의한다.
이 메서드는 DataRequest 메시지를 입력으로 받고 DataResponse 메시지를 반환합니다.
message DataRequest {
bytes data = 1; // 바이너리 데이터
}
message DataResponse {
string message = 1; // 응답 메시지
}
DataRequest 메시지는 바이너리 데이터를 담는 필드 data를 가지고 있습니다.
이 필드는 bytes 타입으로 정의되어 있으며, 이는 임의의 바이너리 데이터를 전송할 수 있음을 의미합니다.
DataResponse 메시지는 응답 메시지를 담는 필드 message를 가지고 있습니다.
이 필드는 string 타입으로, 일반적인 텍스트 응답을 전송할 수 있습니다.
필드 번호의 역할
고유 식별자:
각 필드는 메시지 내에서 고유한 번호를 가져야 하며, 이는 해당 필드를 식별하는 데 사용됩니다.
같은 메시지 내에서 중복된 번호를 사용할 수 없으며, 예약된 번호(19000-19999)는 피해야 합니다.
효율적 인코딩:
Protocol Buffers는 필드 번호를 사용하여 데이터를 바이너리 형식으로 인코딩합니다.
필드 번호가 작을수록 인코딩 효율이 높아지며, 특히 번호가 1에서 15 사이일 경우 단일 바이트로 인코딩됩니다.
변경 가능성:
필드 번호를 변경하지 않는 한, 메시지 구조의 변경이 호환성을 유지할 수 있게 해줍니다.
새로운 필드를 추가할 때는 기존 필드 번호와의 충돌을 피하면서 신중하게 선택해야 합니다.
4.proto 작성 후 gradle build 하기
intelliJ gradle 선택 > Tasks > other > generateProto 이것을 실행해 주면 된다. (명령어도 가능하다.)
일반적으로 grpc 코드를 작성한 다음 (proto파일) 바로 generateProto를 해주는 게 좋다.
그래야 바로바로 grpc 서비스 코드도 상속을 받아서 구현이 가능하다.
5.GrpcClientService
package com.example.gRPC;
import com.google.protobuf.ByteString;
import example.DataServiceGrpc;
import example.DataServiceOuterClass;
import io.grpc.ManagedChannel;
import io.grpc.ManagedChannelBuilder;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;
@Slf4j
@Service
public class GrpcClientService {
private final ManagedChannel channel;
private final DataServiceGrpc.DataServiceBlockingStub stub;
public GrpcClientService() {
this.channel = ManagedChannelBuilder.forAddress("grpc서버ip", grpc서버포트)
.usePlaintext() // SSL을 사용하지 않음
.build();
this.stub = DataServiceGrpc.newBlockingStub(channel);
}
public void sendData(byte[] data) {
DataServiceOuterClass.DataRequest request = DataServiceOuterClass.DataRequest.newBuilder()
.setData(ByteString.copyFrom(data))
.build();
DataServiceOuterClass.DataResponse response = stub.sendData(request);
log.info("gRPC서버 응답 : " + response.getMessage());
}
public void shutdown() {
channel.shutdown();
}
}
import com.google.protobuf.ByteString;
import example.DataServiceGrpc;
import example.DataServiceOuterClass;
import io.grpc.ManagedChannel;
import io.grpc.ManagedChannelBuilder;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;
import com.google.protobuf.ByteString;
Protobuf에서 제공하는 ByteString 클래스입니다. 이 클래스는 바이트 배열을 안전하게 처리할 수 있도록 도와줍니다.
import example.DataServiceGrpc;
gRPC 서비스에 대한 스텁을 생성하기 위한 클래스입니다. 이 클래스는 gRPC 프로토콜에 정의된 서비스 메서드에 대한 호출을 제공합니다.
import example.DataServiceOuterClass;
Protobuf로 정의된 메시지 타입들을 포함하는 클래스입니다. 여기서 정의된 메시지 형식은 gRPC 요청 및 응답에서 사용됩니다.
import io.grpc.ManagedChannel;
gRPC 통신을 위한 채널을 관리하는 클래스입니다. 클라이언트와 서버 간의 연결을 설정하고 유지하는 데 사용됩니다.
import io.grpc.ManagedChannelBuilder;
ManagedChannel 인스턴스를 생성하는 빌더 클래스입니다. 서버의 주소와 포트, SSL 설정 등을 지정하여 채널을 설정할 수 있습니다.
import lombok.extern.slf4j.Slf4j;
Lombok 라이브러리의 어노테이션으로, SLF4J 로깅 기능을 자동으로 추가합니다. 이 어노테이션을 사용하면 코드에서 로깅을 간편하게 사용할 수 있습니다.
import org.springframework.stereotype.Service;
Spring Framework에서 서비스 컴포넌트를 정의하는 데 사용하는 어노테이션입니다. 이 어노테이션을 통해 Spring 컨테이너가 해당 클래스를 서비스로 관리할 수 있게 됩니다.
private final ManagedChannel channel;
private final DataServiceGrpc.DataServiceBlockingStub stub;
ManagedChannel channel
gRPC 서버와의 통신을 위한 채널입니다.
DataServiceGrpc.DataServiceBlockingStub stub
gRPC 서비스에 대한 블로킹 스텁으로, 서버에 요청을 보내고 응답을 받을 수 있습니다.
public GrpcClientService() {
this.channel = ManagedChannelBuilder.forAddress("grpc서버ip", grpc서버포트)
.usePlaintext() // SSL을 사용하지 않음
.build();
this.stub = DataServiceGrpc.newBlockingStub(channel);
}
gRPC 서버의 IP 주소와 포트를 사용하여 채널을 생성합니다.
usePlaintext() 메소드를 통해 SSL을 사용하지 않도록 설정합니다.
public void sendData(byte[] data) {
DataServiceOuterClass.DataRequest request = DataServiceOuterClass.DataRequest.newBuilder()
.setData(ByteString.copyFrom(data))
.build();
DataServiceOuterClass.DataResponse response = stub.sendData(request);
log.info("gRPC서버 응답 : " + response.getMessage());
}
바이트 배열 데이터를 gRPC 서버에 전송하는 메서드입니다.
DataRequest 객체를 생성하고, ByteString 형식으로 데이터를 설정합니다.
stub.sendData(request)를 호출하여 서버에 요청을 보냅니다.
public void shutdown() {
channel.shutdown();
}
gRPC 채널을 종료하는 메서드로, 서버와의 연결을 안전하게 종료합니다.
GrpcClientService.sendData를 사용해 byte[] 을 넣어 사용하면 지정된 grpc 서버에서 받아지는 것을 확인 할 수 있다.
참고 링크 :
https://youtu.be/uwrR5e5_FH8?si=XMZjQgOUZi6ZtUmJ
'STUDY > SpringBoot' 카테고리의 다른 글
Spring Boot - grpc server 예제 (0) | 2024.10.23 |
---|---|
Spring Boot - kafka producer 예제 (0) | 2024.10.11 |
[Springboot] 22-09-29 삭제·상세·수정 기능 수업 -3 (0) | 2022.09.29 |
[Springboot] 22-09-29 페이징 기능 수업 -2 (0) | 2022.09.29 |
[Springboot] 22-09-28 페이징 기능 수업 -1 (0) | 2022.09.28 |