Xây dựng dịch vụ gRPC bằng Java

1. Tổng quan

gRPC là một khung và bộ công cụ cho lệnh gọi quy trình từ xa (RPC) trung lập về ngôn ngữ và nền tảng do Google phát triển. Lớp này cho phép bạn xác định dịch vụ bằng Vùng đệm giao thức, bộ công cụ và ngôn ngữ chuyển đổi tuần tự nhị phân đặc biệt mạnh mẽ. Sau đó, bạn có thể tạo các mã giả lập máy khách và máy chủ tương thích từ định nghĩa dịch vụ bằng nhiều ngôn ngữ.

Trong lớp học lập trình này, bạn sẽ tìm hiểu cách xây dựng một dịch vụ dựa trên Java để hiển thị một API bằng khung gRPC, sau đó viết một ứng dụng khách để sử dụng mã giả lập phía máy khách gRPC đã tạo.

Kiến thức bạn sẽ học được

  • Ngôn ngữ vùng đệm giao thức
  • Cách triển khai dịch vụ gRPC bằng Java
  • Cách triển khai ứng dụng gRPC bằng Java

Bạn sẽ dùng hướng dẫn này như thế nào?

Chỉ có thể đọc Đọc và hoàn thành bài tập

Bạn đánh giá trải nghiệm của mình khi xây dựng ứng dụng Node.js như thế nào?

Người mới tập Trung cấp Thành thạo

Bạn đánh giá thế nào về trải nghiệm xây dựng ứng dụng Go?

Người mới tập Trung cấp Thành thạo

2. Thiết lập và yêu cầu

Thiết lập môi trường theo tiến độ riêng

  1. Đăng nhập vào Cloud Console rồi tạo dự án mới hoặc sử dụng lại dự án hiện có. Nếu chưa có tài khoản Gmail hoặc Google Workspace, bạn phải tạo một tài khoản.

96a9c957bc475304.pngs

b9a10ebdf5b5a448.png

a1e3c01a38fa61c2.png

Xin lưu ý rằng mã dự án là một tên riêng biệt trong tất cả dự án Google Cloud (tên ở trên đã được sử dụng nên sẽ không phù hợp với bạn!). Lớp này sẽ được đề cập sau trong lớp học lập trình này là PROJECT_ID.

  1. Tiếp theo, bạn sẽ cần bật tính năng thanh toán trong Cloud Console để sử dụng tài nguyên của Google Cloud.

Việc chạy qua lớp học lập trình này sẽ không tốn nhiều chi phí. Hãy nhớ làm theo mọi hướng dẫn trong phần "Dọn dẹp" sẽ tư vấn cho bạn cách tắt tài nguyên để bạn không phải chịu thanh toán ngoài hướng dẫn này. Người dùng mới của Google Cloud đủ điều kiện tham gia chương trình Dùng thử miễn phí 300 USD.

Google Cloud Shell

Mặc dù bạn có thể vận hành lớp học lập trình này trên máy tính, nhưng trong lớp học lập trình này, chúng ta sẽ sử dụng Google Cloud Shell, một môi trường dòng lệnh chạy trên Đám mây.

Máy ảo dựa trên Debian này được tải tất cả các công cụ phát triển mà bạn cần. Dịch vụ này cung cấp thư mục gốc 5 GB ổn định và chạy trong Google Cloud, giúp nâng cao đáng kể hiệu suất và khả năng xác thực của mạng. Tức là tất cả những gì bạn cần để thực hiện lớp học lập trình này là một trình duyệt (vâng, trình duyệt này hoạt động trên Chromebook).

  1. Để kích hoạt Cloud Shell trong Cloud Console, bạn chỉ cần nhấp vào Kích hoạt Cloud Shell a8460e837e9f5fda.png (chỉ mất vài phút để cấp phép và kết nối với môi trường).

b532b2f19ab85dda.png

Ảnh chụp màn hình lúc 10:13.43 chiều 14/6/2017.png

Sau khi kết nối với Cloud Shell, bạn sẽ thấy mình đã được xác thực và dự án đã được đặt thành PROJECT_ID.

gcloud auth list

Kết quả lệnh

Credentialed accounts:
 - <myaccount>@<mydomain>.com (active)
gcloud config list project

Kết quả lệnh

[core]
project = <PROJECT_ID>

Nếu vì lý do nào đó mà dự án không được thiết lập, chỉ cần phát hành lệnh sau:

gcloud config set project <PROJECT_ID>

Bạn đang tìm PROJECT_ID? Hãy xem mã nhận dạng bạn đã sử dụng ở các bước thiết lập hoặc tra cứu trong trang tổng quan Cloud Console:

2485e00c1223af09.pngs

Cloud Shell cũng đặt một số biến môi trường theo mặc định. Điều này có thể hữu ích khi bạn chạy các lệnh sau này.

echo $GOOGLE_CLOUD_PROJECT

Kết quả lệnh

<PROJECT_ID>
  1. Cuối cùng, đặt cấu hình dự án và vùng mặc định.
gcloud config set compute/zone us-central1-f

Bạn có thể chọn nhiều vùng khác nhau. Để biết thêm thông tin, hãy xem Khu vực và Vùng.

3. Tạo dịch vụ gRPC

Tạo một dự án Java mới bằng Maven:

$ mvn archetype:generate -DgroupId=com.example.grpc \
 -DartifactId=grpc-hello-server \
 -DarchetypeArtifactId=maven-archetype-quickstart \
 -DinteractiveMode=false
$ cd grpc-hello-server

Thêm tệp định nghĩa gRPC

Trong gRPC, tải trọng dịch vụ (yêu cầu và phản hồi) cũng như hoạt động dịch vụ cần được ghi lại bằng IDL (Ngôn ngữ định nghĩa giao diện). gRPC sử dụng cú pháp Protobuffer 3 để xác định tải trọng và hoạt động của thông báo. Hãy tạo một tệp proto cho một Dịch vụ lời chào đơn giản với hai câu lệnh Hello Request (Xin chào) và Hello Response (phản hồi).

Trước tiên, hãy tạo một thư mục proto mới để lưu giữ tệp proto mới:

$ mkdir -p src/main/proto

Sau đó, hãy tạo một tệp proto mới src/main/proto/GreetingService.proto.

Bạn có thể dùng vim,nano, hoặc emacs để chỉnh sửa tệp:

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);
}

Thêm phần phụ thuộc và trình bổ trợ gRPC

Sau khi bạn có định nghĩa, chúng ta có thể tạo cả mã giả lập phía máy chủ và mã giả lập phía máy khách qua tệp này. Bạn cần thêm các phần phụ thuộc và trình bổ trợ gRPC.

Trước tiên, hãy thêm các phần phụ thuộc gRPC vào pom.xml:

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>

Sau đó, thêm trình bổ trợ:

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>

Tạo mã giả lập

Khi bạn xây dựng ứng dụng, trình bổ trợ sẽ chuyển đổi các định nghĩa proto thành mã Java.

$ mvn -DskipTests package

Cách xem các tệp đã tạo:

$ find target/generated-sources

Triển khai dịch vụ

Trước tiên, hãy tạo một lớp GreetingServiceImpl mới sẽ triển khai thao tác greeting:

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();
  }
}

Triển khai máy chủ

Cuối cùng, bạn cần khởi động một máy chủ để theo dõi một cổng và đăng ký phương thức triển khai dịch vụ này. Chỉnh sửa lớp App và đây là phương thức chính:

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();
    }
}

Cuối cùng, hãy chạy máy chủ:

$ mvn -DskipTests package exec:java -Dexec.mainClass=com.example.grpc.App
...
Server Started

4. Sử dụng Dịch vụ

Trình tạo này đã tạo tất cả mã giả lập phía máy khách. Để đơn giản hoá phòng thí nghiệm này, chúng ta sẽ sử dụng cùng một dự án Maven, nhưng chỉ cần thêm một lớp Client mới bằng phương thức chính mới.

Trước tiên, hãy nhấp vào dấu + để mở một phiên Cloud Shell mới để bạn không cần phải chấm dứt máy chủ:

1ff0fda960b9adfc.png

Trong phiên mới, chuyển sang thư mục grpc-hello-server:

$ cd grpc-hello-server

Sau đó, hãy thêm lớp Client mới:

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();
    }
}

Cuối cùng, hãy chạy ứng dụng:

$ mvn -DskipTests package exec:java -Dexec.mainClass=com.example.grpc.Client
...
greeting: "Hello there, Ray"

Vậy là xong! Khá đơn giản phải không?

5. Dịch vụ xem trực tuyến

Còn rất nhiều tính năng khác mà bạn có thể thử. Ví dụ: Bạn có thể dễ dàng xây dựng một dịch vụ truyền trực tuyến chỉ bằng cách thêm từ khoá stream trong tệp proto vào yêu cầu hoặc tham số phản hồi, chẳng hạn như

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);
}

Hãy cập nhật máy chủ của bạn để gửi nhiều phản hồi thay vì chỉ một phản hồi. Bạn có thể thực hiện việc này bằng cách thực hiện nhiều lệnh gọi 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();
  }
}

Ứng dụng phải sử dụng mã giả lập không đồng bộ thay vì mã giả lập chặn. Cập nhật mã ứng dụng khách, nhớ cập nhật loại stub thành 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();
        }
      });
    }
}

Tạo lại ứng dụng:

$ mvn -DskipTests package

Khởi động lại cả máy chủ và máy khách trong phiên Cloud Shell riêng.

Cách khởi động máy chủ:

$ mvn exec:java -Dexec.mainClass=com.example.grpc.App
...
Server Started

Cách bắt đầu ứng dụng:

$ mvn exec:java -Dexec.mainClass=com.example.grpc.Client
...
greeting: "Hello there, Ray"
greeting: "Hello there, Ray"
greeting: "Hello there, Ray"

6. Xin chúc mừng!

Nội dung đã đề cập:

  • Ngôn ngữ vùng đệm giao thức
  • Cách triển khai máy chủ gRPC bằng Java
  • Cách triển khai ứng dụng gRPC bằng Java

Các bước tiếp theo:

Gửi ý kiến phản hồi cho chúng tôi

  • Vui lòng dành chút thời gian để hoàn thành bản khảo sát rất ngắn của chúng tôi