전 편에서 gRPC에 대한 설명과 특징을 알아보았습니다. 이번 포스트에서는 grpc-java 모듈을 활용한 몇 가지 예제코드를 소개하고, 실제 프로젝트에 적용하는 방안에 대해 소개해보겠습니다.
스텁 생성을 위해 protoc 플러그인 설정
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>com.nhn.gia</groupId>
<artifactId>gia-grpc</artifactId>
<version>1.0-SNAPSHOT</version>
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<grpc.version>1.25.0</grpc.version>
<protobuf.version>3.11.0</protobuf.version>
<protoc.version>3.11.0</protoc.version>
<maven.compiler.source>1.7</maven.compiler.source>
<maven.compiler.target>1.7</maven.compiler.target>
</properties>
<dependencies>
<dependency>
<groupId>io.grpc</groupId>
<artifactId>grpc-netty-shaded</artifactId>
<scope>runtime</scope>
<version>${grpc.version}</version>
</dependency>
<dependency>
<groupId>io.grpc</groupId>
<artifactId>grpc-protobuf</artifactId>
<version>${grpc.version}</version>
</dependency>
<dependency>
<groupId>io.grpc</groupId>
<artifactId>grpc-stub</artifactId>
<version>${grpc.version}</version>
</dependency>
</dependencies>
<build>
<!-- Maven OS Extension -->
<extensions>
<extension>
<groupId>kr.motd.maven</groupId>
<artifactId>os-maven-plugin</artifactId>
<version>1.6.2</version>
</extension>
</extensions>
<plugins>
<!-- proto (IDL) generator plugin -->
<plugin>
<groupId>org.xolstice.maven.plugins</groupId>
<artifactId>protobuf-maven-plugin</artifactId>
<version>0.6.1</version>
<configuration>
<protocArtifact>com.google.protobuf:protoc:${protoc.version}:exe:${os.detected.classifier}</protocArtifact>
<pluginId>grpc-java</pluginId>
<pluginArtifact>io.grpc:protoc-gen-grpc-java:${grpc.version}:exe:${os.detected.classifier}</pluginArtifact>
</configuration>
<executions>
<execution>
<goals>
<goal>compile</goal>
<goal>compile-custom</goal>
</goals>
</execution>
</executions>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<configuration>
<source>8</source>
<target>8</target>
</configuration>
</plugin>
</plugins>
</build>
</project>
mvn protobuf:{goal}
명령어로 컴파일 할 수 있다.message
는 서버-클라이언트 간 송수신될 데이터 포맷service
는 서버, 클라이언트에서 원격 호출되는 서비스 메소드syntax = "proto3";
package proto_test;
option java_package = "com.nhn.gia.grpc.proto";
option java_multiple_files = true;
service EchoService {
rpc Echo (EchoRequest) returns (EchoResponse) {}
}
message EchoRequest {
string msg = 1;
}
message EchoResponse {
string msg = 1;
}
service
의 RPC 메소드들을 구현해줌rpc Echo (EchoRequest) returns (EchoResponse) {}
를 구현public class EchoServiceImpl extends EchoServiceGrpc.EchoServiceImplBase {
@Override
public void echo(EchoRequest request, StreamObserver<EchoResponse> responseObserver) {
EchoResponse response = EchoResponse.newBuilder().setMsg(request.getMsg()).build();
responseObserver.onNext(response);
responseObserver.onCompleted();
}
}
작성한 서비스를 사용하는 서버 프로그램 작성
public class EchoServer {
public static void main(String[] args) throws IOException, InterruptedException {
Server server = ServerBuilder.forPort(8000)
.addService(new EchoServiceImpl())
.build();
server.start();
Runtime.getRuntime().addShutdownHook(new Thread(() -> {
server.shutdown();
}));
server.awaitTermination();
}
}
public class EchoClient {
public static void main(String[] args) {
ManagedChannel channel = ManagedChannelBuilder
.forAddress("localhost", 8000)
.usePlaintext()
.build();
EchoServiceBlockingStub stub = EchoServiceGrpc.newBlockingStub(channel);
EchoResponse response = stub.echo(EchoRequest.newBuilder().setMsg("Hello gRPC!!").build());
Logger.log("Echo Client: %s", response.getMsg());
channel.shutdown();
}
}
스프링 환경에서도 gRPC를 사용할 수 있다. 공식은 아니지만 몇 가지 개인 또는 그룹에서 제작해서 사용하고 있는 Spring boot starter가 있다. (https://github.com/LogNet/grpc-spring-boot-starter) (https://github.com/saturnism/spring-boot-starter-grpc)
아래는 별도 Spring Boot Starter 없이 진행한 예제
syntax = "proto3";
option java_package = "com.nhn.gia.proto";
option java_multiple_files = true;
service UserService {
rpc GetUser(UserSearchRequest) returns (User) {}
}
message UserSearchRequest {
string id = 1;
}
message User {
string _id = 1;
int64 index = 2;
bool isActive = 3;
int32 age = 4;
string name = 5;
string gender = 6;
string email = 7;
}
@Service
public class UserServiceImpl extends UserServiceGrpc.UserServiceImplBase {
private static final Logger logger = LoggerFactory.getLogger(UserServiceImpl.class);
private final UserRepository userRepository;
@Autowired
public UserServiceImpl(UserRepository userRepository) {
this.userRepository = userRepository;
}
@Override
public void getUser(UserSearchRequest request, StreamObserver<User> responseObserver) {
String requestId = request.getId();
logger.info("requestId: {}", requestId);
UserEntity entity = userRepository.findOne(requestId);
responseObserver.onNext(buildUser(entity));
responseObserver.onCompleted();
}
private User buildUser(UserEntity entity) {
return User.newBuilder()
.setId(entity.getId())
.setIndex(entity.getIndex())
.setIsActive(entity.getIsActive())
.setAge(entity.getAge())
.setName(entity.getName())
.setGender(entity.getGender())
.setEmail(entity.getEmail())
.build();
}
}
@Configuration
public class GrpcServerConfiguration {
@Value("${grpc.server.port}")
private int grpcServerPort;
@Bean
public Server grpcServer(UserServiceImpl userService) {
return ServerBuilder.forPort(grpcServerPort)
.addService(userService)
.build();
}
}
@Component
public class GrpcServerRunner implements ApplicationRunner, DisposableBean {
private static final Logger logger = LoggerFactory.getLogger(GrpcServerRunner.class);
@Autowired
private Server grpcServer;
@Override
public void run(ApplicationArguments args) throws Exception {
grpcServer.start();
logger.info("GrpcServer listening on port {}", grpcServer.getPort());
grpcServer.awaitTermination();
}
@Override
public void destroy() throws Exception {
if (grpcServer != null) {
grpcServer.shutdown();
}
}
}