🍳머리말
gRPC를 spring boot 내에서 실행해보는 예제 설명글입니다.
📕 Prerequisite
📔 jdk 11
📔 spring boot community
📕 gRPC Interface
📔 설명
gRPC를 사용하기 위해서는 .proto file에 정의된 data규약과 통신방법을 service와 message keyword로 정의해줘야 합니다.
📔 프로젝트 생성
library만 만들 것이기 때문에 간단히 gradle project로 생성해줍니다.
📔 folder및 file 생성
📑 folder생성
src/main에 proto folder를 생성해줍니다. 해당 folder 이름을 인식, .proto file을 compile할 수 있습니다.
📑 file생성
proto folder 하위에 helloworld.proto file을 생성해줍니다.
syntax = "proto3"; //proto3을 사용함
option java_multiple_files = true;
option java_package = "org.chb.examples.lib";
option java_outer_classname = "HelloWorldProto";
//service규정
service Simple {
//SayHello가 오면 HelloReply를 하겠다의 의미
rpc SayHello (HelloRequest) returns (HelloReply) {
}
}
//요청 data 규약 : 문자열 name을 1로 사용하겠다.
message HelloRequest {
string name = 1;
}
//응답 data 규약 : 문자열 message을 1로 사용하겠다.
message HelloReply {
string message = 1;
}
📑 build.gradle 설정
기존 build.gradle과 겹치지 않도록 다음 예시를 보고 적절히 수정합니다.
buildscript {
ext {
protobufVersion = '3.14.0'
protobufPluginVersion = '0.8.14'
grpcVersion = '1.35.0'
}
}
plugins {
id 'java'
id 'com.google.protobuf' version "${protobufPluginVersion}"
}
group 'org.example'
version '1.0-SNAPSHOT'
repositories {
mavenCentral()
}
dependencies {
testImplementation 'org.junit.jupiter:junit-jupiter-api:5.7.0'
testRuntimeOnly 'org.junit.jupiter:junit-jupiter-engine:5.7.0'
implementation "io.grpc:grpc-protobuf:${grpcVersion}"
implementation "io.grpc:grpc-stub:${grpcVersion}"
compileOnly 'jakarta.annotation:jakarta.annotation-api:1.3.5' // Java 9+ compatibility - Do NOT update to 2.0.0
}
protobuf {
protoc {
artifact = "com.google.protobuf:protoc:${protobufVersion}"
}
generatedFilesBaseDir = "$projectDir/src/generated"
clean {
delete generatedFilesBaseDir
}
plugins {
grpc {
artifact = "io.grpc:protoc-gen-grpc-java:${grpcVersion}"
}
}
generateProtoTasks {
all()*.plugins {
grpc{}
}
}
}
test {
useJUnitPlatform()
}
📑 Tasks -> build
Tasks의 build를 이용해 압축된 .jar file을 얻습니다. 이는 client와 server의 library import로 사용될 예정입니다.
📕 gRPC Client
📔 설명
client로 사용될 application을 만듭니다. 이는 interface와는 다르게 제대로 된 application project를 생성합니다.
📔 프로젝트 생성
spring initilizer로 spring boot project를 생성해줍니다.
📔 folder 및 file 생성
📑 folder생성
src folder level에 libs라는 folder를 생성한 뒤에 해당 경로에 만들어두었던 interface.jar를 넣어줍니다.
📑 source code 작성
java/{project명} 경로 하위에 다음 file들을 만들어줍니다.
GrpcClientController.java
package com.example.grpcClient;
import lombok.RequiredArgsConstructor;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;
@RestController
@RequiredArgsConstructor
public class GrpcClientController {
private final GrpcClientService grpcClientService;
@GetMapping("/test")
public String printMessage() {
return grpcClientService.sendMessage("test");
}
}
GrpcClientService.java
package com.example.grpcClient;
import io.grpc.StatusRuntimeException;
import net.devh.boot.grpc.client.inject.GrpcClient;
import org.chb.examples.lib.HelloReply;
import org.chb.examples.lib.HelloRequest;
import org.chb.examples.lib.SimpleGrpc;
import org.springframework.stereotype.Service;
@Service
public class GrpcClientService {
@GrpcClient("test")
private SimpleGrpc.SimpleBlockingStub simpleStub;
public String sendMessage(final String name) {
try{
HelloReply response = this.simpleStub.sayHello(HelloRequest.newBuilder().setName(name).build());
return response.getMessage();
} catch (StatusRuntimeException e) {
return "FAILED with " + e.getStatus().getCode().name();
}
}
}
📑 application.yaml작성
server:
port: 8081
grpc:
client:
test:
address: 'static://127.0.0.1:9090'
negotiationtype: plaintext
📑 build.gradle 설정
다음 예시 file로 겹치지 않도록 적절히 수정해줍니다.
plugins {
id 'org.springframework.boot' version '2.7.0'
id 'io.spring.dependency-management' version '1.0.11.RELEASE'
id 'java'
}
group = 'com.example'
version = '0.0.1-SNAPSHOT'
sourceCompatibility = '11'
repositories {
mavenCentral()
}
configurations {
compileOnly {
extendsFrom annotationProcessor
}
}
dependencies {
implementation 'org.springframework.boot:spring-boot-starter'
testImplementation 'org.springframework.boot:spring-boot-starter-test'
implementation 'org.springframework.boot:spring-boot-starter-web'
implementation 'net.devh:grpc-client-spring-boot-starter:2.12.0.RELEASE'
implementation files('libs/grpcInterface-1.0-SNAPSHOT.jar')
compileOnly 'org.projectlombok:lombok'
annotationProcessor 'org.projectlombok:lombok'
testImplementation 'org.springframework.boot:spring-boot-starter-test'
}
tasks.named('test') {
useJUnitPlatform()
}
📕 gRPC Server
📔 설명
server로 사용될 application을 만듭니다. 이는 client와 마찬가지로 제대로 된 application project를 생성합니다.
📔 프로젝트 생성
spring initilizer로 spring boot project를 생성해줍니다.
📔 folder 및 file 생성
📑 folder생성
src folder level에 libs라는 folder를 생성한 뒤에 해당 경로에 만들어두었던 interface.jar를 넣어줍니다.
📑 source code 작성
main/java/{project명} 경로 하위에 다음 file을 만들어줍니다.
GrpcServerService.java
package com.example.grpcServer;
import io.grpc.stub.StreamObserver;
import net.devh.boot.grpc.server.service.GrpcService;
import org.chb.examples.lib.HelloReply;
import org.chb.examples.lib.HelloRequest;
import org.chb.examples.lib.SimpleGrpc;
import org.springframework.beans.factory.annotation.Autowired;
@GrpcService
public class GrpcServerService extends SimpleGrpc.SimpleImplBase {
@Override
public void sayHello(HelloRequest request, StreamObserver<HelloReply> responseObserver) {
HelloReply reply = HelloReply.newBuilder()
.setMessage("Hello ==> " + request.getName())
.build();
responseObserver.onNext(reply);
responseObserver.onCompleted();
}
}
📑 application.yaml 작성
main/resources 하위 경로에 application설정을 추가합니다.
grpc:
server:
port: 9090
spring:
application:
name: user-service
main:
allow-circular-references: true
📑 build.gradle 작성
plugins {
id 'org.springframework.boot' version '2.7.0'
id 'io.spring.dependency-management' version '1.0.11.RELEASE'
id 'java'
}
group = 'com.example'
version = '0.0.1-SNAPSHOT'
sourceCompatibility = '11'
repositories {
mavenCentral()
}
dependencies {
implementation 'org.springframework.boot:spring-boot-starter'
implementation 'link.jfire:simplerpc:1.0'
implementation 'io.github.HuChen-dot:simplerpc-spring-boot-starter:1.6'
implementation 'net.devh:grpc-server-spring-boot-starter:2.13.1.RELEASE'
implementation files('libs/grpcInterface-1.0-SNAPSHOT.jar')
testImplementation 'org.springframework.boot:spring-boot-starter-test'
}
tasks.named('test') {
useJUnitPlatform()
}
📕 구동
📔 실행
client와 server를 run해줍니다.
📔 결과
client는 localhost:8081로 localhost:9090 server에 request를 보내며 /test API를 통해 response를 하고 있습니다.
📑 client모습1 : 기본 화면
📑 client모습2 : API호출 후 server로 부터 response를 받은 모습
📕 출처
https://yidongnan.github.io/grpc-spring-boot-starter/en/server/getting-started.html
*더 나은 내용을 위해 지적, 조언은 언제나 환영입니다.