gRPC-জাভা দিয়ে শুরু করা

1. ভূমিকা

এই কোডল্যাবে, আপনি gRPC-Java ব্যবহার করে একটি ক্লায়েন্ট এবং সার্ভার তৈরি করবেন যা জাভাতে লেখা একটি রুট-ম্যাপিং অ্যাপ্লিকেশনের ভিত্তি তৈরি করবে।

টিউটোরিয়ালের শেষে, আপনার কাছে এমন একটি ক্লায়েন্ট থাকবে যা gRPC ব্যবহার করে একটি দূরবর্তী সার্ভারের সাথে সংযোগ স্থাপন করবে এবং মানচিত্রের নির্দিষ্ট স্থানাঙ্কে অবস্থিত জিনিসের নাম বা ডাক ঠিকানা পাবে। একটি সম্পূর্ণরূপে উন্নত অ্যাপ্লিকেশন এই ক্লায়েন্ট-সার্ভার ডিজাইন ব্যবহার করে কোনও রুটের আগ্রহের বিষয়গুলি গণনা বা সংক্ষিপ্ত করতে পারে।

সার্ভারের API একটি প্রোটোকল বাফার ফাইলে সংজ্ঞায়িত করা হয়, যা ক্লায়েন্ট এবং সার্ভারের জন্য বয়লারপ্লেট কোড তৈরি করতে ব্যবহার করা হবে যাতে তারা একে অপরের সাথে যোগাযোগ করতে পারে, সেই কার্যকারিতা বাস্তবায়নে আপনার সময় এবং শ্রম সাশ্রয় করে।

এই জেনারেট করা কোডটি কেবল সার্ভার এবং ক্লায়েন্টের মধ্যে যোগাযোগের জটিলতাই নয়, ডেটা সিরিয়ালাইজেশন এবং ডিসিরিয়ালাইজেশনেরও যত্ন নেয়।

তুমি কি শিখবে

  • একটি পরিষেবা API সংজ্ঞায়িত করতে প্রোটোকল বাফার কীভাবে ব্যবহার করবেন।
  • স্বয়ংক্রিয় কোড জেনারেশন ব্যবহার করে প্রোটোকল বাফার সংজ্ঞা থেকে কীভাবে একটি gRPC-ভিত্তিক ক্লায়েন্ট এবং সার্ভার তৈরি করবেন।
  • জিআরপিসির সাথে ক্লায়েন্ট-সার্ভার যোগাযোগের ধারণা।

এই কোডল্যাবটি জাভা ডেভেলপারদের জন্য যারা gRPC তে নতুন অথবা gRPC এর রিফ্রেশার খুঁজছেন, অথবা ডিস্ট্রিবিউটেড সিস্টেম তৈরিতে আগ্রহী অন্যদের জন্য। এর জন্য কোনও পূর্ববর্তী gRPC অভিজ্ঞতার প্রয়োজন নেই।

2. শুরু করার আগে

পূর্বশর্ত

  • JDK সংস্করণ ৮ বা তার বেশি

কোডটি পান

যাতে আপনাকে সম্পূর্ণরূপে স্ক্র্যাচ থেকে শুরু করতে না হয়, এই কোডল্যাবটি আপনাকে অ্যাপ্লিকেশনটির সোর্স কোডের একটি স্ক্যাফোল্ড প্রদান করে। নিম্নলিখিত ধাপগুলি আপনাকে অ্যাপ্লিকেশনটি কীভাবে শেষ করতে হবে তা দেখাবে, যার মধ্যে বয়লারপ্লেট জিআরপিসি কোড তৈরি করতে প্রোটোকল বাফার কম্পাইলার প্লাগইন ব্যবহার করাও অন্তর্ভুক্ত।

প্রথমে, কোডল্যাব ওয়ার্কিং ডিরেক্টরি তৈরি করুন এবং এতে সিডি দিন:

mkdir grpc-java-getting-started && cd grpc-java-getting-started

কোডল্যাবটি ডাউনলোড করে এক্সট্রাক্ট করুন:

curl -sL https://github.com/grpc-ecosystem/grpc-codelabs/archive/refs/heads/v1.tar.gz \
  | tar xvz --strip-components=4 \
  grpc-codelabs-1/codelabs/grpc-java-getting-started/start_here

অন্যথায়, আপনি শুধুমাত্র কোডল্যাব ডিরেক্টরি সম্বলিত .zip ফাইলটি ডাউনলোড করতে পারেন এবং ম্যানুয়ালি এটি আনজিপ করতে পারেন।

যদি আপনি কোনও বাস্তবায়নে টাইপিং এড়িয়ে যেতে চান, তাহলে সম্পূর্ণ সোর্স কোডটি GitHub-এ উপলব্ধ

৩. পরিষেবাটি সংজ্ঞায়িত করুন

আপনার প্রথম পদক্ষেপ হল অ্যাপ্লিকেশনের gRPC পরিষেবা, এর RPC পদ্ধতি এবং Protocol Buffers ব্যবহার করে এর অনুরোধ এবং প্রতিক্রিয়া বার্তার ধরণগুলি সংজ্ঞায়িত করা। আপনার পরিষেবাটি প্রদান করবে:

  • GetFeature নামক একটি RPC পদ্ধতি যা সার্ভার প্রয়োগ করে এবং ক্লায়েন্ট কল করে।
  • GetFeature পদ্ধতি ব্যবহার করার সময় ক্লায়েন্ট এবং সার্ভারের মধ্যে আদান-প্রদান করা ডেটা স্ট্রাকচার হল Point এবং Feature । ক্লায়েন্ট সার্ভারে তার GetFeature অনুরোধে Point হিসেবে ম্যাপ কোঅর্ডিনেট প্রদান করে এবং সার্ভার একটি সংশ্লিষ্ট Feature মাধ্যমে উত্তর দেয় যা সেই কোঅর্ডিনেটগুলিতে যা কিছু আছে তা বর্ণনা করে।

এই RPC পদ্ধতি এবং এর বার্তার ধরণগুলি প্রদত্ত সোর্স কোডের src/main/proto/routeguide/route_guide.proto ফাইলে সংজ্ঞায়িত করা হবে।

প্রোটোকল বাফারগুলি সাধারণত প্রোটোবাফ নামে পরিচিত। gRPC পরিভাষা সম্পর্কে আরও তথ্যের জন্য, gRPC এর মূল ধারণা, স্থাপত্য এবং জীবনচক্র দেখুন।

যেহেতু আমরা এই উদাহরণে জাভা কোড তৈরি করছি, তাই আমরা আমাদের .proto তে একটি java_package ফাইল বিকল্প এবং জাভা ক্লাসের জন্য একটি নাম নির্দিষ্ট করেছি:

option java_package = "io.grpc.examples.routeguide";
option java_outer_classname = "RouteGuideProto";

বার্তার ধরণ

সোর্স কোডের routeguide/route_guide.proto ফাইলে, প্রথমে Point বার্তার ধরণ নির্ধারণ করুন। একটি Point একটি মানচিত্রে একটি অক্ষাংশ-দ্রাঘিমাংশ স্থানাঙ্ক জোড়াকে প্রতিনিধিত্ব করে। এই কোডল্যাবের জন্য, স্থানাঙ্কগুলির জন্য পূর্ণসংখ্যা ব্যবহার করুন:

message Point {
  int32 latitude = 1;
  int32 longitude = 2;
}

message কাঠামোর প্রতিটি ক্ষেত্রের জন্য 1 এবং 2 নম্বরগুলি অনন্য আইডি নম্বর।

এরপর, Feature বার্তার ধরণ নির্ধারণ করুন। একটি Feature একটি Point দ্বারা নির্দিষ্ট স্থানে থাকা কোনও কিছুর নাম বা ডাক ঠিকানার জন্য একটি string ফিল্ড ব্যবহার করে:

message Feature {
  // The name or address of the feature.
  string name = 1;

  // The point where the feature is located.
  Point location = 2;
}

পরিষেবা পদ্ধতি

route_guide.proto ফাইলটিতে RouteGuide নামে একটি service কাঠামো রয়েছে যা অ্যাপ্লিকেশনের পরিষেবা দ্বারা প্রদত্ত এক বা একাধিক পদ্ধতি সংজ্ঞায়িত করে।

RouteGuide সংজ্ঞার ভেতরে rpc পদ্ধতি GetFeature যোগ করুন। যেমনটি আগে ব্যাখ্যা করা হয়েছে, এই পদ্ধতিটি একটি নির্দিষ্ট স্থানাঙ্কের সেট থেকে একটি অবস্থানের নাম বা ঠিকানা অনুসন্ধান করবে, তাই GetFeature একটি নির্দিষ্ট Point জন্য একটি Feature ফেরত দেবে:

service RouteGuide {
  // Definition of the service goes here

  // Obtains the feature at a given position.
  rpc GetFeature(Point) returns (Feature) {}
}

এটি একটি ইউনারি RPC পদ্ধতি: একটি সহজ RPC যেখানে ক্লায়েন্ট সার্ভারে একটি অনুরোধ পাঠায় এবং স্থানীয় ফাংশন কলের মতো প্রতিক্রিয়া ফিরে আসার জন্য অপেক্ষা করে।

৪. ক্লায়েন্ট এবং সার্ভার কোড তৈরি করুন

এরপর আমাদের .proto পরিষেবা সংজ্ঞা থেকে gRPC ক্লায়েন্ট এবং সার্ভার ইন্টারফেস তৈরি করতে হবে। আমরা একটি বিশেষ gRPC জাভা প্লাগইন সহ প্রোটোকল বাফার কম্পাইলার protoc ব্যবহার করে এটি করি। gRPC পরিষেবা তৈরি করতে আপনাকে proto3 কম্পাইলার (যা proto2 এবং proto3 সিনট্যাক্স উভয়কেই সমর্থন করে) ব্যবহার করতে হবে।

Gradle অথবা Maven ব্যবহার করার সময়, protoc build প্লাগইন বিল্ডের অংশ হিসেবে প্রয়োজনীয় কোড তৈরি করতে পারে। আপনার নিজস্ব .proto ফাইল থেকে কোড তৈরি করার পদ্ধতি জানতে আপনি grpc-java README দেখতে পারেন।

এই প্রকল্পটি তৈরি করার জন্য আমরা কোডল্যাবের সোর্স কোডে একটি গ্রেডল পরিবেশ এবং কনফিগারেশন প্রদান করেছি।

grpc-java-getting-started ডিরেক্টরির ভিতরে, নিম্নলিখিত কমান্ডটি চালান:

$ chmod +x gradlew
$ ./gradlew generateProto

আমাদের পরিষেবা সংজ্ঞা থেকে নিম্নলিখিত ক্লাসগুলি তৈরি করা হয়েছে:

  • Feature.java , Point.java , এবং অন্যান্য যেগুলিতে আমাদের অনুরোধ এবং প্রতিক্রিয়া বার্তার ধরণগুলি পূরণ, সিরিয়ালাইজ এবং পুনরুদ্ধার করার জন্য সমস্ত প্রোটোকল বাফার কোড থাকে।
  • RouteGuideGrpc.java যেখানে (আরও কিছু দরকারী কোড সহ) RouteGuide সার্ভার বাস্তবায়নের জন্য একটি বেস ক্লাস, RouteGuideGrpc.RouteGuideImplBase রয়েছে, যেখানে RouteGuide পরিষেবায় সংজ্ঞায়িত সমস্ত পদ্ধতি এবং ক্লায়েন্টদের ব্যবহারের জন্য স্টাব ক্লাস রয়েছে।

৫. সার্ভারটি বাস্তবায়ন করুন

প্রথমে দেখা যাক কিভাবে আমরা একটি RouteGuide সার্ভার তৈরি করি। আমাদের RouteGuide পরিষেবাকে তার কাজ করতে সাহায্য করার দুটি অংশ রয়েছে:

  • আমাদের পরিষেবার সংজ্ঞা থেকে তৈরি পরিষেবা ইন্টারফেস বাস্তবায়ন করা, যা আমাদের পরিষেবার প্রকৃত "কাজ" করে।
  • ক্লায়েন্টদের কাছ থেকে অনুরোধ শোনার জন্য এবং সঠিক পরিষেবা বাস্তবায়নে প্রেরণের জন্য একটি gRPC সার্ভার চালানো।

RouteGuide বাস্তবায়ন করুন

আপনি দেখতে পাচ্ছেন, আমাদের সার্ভারে একটি RouteGuideService ক্লাস রয়েছে যা জেনারেট করা RouteGuideGrpc.RouteGuideImplBase অ্যাবস্ট্রাক্ট ক্লাসকে প্রসারিত করে:

private static class RouteGuideService extends RouteGuideGrpc.RouteGuideImplBase {
...
}

আপনার সার্ভারে বৈশিষ্ট্যগুলি চালু করার জন্য আমরা নিম্নলিখিত 2টি ফাইল সরবরাহ করেছি:

./src/main/java/io/grpc/examples/routeguide/RouteGuideUtil.java

./src/main/resources/io/grpc/examples/routeguide/route_guide_db.json

আসুন একটি সহজ RPC বাস্তবায়ন বিস্তারিতভাবে দেখি।

ইউনারি আরপিসি

RouteGuideService আমাদের সকল পরিষেবা পদ্ধতি প্রয়োগ করে। এই ক্ষেত্রে এটি কেবল GetFeature() , ক্লায়েন্ট থেকে একটি Point বার্তা নেয় এবং একটি Feature বার্তায় পরিচিত স্থানের তালিকা থেকে সংশ্লিষ্ট অবস্থানের তথ্য ফেরত দেয়।

@Override
public void getFeature(Point request, StreamObserver<Feature> responseObserver) {
  responseObserver.onNext(checkFeature(request));
  responseObserver.onCompleted();
}

getFeature() পদ্ধতি দুটি পরামিতি গ্রহণ করে:

  • Point : অনুরোধ।
  • StreamObserver<Feature> : একটি রেসপন্স অবজারভার, যা সার্ভারের জন্য একটি বিশেষ ইন্টারফেস যার মাধ্যমে তারা তাদের রেসপন্সের মাধ্যমে কল করতে পারে।

ক্লায়েন্টকে আমাদের প্রতিক্রিয়া জানাতে এবং কলটি সম্পূর্ণ করতে:

  1. আমাদের পরিষেবা সংজ্ঞায় উল্লেখিত পদ্ধতি অনুযায়ী, আমরা ক্লায়েন্টে ফিরে আসার জন্য একটি Feature response object তৈরি এবং পূরণ করি। এই উদাহরণে, আমরা এটি একটি পৃথক private checkFeature() পদ্ধতিতে করি।
  2. আমরা Feature ফেরত দিতে রেসপন্স অবজারভারের onNext() পদ্ধতি ব্যবহার করি।
  3. আমরা RPC এর সাথে কাজ শেষ করেছি তা নির্দিষ্ট করার জন্য প্রতিক্রিয়া পর্যবেক্ষকের onCompleted() পদ্ধতি ব্যবহার করি।

সার্ভার শুরু করুন

আমাদের সকল পরিষেবা পদ্ধতি বাস্তবায়নের পর, আমাদের একটি gRPC সার্ভার শুরু করতে হবে যাতে ক্লায়েন্টরা আসলে আমাদের পরিষেবা ব্যবহার করতে পারে। আমরা আমাদের বয়লারপ্লেটে ServerBuilder অবজেক্ট তৈরির বিষয়টি অন্তর্ভুক্ত করি:

ServerBuilder.forPort(port), port, RouteGuideUtil.parseFeatures(featureFile)

আমরা কনস্ট্রাক্টরে পরিষেবাটি তৈরি করি:

  1. বিল্ডারের forPort() পদ্ধতি ব্যবহার করে ক্লায়েন্টের অনুরোধ শোনার জন্য আমরা যে পোর্টটি ব্যবহার করতে চাই তা নির্দিষ্ট করুন (এটি ওয়াইল্ডকার্ড ঠিকানা ব্যবহার করবে)।
  2. আমাদের পরিষেবা বাস্তবায়ন ক্লাস RouteGuideService এর একটি উদাহরণ তৈরি করুন এবং এটি বিল্ডারের addService() পদ্ধতিতে পাস করুন।
  3. আমাদের পরিষেবার জন্য একটি RPC সার্ভার তৈরি করতে বিল্ডারে build() কল করুন।

নিচের স্নিপেটটি দেখায় কিভাবে আমরা একটি ServerBuilder অবজেক্ট তৈরি করি।

/** Create a RouteGuide server listening on {@code port} using {@code featureFile} database. */
public RouteGuideServer(int port, URL featureFile) throws IOException {
    this(Grpc.newServerBuilderForPort(port, InsecureServerCredentials.create()),
        port, RouteGuideUtil.parseFeatures(featureFile));
  }

নিম্নলিখিত স্নিপেটটি দেখায় কিভাবে আমরা আমাদের RouteGuide পরিষেবার জন্য একটি সার্ভার অবজেক্ট তৈরি করি।

/** Create a RouteGuide server using serverBuilder as a base and features as data. */
public RouteGuideServer(ServerBuilder<?> serverBuilder, int port, Collection<Feature> features) {
  this.port = port;
  server = serverBuilder.addService(new RouteGuideService(features))
      .build();
}

উপরে তৈরি সার্ভারে কল start জন্য একটি স্টার্ট পদ্ধতি প্রয়োগ করুন।

public void start() throws IOException {
  server.start();
  logger.info("Server started, listening on " + port);
}

সার্ভারটি সম্পূর্ণ হওয়ার জন্য অপেক্ষা করার জন্য একটি পদ্ধতি প্রয়োগ করুন যাতে এটি তাৎক্ষণিকভাবে প্রস্থান না করে।

/** Await termination on the main thread since the grpc library uses daemon threads. */
private void blockUntilShutdown() throws InterruptedException {
  if (server != null) {
    server.awaitTermination();
  }
}

আপনি দেখতে পাচ্ছেন, আমরা একটি ServerBuilder ব্যবহার করে আমাদের সার্ভার তৈরি এবং শুরু করি।

মূল পদ্ধতিতে আমরা:

  1. একটি RouteGuideServer ইনস্ট্যান্স তৈরি করুন।
  2. আমাদের পরিষেবার জন্য একটি RPC সার্ভার সক্রিয় করতে start() এ কল করুন।
  3. blockUntilShutdown() কল করে পরিষেবাটি বন্ধ হওয়ার জন্য অপেক্ষা করুন।
 public static void main(String[] args) throws Exception {
    RouteGuideServer server = new RouteGuideServer(8980);
    server.start();
    server.blockUntilShutdown();
  }

৬. ক্লায়েন্ট তৈরি করুন

এই বিভাগে, আমরা আমাদের RouteGuide পরিষেবার জন্য একটি ক্লায়েন্ট তৈরি করার দিকে নজর দেব।

একটি স্টাব ইনস্ট্যান্টিয়েট করুন

সার্ভিস মেথড কল করার জন্য, আমাদের প্রথমে একটি স্টাব তৈরি করতে হবে। দুই ধরণের স্টাব আছে, কিন্তু এই কোডল্যাবের জন্য আমাদের কেবল ব্লকিংটি ব্যবহার করতে হবে। দুটি প্রকার হল:

  • একটি ব্লকিং/সিঙ্ক্রোনাস স্টাব যা একটি RPC কল করে এবং সার্ভারের প্রতিক্রিয়ার জন্য অপেক্ষা করে, এবং হয় একটি প্রতিক্রিয়া ফেরত দেবে অথবা একটি ব্যতিক্রম উত্থাপন করবে।
  • একটি নন-ব্লকিং/অ্যাসিঙ্ক্রোনাস স্টাব যা সার্ভারে নন-ব্লকিং কল করে, যেখানে প্রতিক্রিয়া অ্যাসিঙ্ক্রোনাসভাবে ফেরত পাঠানো হয়। আপনি শুধুমাত্র অ্যাসিঙ্ক্রোনাস স্টাব ব্যবহার করে নির্দিষ্ট ধরণের স্ট্রিমিং কল করতে পারেন।

প্রথমে আমাদের একটি gRPC চ্যানেল তৈরি করতে হবে এবং তারপর চ্যানেলটি ব্যবহার করে আমাদের স্টাব তৈরি করতে হবে।

আমরা চ্যানেলটি তৈরি করতে সরাসরি একটি ManagedChannelBuilder ব্যবহার করতে পারতাম।

ManagedChannelBuilder.forAddress(host, port).usePlaintext().build

কিন্তু আসুন একটি ইউটিলিটি পদ্ধতি ব্যবহার করি যা hostname:port সহ একটি স্ট্রিং নেয়।

Grpc.newChannelBuilder(target, InsecureChannelCredentials.create()).build();

এখন আমরা চ্যানেলটি ব্যবহার করে আমাদের ব্লকিং স্টাব তৈরি করতে পারি। এই কোডল্যাবের জন্য, আমাদের কাছে শুধুমাত্র ব্লকিং RPC আছে, তাই আমরা আমাদের .proto থেকে তৈরি RouteGuideGrpc ক্লাসে প্রদত্ত newBlockingStub পদ্ধতি ব্যবহার করি।

blockingStub = RouteGuideGrpc.newBlockingStub(channel);

কল পরিষেবা পদ্ধতি

এবার দেখা যাক আমরা আমাদের পরিষেবা পদ্ধতিগুলিকে কীভাবে কল করি।

সরল RPC

সাধারণ RPC GetFeature কল করা স্থানীয় পদ্ধতিতে কল করার মতোই সহজ।

আমরা একটি রিকোয়েস্ট প্রোটোকল বাফার অবজেক্ট তৈরি করি এবং পূরণ করি (আমাদের ক্ষেত্রে Point ), এটি আমাদের ব্লকিং স্টাবের getFeature() পদ্ধতিতে পাস করি এবং একটি Feature ফিরে পাই।

যদি কোনও ত্রুটি দেখা দেয়, তবে এটি একটি Status হিসাবে এনকোড করা হয়, যা আমরা StatusRuntimeException থেকে পেতে পারি।

Point request = Point.newBuilder().setLatitude(lat).setLongitude(lon).build();

Feature feature;
try {
  feature = blockingStub.getFeature(request);
} catch (StatusRuntimeException e) {
  logger.log(Level.WARNING, "RPC failed: {0}", e.getStatus());
  return;
}

নির্দিষ্ট স্থানে কোনও বৈশিষ্ট্য ছিল কিনা তার উপর ভিত্তি করে বয়লারপ্লেটটি বিষয়বস্তু সম্বলিত একটি বার্তা লগ করে।

৭. চেষ্টা করে দেখুন!

  1. start_here ডিরেক্টরির ভিতরে, নিম্নলিখিত কমান্ডটি চালান:
$ ./gradlew installDist

এটি আপনার কোড কম্পাইল করবে, একটি জারে প্যাকেজ করবে এবং উদাহরণটি চালানোর জন্য স্ক্রিপ্ট তৈরি করবে। এগুলি build/install/start_here/bin/ ডিরেক্টরিতে তৈরি করা হবে। স্ক্রিপ্টগুলি হল: route-guide-server এবং route-guide-client

ক্লায়েন্ট শুরু করার আগে সার্ভারটি চলমান থাকা প্রয়োজন।

  1. সার্ভারটি চালান:
$ ./build/install/start_here/bin/route-guide-server
  1. ক্লায়েন্ট চালান:
$ ./build/install/start_here/bin/route-guide-client

আপনি এইরকম আউটপুট দেখতে পাবেন, স্পষ্টতার জন্য টাইমস্ট্যাম্প বাদ দেওয়া হবে:

INFO: *** GetFeature: lat=409,146,138 lon=-746,188,906
INFO: Found feature called "Berkshire Valley Management Area Trail, Jefferson, NJ, USA" at 40.915, -74.619
INFO: *** GetFeature: lat=0 lon=0
INFO: Found no feature at 0, 0

৮. এরপর কী?