1. 개요
gRPC는 Google에서 개발한 언어 및 플랫폼 중립적인 리모트 프로시져 콜 (RPC) 프레임워크 및 도구 모음입니다. 특히 강력한 바이너리 직렬화 툴셋 및 언어인 프로토콜 버퍼를 사용하여 서비스를 정의할 수 있습니다. 그런 다음 다양한 언어로 된 서비스 정의에서 직관적인 클라이언트 및 서버 스텁을 생성할 수 있습니다.
이 Codelab에서는 gRPC 프레임워크를 사용하여 API를 노출하는 Java 기반 서비스를 빌드한 다음 생성된 gRPC 클라이언트 측 스텁을 사용할 클라이언트를 작성하는 방법을 알아봅니다.
학습할 내용
- 프로토콜 버퍼 언어
- 자바를 사용하여 gRPC 서비스를 구현하는 방법
- 자바를 사용하여 gRPC 클라이언트를 구현하는 방법
이 튜토리얼을 어떻게 사용하실 계획인가요?
귀하의 Node.js 앱 빌드 경험을 평가해 주세요.
Go 앱을 빌드한 경험을 평가해 주세요.
<ph type="x-smartling-placeholder">2. 설정 및 요구사항
자습형 환경 설정
- Cloud 콘솔에 로그인하고 새 프로젝트를 만들거나 기존 프로젝트를 다시 사용합니다. 아직 Gmail이나 Google Workspace 계정이 없는 경우 계정을 만들어야 합니다.
모든 Google Cloud 프로젝트에서 고유한 이름인 프로젝트 ID를 기억하세요(위의 이름은 이미 사용되었으므로 사용할 수 없습니다). 이 ID는 나중에 이 Codelab에서 PROJECT_ID
라고 부릅니다.
- 그런 후 Google Cloud 리소스를 사용할 수 있도록 Cloud Console에서 결제를 사용 설정해야 합니다.
이 Codelab 실행에는 많은 비용이 들지 않습니다. 이 가이드를 마친 후 비용이 결제되지 않도록 리소스 종료 방법을 알려주는 '삭제' 섹션의 안내를 따르세요. Google Cloud 신규 사용자에게는 미화$300 상당의 무료 체험판 프로그램에 참여할 수 있는 자격이 부여됩니다.
Google Cloud Shell
이 Codelab은 컴퓨터에서 실행할 수 있지만 이 Codelab에서는 Cloud에서 실행되는 명령줄 환경인 Google Cloud Shell을 사용합니다.
이 Debian 기반 가상 머신에는 필요한 모든 개발 도구가 로드되어 있습니다. 영구적인 5GB 홈 디렉터리를 제공하고 Google Cloud에서 실행되므로 네트워크 성능과 인증이 크게 개선됩니다. 즉, 이 Codelab에 필요한 것은 브라우저뿐입니다(Chromebook에서도 작동 가능).
- Cloud Console에서 Cloud Shell을 활성화하려면 단순히 Cloud Shell 활성화를 클릭합니다. 환경을 프로비저닝하고 연결하는 데 몇 정도만 소요됩니다.
Cloud Shell에 연결되면 사용자 인증이 이미 완료되었고 프로젝트가 내 PROJECT_ID
에 설정되어 있음을 확인할 수 있습니다.
gcloud auth list
명령어 결과
Credentialed accounts: - <myaccount>@<mydomain>.com (active)
gcloud config list project
명령어 결과
[core] project = <PROJECT_ID>
어떤 이유로든 프로젝트가 설정되지 않았으면 다음 명령어를 실행하면 됩니다.
gcloud config set project <PROJECT_ID>
PROJECT_ID
를 찾고 계신가요? 설정 단계에서 사용한 ID를 확인하거나 Cloud Console 대시보드에서 확인하세요.
또한 Cloud Shell은 기본적으로 이후 명령어를 실행할 때 유용할 수 있는 몇 가지 환경 변수를 설정합니다.
echo $GOOGLE_CLOUD_PROJECT
명령어 결과
<PROJECT_ID>
- 마지막으로 기본 영역 및 프로젝트 구성을 설정합니다.
gcloud config set compute/zone us-central1-f
다양한 영역을 선택할 수 있습니다. 자세한 내용은 리전 및 영역을 참조하세요.
3. gRPC 서비스 만들기
Maven으로 새 Java 프로젝트를 만듭니다.
$ mvn archetype:generate -DgroupId=com.example.grpc \ -DartifactId=grpc-hello-server \ -DarchetypeArtifactId=maven-archetype-quickstart \ -DinteractiveMode=false $ cd grpc-hello-server
gRPC 정의 파일 추가
gRPC에서는 서비스 페이로드 (요청 및 응답)와 서비스 작업을 IDL (인터페이스 정의 언어)로 캡처해야 합니다. gRPC는 Protobuffer 3 문법을 사용하여 메시지 페이로드와 작업을 정의합니다. Hello 요청과 Hello 응답이 포함된 간단한 인사말 서비스의 proto 파일을 만들어 보겠습니다.
먼저 새 proto 파일을 저장할 새 proto 디렉터리를 만듭니다.
$ mkdir -p src/main/proto
그런 다음 새 proto 파일 src/main/proto/GreetingService.proto
를 만듭니다.
vim,nano,
또는 emacs
를 사용하여 파일을 수정할 수 있습니다.
src/main/proto/GreetingService.proto
syntax = "proto3"; package com.example.grpc; // Request payload message HelloRequest { // Each message attribute is strongly typed. // You also must assign a "tag" number. // Each tag number is unique within the message. string name = 1; // This defines a strongly typed list of String repeated string hobbies = 2; // There are many more basics types, like Enum, Map // See https://developers.google.com/protocol-buffers/docs/proto3 // for more information. } message HelloResponse { string greeting = 1; } // Defining a Service, a Service can have multiple RPC operations service GreetingService { // Define a RPC operation rpc greeting(HelloRequest) returns (HelloResponse); }
gRPC 종속 항목 및 플러그인 추가
정의가 지정되면 이 파일에서 서버 측 스텁과 클라이언트 측 스텁을 모두 생성할 수 있습니다. gRPC 종속 항목과 플러그인을 추가해야 합니다.
먼저 pom.xml
에 gRPC 종속 항목을 추가합니다.
pom.xml
<project> ... <dependencies> <dependency> <groupId>io.grpc</groupId> <artifactId>grpc-netty-shaded</artifactId> <version>1.24.0</version> </dependency> <dependency> <groupId>io.grpc</groupId> <artifactId>grpc-protobuf</artifactId> <version>1.24.0</version> </dependency> <dependency> <groupId>io.grpc</groupId> <artifactId>grpc-stub</artifactId> <version>1.24.0</version> </dependency> <dependency> <groupId>javax.annotation</groupId> <artifactId>javax.annotation-api</artifactId> <version>1.3.2</version> </dependency> ... </dependencies> ... </project>
그런 다음 플러그인을 추가합니다.
pom.xml
<project> ... <dependencies> ... </dependencies> <build> <extensions> <extension> <groupId>kr.motd.maven</groupId> <artifactId>os-maven-plugin</artifactId> <version>1.6.2</version> </extension> </extensions> <plugins> <plugin> <groupId>org.xolstice.maven.plugins</groupId> <artifactId>protobuf-maven-plugin</artifactId> <version>0.6.1</version> <configuration> <protocArtifact>com.google.protobuf:protoc:3.9.0:exe:${os.detected.classifier}</protocArtifact> <pluginId>grpc-java</pluginId> <pluginArtifact>io.grpc:protoc-gen-grpc-java:1.24.0:exe:${os.detected.classifier}</pluginArtifact> </configuration> <executions> <execution> <goals> <goal>compile</goal> <goal>compile-custom</goal> </goals> </execution> </executions> </plugin> </plugins> </build> </project>
스텁 생성
애플리케이션을 빌드할 때 플러그인은 proto 정의를 Java 코드로 변환합니다.
$ mvn -DskipTests package
생성된 파일을 보려면 다음 안내를 따르세요.
$ find target/generated-sources
서비스 구현
먼저 greeting
작업을 구현할 새 GreetingServiceImpl
클래스를 만듭니다.
src/main/java/com/example/grpc/GreetingServiceImpl.java
package com.example.grpc; import io.grpc.stub.StreamObserver; public class GreetingServiceImpl extends GreetingServiceGrpc.GreetingServiceImplBase { @Override public void greeting(GreetingServiceOuterClass.HelloRequest request, StreamObserver<GreetingServiceOuterClass.HelloResponse> responseObserver) { // HelloRequest has toString auto-generated. System.out.println(request); // You must use a builder to construct a new Protobuffer object GreetingServiceOuterClass.HelloResponse response = GreetingServiceOuterClass.HelloResponse.newBuilder() .setGreeting("Hello there, " + request.getName()) .build(); // Use responseObserver to send a single response back responseObserver.onNext(response); // When you are done, you must call onCompleted. responseObserver.onCompleted(); } }
서버 구현
마지막으로 포트에서 수신 대기하는 서버를 시작하고 이 서비스 구현을 등록해야 합니다. App
클래스 및 기본 메서드를 수정합니다.
src/main/java/com/example/grpc/App.java
package com.example.grpc; import io.grpc.*; public class App { public static void main( String[] args ) throws Exception { // Create a new server to listen on port 8080 Server server = ServerBuilder.forPort(8080) .addService(new GreetingServiceImpl()) .build(); // Start the server server.start(); // Server threads are running in the background. System.out.println("Server started"); // Don't exit the main thread. Wait until server is terminated. server.awaitTermination(); } }
마지막으로 서버를 실행합니다.
$ mvn -DskipTests package exec:java -Dexec.mainClass=com.example.grpc.App ... Server Started
4. 서비스 사용
생성기에서 이미 모든 클라이언트 측 스텁을 생성했습니다. 실습의 편의를 위해 동일한 Maven 프로젝트를 사용하지만 새로운 기본 메서드를 사용하여 새 Client
클래스를 추가하기만 하면 됩니다.
먼저 +를 클릭하여 새 Cloud Shell 세션을 엽니다. 그러면 서버를 종료할 필요가 없습니다.
새 세션에서 grpc-hello-server
디렉터리로 전환합니다.
$ cd grpc-hello-server
그런 다음 새 Client
클래스를 추가합니다.
src/main/java/com/example/grpc/Client.java
package com.example.grpc; import io.grpc.*; public class Client { public static void main( String[] args ) throws Exception { // Channel is the abstraction to connect to a service endpoint // Let's use plaintext communication because we don't have certs final ManagedChannel channel = ManagedChannelBuilder.forTarget("localhost:8080") .usePlaintext(true) .build(); // It is up to the client to determine whether to block the call // Here we create a blocking stub, but an async stub, // or an async stub with Future are always possible. GreetingServiceGrpc.GreetingServiceBlockingStub stub = GreetingServiceGrpc.newBlockingStub(channel); GreetingServiceOuterClass.HelloRequest request = GreetingServiceOuterClass.HelloRequest.newBuilder() .setName("Ray") .build(); // Finally, make the call using the stub GreetingServiceOuterClass.HelloResponse response = stub.greeting(request); System.out.println(response); // A Channel should be shutdown before stopping the process. channel.shutdownNow(); } }
마지막으로 클라이언트를 실행합니다.
$ mvn -DskipTests package exec:java -Dexec.mainClass=com.example.grpc.Client ... greeting: "Hello there, Ray"
작업이 끝났습니다. 간단하죠?
5. 스트리밍 서비스
이 외에도 다양한 방법을 시도해 볼 수 있습니다. 예를 들어 proto 파일의 stream
키워드를 요청 또는 응답 매개변수에 추가하면 간단하게 스트리밍 서비스를 빌드할 수 있습니다. 예를 들어
src/main/proto/GreetingService.proto
syntax = "proto3"; package com.example.grpc; ... // Defining a Service, a Service can have multiple RPC operations service GreetingService { // MODIFY HERE: Update the return to streaming return. rpc greeting(HelloRequest) returns (stream HelloResponse); }
하나의 응답이 아닌 여러 응답을 보내도록 서버를 업데이트합니다. 이렇게 하려면 responseObserver.onNext(...)
를 여러 번 호출하면 됩니다.
src/main/java/com/example/grpc/GreetingServiceImpl.java
package com.example.grpc; import io.grpc.stub.StreamObserver; public class GreetingServiceImpl extends GreetingServiceGrpc.GreetingServiceImplBase { @Override public void greeting(GreetingServiceOuterClass.HelloRequest request, StreamObserver<GreetingServiceOuterClass.HelloResponse> responseObserver) { ... // Feel free to construct different responses if you'd like. responseObserver.onNext(response); responseObserver.onNext(response); responseObserver.onNext(response); // When you are done, you must call onCompleted. responseObserver.onCompleted(); } }
클라이언트는 차단 스텁 대신 비동기 스텁을 사용해야 합니다. 클라이언트 코드를 업데이트하여 stub
유형을 GreetingServiceStub
로 업데이트합니다.
src/main/java/com/example/grpc/Client.java
package com.example.grpc; import io.grpc.*; // New import import io.grpc.stub.*; public class Client { public static void main( String[] args ) throws Exception { final ManagedChannel channel = ManagedChannelBuilder.forTarget("localhost:8080") .usePlaintext(true) .build(); // Replace the previous synchronous code with asynchronous code. // This time use an async stub: GreetingServiceGrpc.GreetingServiceStub stub = GreetingServiceGrpc.newStub(channel); // Construct a request GreetingServiceOuterClass.HelloRequest request = GreetingServiceOuterClass.HelloRequest.newBuilder() .setName("Ray") .build(); // Make an Asynchronous call. Listen to responses w/ StreamObserver stub.greeting(request, new StreamObserver<GreetingServiceOuterClass.HelloResponse>() { public void onNext(GreetingServiceOuterClass.HelloResponse response) { System.out.println(response); } public void onError(Throwable t) { } public void onCompleted() { // Typically you'll shutdown the channel somewhere else. // But for the purpose of the lab, we are only making a single // request. We'll shutdown as soon as this request is done. channel.shutdownNow(); } }); } }
애플리케이션을 다시 빌드합니다.
$ mvn -DskipTests package
자체 Cloud Shell 세션에서 서버와 클라이언트를 모두 다시 시작합니다.
서버를 시작하려면 다음 안내를 따르세요.
$ mvn exec:java -Dexec.mainClass=com.example.grpc.App ... Server Started
클라이언트를 시작하려면 다음 안내를 따르세요.
$ mvn exec:java -Dexec.mainClass=com.example.grpc.Client ... greeting: "Hello there, Ray" greeting: "Hello there, Ray" greeting: "Hello there, Ray"
6. 축하합니다.
학습한 내용:
- 프로토콜 버퍼 언어
- 자바를 사용하여 gRPC 서버를 구현하는 방법
- 자바를 사용하여 gRPC 클라이언트를 구현하는 방법
다음 단계:
- gRPC 자바에 대해 자세히 알아보기
- GitHub의 gRPC Java 예 더보기
- gRPC의 스트리밍에 대해 알아보기
- gRPC로 REST 트랜스코딩에 대해 자세히 알아보기
Google에 의견 보내기
- 잠시 시간을 내어 간단한 설문조사에 응해주시기 바랍니다.