gRPC 기초 + 실습 예제
GRPC
목차
- RPC란?
- gRPC란?
- start gRPC with java + python
- java gRPC server
- java gRPC client
- python gRPC client
- with docker
1. RPC란?
Remote Procedure Call의 약자. 원격에 있는 함수를 호출하는 것을 말한다.
과거엔 프로세스간 통신을 위해 많이 사용되었으나 현재는 웹 기술의 발달로 SOAP, REST에 많이 밀려난 상태.
분산컴퓨팅 환경에서 많이 사용하며 MSA에서도 사용하기 적절하다.
Skeleton, Stub이라고 부르는 IDL 코드로 request params, response params를 파악해야 함
(작성중)
2. gRPC란?
구글에서 사용하던 'Stubby' 라는 RPC 스택을 강화하여 오픈소스화한 기술.
http/2 사용
Protocol buffers 사용
Low Latency, Highly scalable, distributed systems
(작성중)
3. start gRPC with java + python(일단 STEP 나열하고 설명은 나중에)
3-1. java gRPC Server
0. 작업용 디렉토리 생성. 앞으로 '핸즈온 디렉토리'는 여기서 만든 디렉토리를 의미합니다. 이름은 자유! (예 : grpc_handson)
1. 핸즈온 디렉토리에 서버용 프로젝트 생성.(Gradle, jdk 1.8) 이름은 자유! (예 : java_grpc_server)
2. build.gradle 내용 추가
buildscript {
repositories {
mavenCentral()
}
dependencies {
classpath 'com.google.protobuf:protobuf-gradle-plugin:0.8.8'
}
}
plugins {
id 'java'
}
sourceCompatibility = 1.8
targetCompatibility = 1.8
sourceSets {
src {
main {
java {
srcDirs 'build/generated/source/proto/main/grpc'
srcDirs 'build/generated/source/proto/main/java'
}
}
}
}
jar {
// enabled = true
archivesBaseName = "grpc_handson_jar"
from {
configurations.runtimeClasspath.collect { it.isDirectory() ? it : zipTree(it) }
}
manifest {
attributes 'Main-Class' : "HelloServerRunner"
}
}
apply plugin: 'com.google.protobuf'
repositories {
mavenCentral()
}
dependencies {
/**
* gRPC
*/
compile group: 'io.grpc', name: 'grpc-netty-shaded', version: '1.35.0'
compile group: 'io.grpc', name: 'grpc-protobuf', version: '1.35.0'
compile group: 'io.grpc', name: 'grpc-stub', version: '1.35.0'
implementation 'javax.annotation:javax.annotation-api:1.3.2'
implementation "com.google.protobuf:protobuf-java-util:3.8.0"
compile group: 'com.google.protobuf', name: 'protobuf-java', version: '3.8.0'
testCompile group: 'junit', name: 'junit', version: '4.12'
}
protobuf {
protoc {
artifact = "com.google.protobuf:protoc:3.8.0"
}
plugins {
grpc {
artifact = 'io.grpc:protoc-gen-grpc-java:1.35.0'
}
}
generateProtoTasks {
all()*.plugins {
grpc {}
}
}
}
3. 프로젝트 디렉토리 > src > main 하위에 'proto' 디렉토리 생성
4. 생성한 디렉토리에 'HelloService.proto' 파일 생성 및 내용 작성
syntax = "proto3";
option java_multiple_files = true;
message HelloRequest {
string name = 1;
string alias = 2;
}
message HelloResponse {
string greeting = 1;
}
service HelloService {
rpc hello(HelloRequest) returns (HelloResponse);
}
5. 우측 Gradle 탭 > Tasks > other > generateProto 실행 (없을 경우 gradle > Reload All Gradle Projects)
6. src > main > java에 'HelloServiceImpl.java' 클래스 생성 및 내용 작성
public class HelloServiceImpl extends HelloServiceGrpc.HelloServiceImplBase{
@Override
public void hello(HelloRequest request, StreamObserver<HelloResponse> responseObserver) {
String greeting = "Hello, " + request.getName() + " a.k.a. " + request.getAlias();
System.out.println("Remote client called a service 'hello'.");
HelloResponse helloResponse = HelloResponse.newBuilder()
.setGreeting(greeting)
.build();
responseObserver.onNext(helloResponse);
responseObserver.onCompleted();
}
}
7. src > main > java에 'HelloServerRunner.java' 클래스 생성 및 내용 작성
public class HelloServerRunner {
public static void main(String args[]) throws IOException, InterruptedException {
Server server = ServerBuilder
.forPort(36200)
.addService(new HelloServiceImpl()).build();
System.out.println("Listening port 36200...");
server.start();
server.awaitTermination();
}
}
8. 서버 실행
3-2. java gRPC Client
1. 핸즈온 디렉토리에 서버 생성 STEP의 1~5번 진행. 프로젝트 이름은 자유! (예 : java_grpc_client)
2. src > main > java에 'HelloClientRunner.java' 생성 및 내용 작성
public class HelloClientRunner {
public static void main(String[] args) {
ManagedChannel channel = ManagedChannelBuilder.forAddress("localhost", 36200)
.usePlaintext()
.build();
HelloServiceGrpc.HelloServiceBlockingStub stub
= HelloServiceGrpc.newBlockingStub(channel);
HelloResponse helloResponse = stub.hello(HelloRequest.newBuilder()
.setName("Leo") //Replace to ur name :)
.setAlias("grpc newbie") //Replace to ur alias :)
.build());
System.out.println("\n-----------------------------------------");
System.out.print(helloResponse);
System.out.println("-----------------------------------------\n");
channel.shutdown();
}
}
3. 클라이언트 실행
3-3. python gRPC Client
1. 준비물 준비
pip3 install --upgrade pip
pip3 install grpcio
pip3 install grpcio-tools
pip3 install --upgrade google-api-python-client --ignore-installed
cd ${handson_directory}
mkdir ${python_grpc_client} //as you wanna naming
cd ${python_grpc_client}
2. 'HelloService.proto' 파일 생성. 내용은 서버에서 사용한 것과 정확히 같음.
syntax = "proto3";
option java_multiple_files = true; //이건 빼도 된다.
message HelloRequest {
string name = 1;
string alias = 2;
}
message HelloResponse {
string greeting = 1;
}
service HelloService {
rpc hello(HelloRequest) returns (HelloResponse);
}
3. 같은 디렉토리에서 protocol buffers 컴파일 진행
python3 -m grpc_tools.protoc -I=./ --python_out=./ --grpc_python_out=./ ./HelloService.proto
4. 같은 디렉토리에 python client 파일 'HelloClient.py' 생성 및 코드 작성
from __future__ import print_function
import grpc
import HelloService_pb2
import HelloService_pb2_grpc
#Replace to ur name and alias :)
_name = "Leo"
_alias = "newbie"
def run():
channel = grpc.insecure_channel('localhost:36200')
stub = HelloService_pb2_grpc.HelloServiceStub(channel)
response = stub.hello(HelloService_pb2.HelloRequest(name=_name, alias=_alias))
print("Hello client received: " + response.greeting)
if __name__ == '__main__':
run()
5. 클라이언트 실행
python3 HelloClient.py
3-4. with docker
1. Gradle build
//프로젝트 홈 디렉토리에서
./gradlew build
2. Dockerfile 작성
vi Dockerfile //not 'd'ockerfile!
-------------------------------------------------
FROM openjdk:8-jdk-alpine
ADD ./build/libs/grpc_handson_jar.jar app.jar
ENTRYPOINT java \
-jar /app.jar
3. Docker build
docker build -t grpc-[server|client]:1.0 .
4. Docker run
docker run -p 36200:36200 grpc-[server|client]:1.0
유의할 점. 자바 클라이언트를 도커로 띄울 땐 HelloClientRunner의 "localhost"를 로컬 아이피 주소로 변경해주어야 한다.
추가로 build.gradle에서 jar > manifest의 Main-class 값을 "HelloClientRunner" 로 수정해주어야 한다.