gRPC-Java का इस्तेमाल शुरू करना

1. परिचय

इस कोडलैब में, gRPC-Java का इस्तेमाल करके एक क्लाइंट और सर्वर बनाया जाएगा. ये दोनों, Java में लिखे गए रूट-मैपिंग ऐप्लिकेशन की बुनियादी संरचना तैयार करते हैं.

ट्यूटोरियल के आखिर तक, आपके पास एक ऐसा क्लाइंट होगा जो gRPC का इस्तेमाल करके, रिमोट सर्वर से कनेक्ट होता है. इससे आपको मैप पर किसी खास जगह के नाम या पते की जानकारी मिलती है. कोई ऐप्लिकेशन, इस क्लाइंट-सर्वर डिज़ाइन का इस्तेमाल करके किसी रास्ते पर मौजूद लोकप्रिय जगहों की सूची बना सकता है या उनके बारे में खास जानकारी दे सकता है.

सर्वर के एपीआई को प्रोटोकॉल बफ़र फ़ाइल में तय किया जाता है. इसका इस्तेमाल क्लाइंट और सर्वर के लिए बॉयलरप्लेट कोड जनरेट करने के लिए किया जाएगा, ताकि वे एक-दूसरे के साथ कम्यूनिकेट कर सकें. इससे आपको इस सुविधा को लागू करने में समय और मेहनत नहीं करनी पड़ेगी.

जनरेट किया गया यह कोड, सर्वर और क्लाइंट के बीच कम्यूनिकेशन की जटिलताओं के साथ-साथ डेटा के क्रमबद्ध और क्रम से हटाने की प्रोसेस को भी मैनेज करता है.

आपको क्या सीखने को मिलेगा

  • किसी सेवा के एपीआई को तय करने के लिए, प्रोटोकॉल बफ़र का इस्तेमाल कैसे करें.
  • ऑटोमेटेड कोड जनरेशन का इस्तेमाल करके, Protocol Buffers की परिभाषा से gRPC पर आधारित क्लाइंट और सर्वर बनाने का तरीका.
  • gRPC के साथ क्लाइंट-सर्वर कम्यूनिकेशन के बारे में जानकारी.

यह कोडलैब, Java डेवलपर के लिए है. इसमें gRPC का इस्तेमाल करने का तरीका बताया गया है. साथ ही, इसमें gRPC के बारे में जानकारी दी गई है. इसके अलावा, यह कोडलैब उन लोगों के लिए भी है जो डिस्ट्रिब्यूटेड सिस्टम बनाने में दिलचस्पी रखते हैं. इसके लिए, gRPC का अनुभव होना ज़रूरी नहीं है.

2. शुरू करने से पहले

ज़रूरी शर्तें

  • JDK का वर्शन 8 या इसके बाद का वर्शन

कोड प्राप्त करें

इसलिए, आपको शुरू से काम न करना पड़े, इस कोडलैब में ऐप्लिकेशन के सोर्स कोड का एक स्केफ़ोल्ड दिया गया है. आपको इसे पूरा करना होगा. यहां दिए गए तरीके से, ऐप्लिकेशन को पूरा करने का तरीका जानें. इसमें, बॉयलरप्लेट gRPC कोड जनरेट करने के लिए, प्रोटोकॉल बफ़र कंपाइलर प्लगिन का इस्तेमाल करने का तरीका भी शामिल है.

सबसे पहले, कोडलैब की वर्किंग डायरेक्ट्री बनाएं और उसमें cd करें:

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 पर उपलब्ध है.

3. सेवा के बारे में जानकारी देना

सबसे पहले, आपको प्रोटोकॉल बफ़र का इस्तेमाल करके, ऐप्लिकेशन की gRPC सेवा, उसके आरपीसी तरीके, और उसके अनुरोध और जवाब के मैसेज टाइप तय करने होंगे. आपकी सेवा से ये सुविधाएं मिलेंगी:

  • GetFeature नाम की एक आरपीसी विधि, जिसे सर्वर लागू करता है और क्लाइंट कॉल करता है.
  • Point और Feature मैसेज टाइप, डेटा स्ट्रक्चर होते हैं. GetFeature तरीके का इस्तेमाल करते समय, क्लाइंट और सर्वर के बीच इनका आदान-प्रदान होता है. क्लाइंट, सर्वर को GetFeature अनुरोध में मैप के कोऑर्डिनेट Point के तौर पर देता है. इसके जवाब में सर्वर, उन कोऑर्डिनेट पर मौजूद जानकारी के साथ Feature भेजता है.

इस आरपीसी तरीके और इसके मैसेज टाइप को, दिए गए सोर्स कोड की src/main/proto/routeguide/route_guide.proto फ़ाइल में तय किया जाएगा.

प्रोटोकॉल बफ़र को आम तौर पर, protobufs के नाम से जाना जाता है. gRPC की शब्दावली के बारे में ज़्यादा जानने के लिए, gRPC के मुख्य कॉन्सेप्ट, आर्किटेक्चर, और लाइफ़साइकल देखें.

इस उदाहरण में, हम Java कोड जनरेट कर रहे हैं. इसलिए, हमने अपनी .proto में java_package फ़ाइल का विकल्प और Java क्लास का नाम तय किया है:

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

1 और 2 नंबर, message स्ट्रक्चर में मौजूद हर फ़ील्ड के लिए यूनीक आईडी नंबर होते हैं.

इसके बाद, 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 फ़ाइल में service स्ट्रक्चर होता है, जिसका नाम RouteGuide होता है. यह ऐप्लिकेशन की सेवा के ज़रिए उपलब्ध कराए गए एक या उससे ज़्यादा तरीकों के बारे में बताता है.

RouteGuide की परिभाषा में rpc तरीका GetFeature जोड़ें. जैसा कि पहले बताया गया है, यह तरीका निर्देशांकों के दिए गए सेट से किसी जगह का नाम या पता ढूंढता है. इसलिए, दिए गए Point के लिए GetFeature को Feature वापस लाने के लिए कहें:

service RouteGuide {
  // Definition of the service goes here

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

यह एक यूनेरी आरपीसी तरीका है: एक सिंपल आरपीसी, जिसमें क्लाइंट सर्वर को अनुरोध भेजता है और जवाब मिलने का इंतज़ार करता है. यह लोकल फ़ंक्शन कॉल की तरह होता है.

4. क्लाइंट और सर्वर कोड जनरेट करना

इसके बाद, हमें अपनी .proto सेवा की परिभाषा से gRPC क्लाइंट और सर्वर इंटरफ़ेस जनरेट करने होंगे. हम ऐसा, खास gRPC Java प्लगिन के साथ प्रोटोकॉल बफ़र कंपाइलर protoc का इस्तेमाल करके करते हैं. gRPC सेवाएं जनरेट करने के लिए, आपको proto3 कंपाइलर का इस्तेमाल करना होगा. यह कंपाइलर, proto2 और proto3, दोनों सिंटैक्स के साथ काम करता है.

Gradle या Maven का इस्तेमाल करते समय, protoc बिल्ड प्लगिन, बिल्ड के हिस्से के तौर पर ज़रूरी कोड जनरेट कर सकता है. अपनी .proto फ़ाइलों से कोड जनरेट करने का तरीका जानने के लिए, grpc-java README पढ़ें.

हमने इस प्रोजेक्ट को बनाने के लिए, कोडलैब के सोर्स कोड में Gradle एनवायरमेंट और कॉन्फ़िगरेशन दिया है.

grpc-java-getting-started डायरेक्ट्री में, यह कमांड चलाएं:

$ chmod +x gradlew
$ ./gradlew generateProto

हमारी सेवा की परिभाषा से ये क्लास जनरेट की जाती हैं:

  • Feature.java, Point.java, और अन्य फ़ाइलें, जिनमें प्रोटोकॉल बफ़र का पूरा कोड होता है. इस कोड का इस्तेमाल, अनुरोध और जवाब के मैसेज टाइप को भरने, क्रम से लगाने, और वापस पाने के लिए किया जाता है.
  • RouteGuideGrpc.java में (कुछ अन्य काम के कोड के साथ) RouteGuide सर्वर के लिए एक बेसिक क्लास होती है, जिसे RouteGuideGrpc.RouteGuideImplBase लागू करते हैं. इसमें RouteGuide सेवा में तय किए गए सभी तरीके और क्लाइंट के इस्तेमाल के लिए स्टब क्लास शामिल होती हैं.

5. सर्वर को लागू करना

सबसे पहले, देखते हैं कि RouteGuide सर्वर कैसे बनाया जाता है. RouteGuide सेवा को काम करने के लिए, दो चीज़ें ज़रूरी होती हैं:

  • हमारी सेवा की परिभाषा से जनरेट किए गए सेवा इंटरफ़ेस को लागू करना. यह हमारी सेवा का असली "काम" करता है.
  • क्लाइंट से मिले अनुरोधों को सुनने और उन्हें सही सेवा लागू करने के लिए, gRPC सर्वर को चालू करना.

RouteGuide को लागू करना

जैसा कि आप देख सकते हैं, हमारे सर्वर में एक RouteGuideService क्लास है, जो जनरेट की गई RouteGuideGrpc.RouteGuideImplBase ऐब्स्ट्रैक्ट क्लास को बढ़ाती है:

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

हमने आपके सर्वर को सुविधाओं के साथ शुरू करने के लिए, ये दो फ़ाइलें उपलब्ध कराई हैं:

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

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

आइए, आरपीसी को लागू करने के आसान तरीके के बारे में ज़्यादा जानें.

यूनरी आरपीसी

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 रिस्पॉन्स ऑब्जेक्ट बनाते हैं और उसमें डेटा भरते हैं. यह हमारी सेवा की परिभाषा में बताया गया है. इस उदाहरण में, हम इसे अलग निजी checkFeature() तरीके से करते हैं.
  2. हम रिस्पॉन्स ऑब्ज़र्वर के onNext() तरीके का इस्तेमाल करके, Feature को वापस भेजते हैं.
  3. हम रिस्पॉन्स ऑब्ज़र्वर के 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();
  }

6. क्लाइंट बनाना

इस सेक्शन में, हम RouteGuide सेवा के लिए क्लाइंट बनाने का तरीका जानेंगे.

स्टब को इंस्टैंशिएट करना

सेवा के तरीकों को कॉल करने के लिए, हमें सबसे पहले एक स्टब बनाना होगा. स्टब दो तरह के होते हैं, लेकिन इस कोडलैब के लिए हमें सिर्फ़ ब्लॉक करने वाले स्टब का इस्तेमाल करना है. ये दो तरह के होते हैं:

  • ब्लॉकिंग/सिंक्रोनस स्टब, जो आरपीसी कॉल करता है और सर्वर से जवाब मिलने का इंतज़ार करता है. यह जवाब देगा या अपवाद बढ़ाएगा.
  • नॉन-ब्लॉकिंग/एसिंक्रोनस स्टब, जो सर्वर को नॉन-ब्लॉकिंग कॉल करता है. इसमें जवाब एसिंक्रोनस तरीके से मिलता है. एसिंक्रोनस स्टब का इस्तेमाल करके ही, कुछ तरह के स्ट्रीमिंग कॉल किए जा सकते हैं.

सबसे पहले, हमें एक gRPC चैनल बनाना होगा. इसके बाद, इस चैनल का इस्तेमाल करके हमें अपना स्टब बनाना होगा.

चैनल बनाने के लिए, सीधे तौर पर ManagedChannelBuilder का इस्तेमाल किया जा सकता था.

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

हालांकि, हम एक यूटिलिटी मेथड का इस्तेमाल करेंगे, जो hostname:port के साथ एक स्ट्रिंग लेता है.

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

अब हम चैनल का इस्तेमाल करके, ब्लॉकिंग स्टब बना सकते हैं. इस कोडलैब के लिए, हमारे पास सिर्फ़ आरपीसी को ब्लॉक करने का विकल्प है. इसलिए, हम RouteGuideGrpc क्लास में दिए गए newBlockingStub तरीके का इस्तेमाल करते हैं. इस क्लास को हमने अपने .proto से जनरेट किया है.

blockingStub = RouteGuideGrpc.newBlockingStub(channel);

कॉल सेवा के तरीके

अब देखते हैं कि हम अपनी सेवा के तरीकों को कैसे कॉल करते हैं.

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

बॉयलरप्लेट, एक मैसेज लॉग करता है. इसमें कॉन्टेंट शामिल होता है. यह इस बात पर निर्भर करता है कि तय किए गए पॉइंट पर कोई सुविधा मौजूद थी या नहीं.

7. इसे आज़माएं!

  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

8. आगे क्या करना है