การสร้างบริการ gRPC ด้วย Java

1. ภาพรวม

gRPC เป็นเฟรมเวิร์กและชุดเครื่องมือสำหรับการเรียกกระบวนการทำงานจากระยะไกล (RPC) ที่ไม่ขึ้นกับภาษาและแพลตฟอร์ม ซึ่งพัฒนาขึ้นที่ Google ซึ่งช่วยให้คุณกำหนดบริการโดยใช้ Protocol Buffers ซึ่งเป็นชุดเครื่องมือและภาษาสำหรับการซีเรียลไลซ์แบบไบนารีที่มีประสิทธิภาพเป็นพิเศษ จากนั้นจะช่วยให้คุณสร้าง Stub ไคลเอ็นต์และเซิร์ฟเวอร์ที่เหมาะสมจากคำจำกัดความของบริการในภาษาต่างๆ ได้

ในโค้ดแล็บนี้ คุณจะได้เรียนรู้วิธีสร้างบริการที่ใช้ Java ซึ่งแสดง API โดยใช้เฟรมเวิร์ก gRPC จากนั้นเขียนไคลเอ็นต์เพื่อใช้ Stub ฝั่งไคลเอ็นต์ gRPC ที่สร้างขึ้น

สิ่งที่คุณจะได้เรียนรู้

  • ภาษาบัฟเฟอร์โปรโตคอล
  • วิธีติดตั้งใช้งานบริการ gRPC โดยใช้ Java
  • วิธีติดตั้งใช้งานไคลเอ็นต์ gRPC โดยใช้ Java

คุณจะใช้บทแนะนำนี้อย่างไร

อ่านอย่างเดียว อ่านและทำแบบฝึกหัด

คุณจะให้คะแนนประสบการณ์ในการสร้างแอป Node.js เท่าใด

ผู้ฝึกหัด ขั้นกลาง ผู้ชำนาญ

คุณจะให้คะแนนประสบการณ์ในการสร้างแอป Go เท่าใด

ผู้ฝึกหัด ขั้นกลาง ผู้ชำนาญ

2. การตั้งค่าและข้อกำหนด

การตั้งค่าสภาพแวดล้อมแบบเรียนรู้ด้วยตนเอง

  1. ลงชื่อเข้าใช้ Cloud Console แล้วสร้างโปรเจ็กต์ใหม่หรือใช้โปรเจ็กต์ที่มีอยู่ซ้ำ หากยังไม่มีบัญชี Gmail หรือ Google Workspace คุณต้องสร้างบัญชี

96a9c957bc475304.png

b9a10ebdf5b5a448.png

a1e3c01a38fa61c2.png

โปรดจดจำรหัสโปรเจ็กต์ ซึ่งเป็นชื่อที่ไม่ซ้ำกันในโปรเจ็กต์ Google Cloud ทั้งหมด (ชื่อด้านบนมีผู้ใช้แล้วและจะใช้ไม่ได้ ขออภัย) ซึ่งจะเรียกว่า PROJECT_ID ในภายหลังใน Codelab นี้

  1. จากนั้นคุณจะต้องเปิดใช้การเรียกเก็บเงินใน Cloud Console เพื่อใช้ทรัพยากร Google Cloud

การทำตาม Codelab นี้ไม่ควรมีค่าใช้จ่ายมากนัก หรืออาจไม่มีเลย โปรดทำตามวิธีการในส่วน "การล้างข้อมูล" ซึ่งจะแนะนำวิธีปิดทรัพยากรเพื่อไม่ให้มีการเรียกเก็บเงินนอกเหนือจากบทแนะนำนี้ ผู้ใช้ Google Cloud รายใหม่มีสิทธิ์เข้าร่วมโปรแกรมช่วงทดลองใช้ฟรีมูลค่า$300 USD

Google Cloud Shell

แม้ว่าคุณจะใช้งาน Codelab นี้จากคอมพิวเตอร์ได้ แต่ใน Codelab นี้เราจะใช้ Google Cloud Shell ซึ่งเป็นสภาพแวดล้อมบรรทัดคำสั่งที่ทำงานในระบบคลาวด์

เครื่องเสมือนที่ใช้ Debian นี้มาพร้อมเครื่องมือพัฒนาทั้งหมดที่คุณต้องการ โดยมีไดเรกทอรีหลักแบบถาวรขนาด 5 GB และทำงานใน Google Cloud ซึ่งช่วยเพิ่มประสิทธิภาพเครือข่ายและการตรวจสอบสิทธิ์ได้อย่างมาก ซึ่งหมายความว่าคุณจะต้องมีเพียงเบราว์เซอร์เท่านั้นสำหรับโค้ดแล็บนี้ (ใช่แล้ว ใช้ได้ใน Chromebook)

  1. หากต้องการเปิดใช้งาน Cloud Shell จาก Cloud Console เพียงคลิกเปิดใช้งาน Cloud Shell a8460e837e9f5fda.png (ระบบจะใช้เวลาเพียงไม่กี่นาทีในการจัดสรรและเชื่อมต่อกับสภาพแวดล้อม)

b532b2f19ab85dda.png

Screen Shot 2017-06-14 at 10.13.43 PM.png

เมื่อเชื่อมต่อกับ 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 ตรวจสอบว่าคุณใช้รหัสใดในขั้นตอนการตั้งค่า หรือค้นหารหัสในแดชบอร์ด Cloud Console

2485e00c1223af09.png

นอกจากนี้ Cloud Shell ยังตั้งค่าตัวแปรสภาพแวดล้อมบางอย่างโดยค่าเริ่มต้น ซึ่งอาจมีประโยชน์เมื่อคุณเรียกใช้คำสั่งในอนาคต

echo $GOOGLE_CLOUD_PROJECT

เอาต์พุตของคำสั่ง

<PROJECT_ID>
  1. สุดท้าย ให้ตั้งค่าโซนเริ่มต้นและการกำหนดค่าโปรเจ็กต์
gcloud config set compute/zone us-central1-f

คุณเลือกโซนต่างๆ ได้หลากหลาย ดูข้อมูลเพิ่มเติมได้ที่ภูมิภาคและโซน

3. สร้างบริการ gRPC

สร้างโปรเจ็กต์ Java ใหม่ด้วย Maven

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

เพิ่มไฟล์คำจำกัดความ gRPC

ใน gRPC เพย์โหลดของบริการ (คำขอและการตอบกลับ) และการดำเนินการของบริการต้องได้รับการบันทึกใน IDL (Interface Definition Language) gRPC ใช้ไวยากรณ์ Protobuffer 3 เพื่อกำหนดเพย์โหลดของข้อความและการดำเนินการ มาสร้างไฟล์ Proto สำหรับบริการทักทายอย่างง่ายที่มีคำขอ Hello และการตอบกลับ Hello กัน

ก่อนอื่นให้สร้างไดเรกทอรี 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

เมื่อคุณมีคำจำกัดความแล้ว เราจะสร้างทั้ง Stub ฝั่งเซิร์ฟเวอร์และ Stub ฝั่งไคลเอ็นต์จากไฟล์นี้ได้ คุณจะต้องเพิ่มทรัพยากร Dependency และปลั๊กอิน gRPC

ก่อนอื่น ให้เพิ่มการอ้างอิง gRPC ลงใน 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>

จากนั้นเพิ่มปลั๊กอินโดยทำดังนี้

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>

สร้าง Stub

เมื่อคุณสร้างแอปพลิเคชัน ปลั๊กอินจะแปลงคำจำกัดความ Proto เป็นโค้ด Java

$ mvn -DskipTests package

วิธีดูไฟล์ที่สร้างขึ้น

$ find target/generated-sources

ติดตั้งใช้งานบริการ

ก่อนอื่น ให้สร้างGreetingServiceImplคลาสใหม่ที่จะใช้การดำเนินการ 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();
  }
}

ติดตั้งใช้งานเซิร์ฟเวอร์

สุดท้าย คุณจะต้องเริ่มเซิร์ฟเวอร์เพื่อรับฟังพอร์ตและลงทะเบียนการติดตั้งใช้งานบริการนี้ แก้ไขApp class และเมธอดหลักของ class ดังนี้

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. การใช้บริการ

เครื่องมือสร้างได้สร้าง Stub ฝั่งไคลเอ็นต์ทั้งหมดแล้ว เพื่อให้แล็บนี้ง่ายขึ้น เราจะใช้โปรเจ็กต์ Maven เดียวกัน แต่จะเพิ่มคลาส Client ใหม่ที่มีเมธอด main ใหม่

ก่อนอื่น ให้คลิก + เพื่อเปิดเซสชัน Cloud Shell ใหม่ คุณจึงไม่ต้องสิ้นสุดเซิร์ฟเวอร์

1ff0fda960b9adfc.png

ในเซสชันใหม่ ให้เปลี่ยนไปที่ไดเรกทอรี 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. บริการสตรีมมิง

คุณลองทำสิ่งอื่นๆ ได้อีกมากมาย เช่น คุณสร้างบริการสตรีมมิงได้อย่างง่ายดายเพียงเพิ่มคีย์เวิร์ด stream ในไฟล์ Proto ไปยังพารามิเตอร์คำขอหรือการตอบกลับ เช่น

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 แบบอะซิงโครนัสแทน Stub ที่บล็อก อัปเดตรหัสไคลเอ็นต์โดยตรวจสอบว่าได้อัปเดตประเภท 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 โดยใช้ Java
  • วิธีติดตั้งใช้งานไคลเอ็นต์ gRPC โดยใช้ Java

ขั้นตอนถัดไป:

แสดงความคิดเห็น

  • โปรดสละเวลาสักครู่เพื่อทำแบบสำรวจสั้นๆ ของเรา