ساخت سرویس gRPC با جاوا

۱. مرور کلی

gRPC یک چارچوب و مجموعه ابزار فراخوانی رویه از راه دور (RPC) است که در گوگل توسعه داده شده است. این چارچوب به شما امکان می‌دهد با استفاده از Protocol Buffers، یک مجموعه ابزار و زبان سریال‌سازی دودویی بسیار قدرتمند، یک سرویس تعریف کنید. سپس به شما امکان می‌دهد stubهای اصطلاحی کلاینت و سرور را از تعریف سرویس خود به زبان‌های مختلف تولید کنید.

در این آزمایشگاه کد، یاد خواهید گرفت که چگونه یک سرویس مبتنی بر جاوا بسازید که با استفاده از چارچوب gRPC یک API را در معرض نمایش قرار می‌دهد، و سپس یک کلاینت بنویسید که از stub تولید شده سمت کلاینت gRPC استفاده کند.

آنچه یاد خواهید گرفت

  • زبان بافر پروتکل
  • نحوه پیاده سازی سرویس gRPC با استفاده از جاوا
  • نحوه پیاده‌سازی کلاینت gRPC با استفاده از جاوا

چگونه از این آموزش استفاده خواهید کرد؟

فقط تا انتها بخوانید آن را بخوانید و تمرین‌ها را انجام دهید

تجربه خود را در ساخت برنامه‌های Node.js چگونه ارزیابی می‌کنید؟

تازه کار متوسط ماهر

تجربه خود را در ساخت برنامه‌های Go چگونه ارزیابی می‌کنید؟

تازه کار متوسط ماهر

۲. تنظیمات و الزامات

تنظیم محیط خودتنظیم

  1. وارد Cloud Console شوید و یک پروژه جدید ایجاد کنید یا از یک پروژه موجود دوباره استفاده کنید. اگر از قبل حساب Gmail یا Google Workspace ندارید، باید یکی ایجاد کنید .

96a9c957bc475304.png

b9a10ebdf5b5a448.png

a1e3c01a38fa61c2.png

شناسه پروژه را به خاطر بسپارید، یک نام منحصر به فرد در تمام پروژه‌های Google Cloud (نام بالا قبلاً گرفته شده و برای شما کار نخواهد کرد، متاسفیم!). بعداً در این آزمایشگاه کد به آن PROJECT_ID گفته خواهد شد.

  1. در مرحله بعد، برای استفاده از منابع گوگل کلود، باید پرداخت را در Cloud Console فعال کنید .

اجرای این آزمایشگاه کد، اگر اصلاً هزینه‌ای نداشته باشد، نباید هزینه زیادی داشته باشد. حتماً دستورالعمل‌های بخش «پاکسازی» را که به شما نحوه خاموش کردن منابع را آموزش می‌دهد، دنبال کنید تا پس از این آموزش، متحمل هزینه نشوید. کاربران جدید Google Cloud واجد شرایط برنامه آزمایشی رایگان ۳۰۰ دلاری هستند.

پوسته ابری گوگل

اگرچه این آزمایشگاه کد می‌تواند از طریق رایانه شما اجرا شود، اما در این آزمایشگاه کد ما از Google Cloud Shell ، یک محیط خط فرمان که در فضای ابری اجرا می‌شود، استفاده خواهیم کرد.

این ماشین مجازی مبتنی بر دبیان، تمام ابزارهای توسعه مورد نیاز شما را در خود جای داده است. این ماشین مجازی یک دایرکتوری خانگی ۵ گیگابایتی دائمی ارائه می‌دهد و در فضای ابری گوگل اجرا می‌شود که عملکرد شبکه و احراز هویت را تا حد زیادی بهبود می‌بخشد. این بدان معناست که تنها چیزی که برای این آزمایشگاه کد نیاز دارید یک مرورگر است (بله، روی کروم‌بوک هم کار می‌کند).

  1. برای فعال کردن Cloud Shell از کنسول Cloud، کافیست روی Activate Cloud Shell کلیک کنید. a8460e837e9f5fda.png (فقط چند لحظه طول می‌کشد تا آماده شود و به محیط متصل شود).

b532b2f19ab85dda.png

اسکرین شات 2017-06-14 ساعت 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

شما می‌توانید مناطق مختلفی را انتخاب کنید. برای اطلاعات بیشتر، به بخش مناطق و نواحی مراجعه کنید.

۳. ایجاد یک سرویس gRPC

یک پروژه جاوا جدید با 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 (زبان تعریف رابط) ثبت شوند. gRPC از سینتکس Protobuffer 3 برای تعریف بارهای پیام و عملیات استفاده می‌کند. بیایید یک فایل proto برای یک سرویس خوشامدگویی ساده با یک درخواست سلام و پاسخ سلام ایجاد کنیم.

ابتدا، یک دایرکتوری جدید proto برای نگهداری فایل جدید proto ایجاد کنید:

$ mkdir -p src/main/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 سمت کلاینت را از این فایل تولید کنیم. باید وابستگی‌ها و افزونه‌های 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 را به کد جاوا تبدیل می‌کند.

$ 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 و متد main آن را ویرایش کنید:

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

۴. استفاده از سرویس

مولد از قبل تمام 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"

همین! خیلی ساده‌ست، نه؟

۵. سرویس استریم

چیزهای خیلی بیشتری هست که می‌توانید امتحان کنید. برای مثال، می‌توانید به راحتی و با اضافه کردن کلمه کلیدی 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"

۶. تبریک می‌گویم!

آنچه ما پوشش داده‌ایم:

  • زبان بافر پروتکل
  • نحوه پیاده سازی سرور gRPC با استفاده از جاوا
  • نحوه پیاده‌سازی کلاینت gRPC با استفاده از جاوا

مراحل بعدی:

نظرات خود را با ما در میان بگذارید

  • لطفا کمی وقت بگذارید و نظرسنجی بسیار کوتاه ما را تکمیل کنید.