إنشاء خدمة gRPC باستخدام Java

1. نظرة عامة

gRPC هو إطار عمل ومجموعة أدوات لإجراءات الاتصال عن بُعد على نطاق محايد ومحايد في اللغة والنظام الأساسي تم تطويره في Google. تتيح لك هذه الأداة تعريف خدمة باستخدام Protocol Buffers، وهي مجموعة أدوات ولغة تسلسلية ثنائية فعّالة بشكل خاص. وتتيح لك هذه الأداة بعد ذلك إنشاء رموز برمجية أصلية للعميل والخادم من تعريف الخدمة الخاص بك إلى مجموعة متنوّعة من اللغات.

في هذا الدرس التطبيقي، ستتعرّف على كيفية إنشاء خدمة مستندة إلى Java تعرض واجهة برمجة تطبيقات باستخدام إطار عمل gRPC، ثم ستكتب برنامج عميل لاستخدام بروتوكول 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.

  1. بعد ذلك، عليك تفعيل الفوترة في Cloud Console لاستخدام موارد Google Cloud.

إنّ تنفيذ هذا الدرس التطبيقي حول الترميز لن يكون مكلفًا أو مكلفًا على الإطلاق. احرص على اتّباع أي تعليمات في قسم "الحذف سريعًا". الذي يقدم لك نصائح حول كيفية إيقاف تشغيل الموارد حتى لا تتكبّد أي فواتير خارج نطاق هذا البرنامج التعليمي. يكون مستخدمو Google Cloud الجدد مؤهَّلون للانضمام إلى برنامج فترة تجريبية مجانية بقيمة 300 دولار أمريكي.

Google Cloud Shell

مع أنّه يمكن إدارة هذا الدرس التطبيقي من الكمبيوتر، سنستخدم Google Cloud Shell، وهي بيئة سطر أوامر يتم تشغيلها في السحابة الإلكترونية في هذا الدرس التطبيقي.

هذا الجهاز الافتراضي المستند إلى نظام دبيان محمل بكل أدوات التطوير التي ستحتاج إليها. وتوفّر هذه الشبكة دليلاً رئيسيًا دائمًا بسعة 5 غيغابايت ويتم تشغيله في Google Cloud، ما يحسّن بشكل كبير من أداء الشبكة والمصادقة. وهذا يعني أنّ كل ما ستحتاجه في هذا الدرس التطبيقي حول الترميز هو متصفّح (نعم، يعمل على جهاز Chromebook).

  1. لتفعيل Cloud Shell من Cloud Console، ما عليك سوى النقر على تفعيل Cloud Shell a8460e837e9f5fda.png (من المفترَض أن تستغرق عملية الإعداد والاتصال بالبيئة بضع دقائق فقط).

b532b2f19ab85dda.png

لقطة شاشة يوم 14-06-2017 في الساعة 10.13.43 مساءً.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). تستخدم gRPC بنية Protobuffer 3 لتحديد حمولات الرسائل والعمليات. لننشئ ملفًا أوليًا لخدمة ترحيب بسيطة تتضمن طلب ترحيب ورد ترحيب.

أولاً، قم بإنشاء دليل 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 ومكوناتها الإضافية.

أولاً، أضِف تبعيات 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>

إنشاء البذور

عند إنشاء التطبيق، سيقوم المكون الإضافي بتحويل تعريفات 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 وهي الطريقة الأساسية التالية:

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 جديدة حتى لا تحتاج إلى إنهاء الخادم:

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 إلى 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

الخطوات التالية:

يُرجى إرسال ملاحظاتك إلينا

  • يُرجى تخصيص بعض الوقت لإكمال الاستطلاع القصير جدًا.