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

1. परिचय

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

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

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

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

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

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

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

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

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

पक्का करें कि आपने ये इंस्टॉल किए हों:

  • GCC. यहां दिए गए निर्देशों का पालन करें.
  • Git: इंस्टॉल करने के निर्देश यहां दिए गए हैं.
  • रस्ट, संस्करण 1.88.0. स्थापना निर्देशों का पालन करें यहाँ.

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

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

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

mkdir grpc-rust-getting-started && cd grpc-rust-getting-started

कोडलाब डाउनलोड करें और एक्सट्रैक्ट करें:

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

वैकल्पिक रूप से, आप केवल कोडलाब डायरेक्टरी वाली .zip फ़ाइल डाउनलोड कर सकते हैं और उसे मैन्युअल रूप से अनज़िप कर सकते हैं.

पूर्ण स्रोत कोड यह हैGitHub पर उपलब्ध है यदि आप कार्यान्वयन में टाइपिंग को छोड़ना चाहते हैं.

3. सेवा को परिभाषित करें

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

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

यह आरपीसी विधि और इसके संदेश प्रकार सभी प्रदान किए गए स्रोत कोड की proto/routeguide.proto फ़ाइल में परिभाषित किए जाएंगे.

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

सेवा विधि

सबसे पहले, हम अपनी सेवा के तरीकों के बारे में बताएंगे. इसके बाद, हम अपने मैसेज टाइप Point और Feature के बारे में बताएंगे. proto/routeguide.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) {}
}

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

मैसेज के टाइप

सोर्स कोड की proto/routeguide.proto फ़ाइल में, सबसे पहले Point मैसेज टाइप तय करें. Point, मैप पर अक्षांश-देशांतर के निर्देशांकों के जोड़े को दिखाता है. इस कोडलैब के लिए, निर्देशांकों के तौर पर पूर्णांकों का इस्तेमाल करें:

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

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

इसके बाद, Feature मैसेज टाइप तय करें. Feature, string फ़ील्ड का इस्तेमाल करता है. इससे Point में बताई गई जगह पर मौजूद किसी चीज़ का नाम या डाक पता पता चलता है:

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

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

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

हमने आपको generated/ डायरेक्ट्री में मौजूद .proto फ़ाइल से जनरेट किया गया कोड पहले ही दे दिया है. इसमें वे सभी बदलाव शामिल हैं जो आपने ऊपर किए हैं. हालांकि, हम आपको यह बताना चाहते हैं कि कोड जनरेट करने की सुविधा कैसे काम करती है.

हमारी .proto फ़ाइल में, क्लाइंट या सर्वर के इस्तेमाल किए जाने वाले सभी स्ट्रक्चर और फ़ंक्शन के बारे में बताया गया है. हम इस कोड को अपने-आप जनरेट करने के लिए, grpc-protobuf-build क्रेट के साथ-साथ Cargo की बिल्ड स्क्रिप्ट (build.rs) का इस्तेमाल करते हैं.

Cargo.toml में, हम [build-dependencies] के तहत grpc-protobuf-build जोड़ते हैं.

build.rs में, हम grpc_protobuf_build::CodeGen को कॉन्फ़िगर करते हैं, ताकि proto/routeguide.proto को generated/ डायरेक्ट्री में कंपाइल किया जा सके. मुख्य लाइनें यहां दी गई हैं:

grpc_protobuf_build::CodeGen::new()
    .include("proto")
    .input("routeguide.proto")
    .output_dir("generated")
    .compile()
    .unwrap();

इससे grpc_protobuf_build क्रेट के कोड जनरेशन को कॉल किया जाता है और routeguide.proto को पास किया जाता है. हमने इसे कुछ कोड में रैप किया है, ताकि यह सिर्फ़ तब चले, जब कोई फ़ीचर फ़्लैग पास किया गया हो. इससे यह सिर्फ़ तब फिर से जनरेट होगा, जब आपको इसकी ज़रूरत होगी. आपको इसे अभी चलाने की ज़रूरत नहीं है, क्योंकि हमने आपके लिए कोड पहले ही जनरेट कर दिया है.

cargo build --bin routeguide-server --features regenerate_proto

cargo build कमांड चलाने पर, build.rs प्रोटोकॉल बफ़र की परिभाषाओं को generate/ डायरेक्ट्री में कंपाइल करता है. इसमें ये शामिल हैं:

  • मैसेज टाइप Point और Feature के लिए स्ट्रक्ट की परिभाषाएं.
  • Tonic की एक सेवा की विशेषता, जिसे हमें सर्वर के लिए लागू करना होगा: route_guide_server::RouteGuide.
  • यह gRPC-Rust क्लाइंट टाइप है, जिसका इस्तेमाल हम सर्वर को कॉल करने के लिए करेंगे: route_guide_client::RouteGuideClient<T>.

ज़्यादा जानकारी के लिए, protoc-gen-rust-grpc गाइड देखें.

इसके बाद, हम सर्वर पर सेवा के तरीकों को लागू करेंगे.

5. सेवा लागू करना

src/server/server.rs में, जनरेट किए गए कोड को gRPC के include_generated_proto! मैक्रो के ज़रिए स्कोप में लाया जा सकता है. साथ ही, RouteGuide ट्रेट और Point को इंपोर्ट किया जा सकता है.

mod grpc_pb {
    grpc::include_generated_proto!("generated", "routeguide");
}

use grpc_pb::{
    route_guide_server::{RouteGuideServer, RouteGuide},
    Point, Feature,
};

हम अपनी सेवा को दिखाने के लिए, एक स्ट्रक्चर तय करके शुरुआत कर सकते हैं. फ़िलहाल, हम इसे src/server/server.rs पर कर सकते हैं:

#[derive(Debug)]
pub struct RouteGuideService {
    features: Vec<Feature>,
}

अब हमें जनरेट किए गए कोड से route_guide_server::RouteGuide ट्रेट को लागू करना होगा.

सिंपल यूनरी आरपीसी

RouteGuideService, हमारी सभी सेवा के तरीकों को लागू करता है. सर्वर साइड पर मौजूद get_feature फ़ंक्शन, मुख्य काम करता है: यह क्लाइंट से Point मैसेज लेता है और Feature मैसेज में, जानी-पहचानी जगहों की सूची से जगह की जानकारी दिखाता है. src/server/server.rs में फ़ंक्शन को लागू करने का तरीका यहां बताया गया है:

#[tonic::async_trait]
impl RouteGuide for RouteGuideService {
    async fn get_feature(&self, request: Request<Point>) -> Result<Response<Feature>, Status> {
        println!("GetFeature = {:?}", request);
        let requested_point = request.get_ref();
        for feature in self.features.iter() {
            if feature.location().latitude() == requested_point.latitude() {
                if feature.location().longitude() == requested_point.longitude(){
                    return Ok(Response::new(feature.clone()))
                };
            };    
        }
        Ok(Response::new(Feature::default()))
    }
}

इस तरीके को लागू करने के बाद, हमें एक gRPC सर्वर भी शुरू करना होगा, ताकि क्लाइंट हमारी सेवा का इस्तेमाल कर सकें. main() को इससे बदलें.

#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
    let addr = "[::1]:10000".parse().unwrap();
    println!("RouteGuideServer listening on: {addr}");
    let route_guide = RouteGuideService {
        features: load(),
    };
    let svc = RouteGuideServer::new(route_guide);
    Server::builder().add_service(svc).serve(addr).await?;
    Ok(())
}

यहां main() में होने वाली प्रोसेस के बारे में सिलसिलेवार तरीके से बताया गया है:

  1. वह पोर्ट बताएं जिसका इस्तेमाल हमें क्लाइंट के अनुरोधों को सुनने के लिए करना है
  2. सहायक फ़ंक्शन load() को कॉल करके सुविधाओं को लोड करके RouteGuideService बनाएं.
  3. हमारे द्वारा बनाई गई सेवा का उपयोग करके RouteGuideServer::new() का उपयोग करके gRPC सर्वर का एक इंस्टेंस बनाएं.
  4. हमारी सेवा कार्यान्वयन को जीआरपीसी सर्वर के साथ पंजीकृत करें.
  5. प्रक्रिया समाप्त होने तक अवरुद्ध प्रतीक्षा करने के लिए हमारे पोर्ट विवरण के साथ सर्वर पर serve() को कॉल करें.

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

इस अनुभाग में, हम src/client/client.rs में हमारी RouteGuide सेवा के लिए एक Rust क्लाइंट बनाने पर विचार करेंगे.

जैसा कि हमने src/server/server.rs में किया था, हम gRPC के include_generated_proto! मैक्रो के माध्यम से जनरेटेड कोड को स्कोप में ला सकते हैं और RouteGuideClient प्रकार को इम्पोर्ट कर सकते हैं.

mod grpc_pb {
    grpc::include_generated_proto!("generated", "routeguide");
}

use grpc_pb::{
    route_guide_client::RouteGuideClient,
    Point,
};

कॉल सेवा विधियाँ

gRPC-Rust में, RPC अतुल्यकालिक और गैर-अवरोधक होते हैं, जो सर्वर से प्रतिक्रियाओं की प्रतीक्षा करने के लिए Rust के async/await सिंटैक्स का उपयोग करते हैं.

सेवा विधियों को कॉल करने के लिए, हम सबसे पहले Channel::builder() का उपयोग करके Channel बनाते हैं, जिसमें सर्वर पता (dns:///[::1]:10000) और कनेक्शन क्रेडेंशियल (LocalChannelCredentials) निर्दिष्ट करते हैं. फिर हम अपने क्लाइंट को इंस्टैंशिएट करने के लिए चैनल को RouteGuideClient::new() में पास करते हैं:

#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
    // Create a new gRPC channel
    let channel = Channel::builder(
        "dns:///[::1]:10000",
        Arc::new(LocalChannelCredentials::new()),
    )
    .build();

    // Create a new client
    let client = RouteGuideClient::new(channel);



}

इस फ़ंक्शन में, RouteGuideClient::new() जेनेरिक चैनल को जनरेटेड क्लाइंट स्टब से जोड़ता है जो हमारी .proto सेवा परिभाषा में परिभाषित विधियों को लागू करता है.

सरल आरपीसी

सरल RPC GetFeature को कॉल करना उतना ही सीधा है जितना कि एक स्थानीय विधि को कॉल करना. इसे main() में जोड़ें:

println!("*** SIMPLE RPC ***");
let point = proto!(Point {
    latitude: 409_146_138,
    longitude: -746_188_906,
});
let response = client
    .get_feature(point)
    .await
    .expect("RPC error");

gRPC-Rust में, हम प्रोटोबफ़ संदेश Point को सीधे client.get_feature(point) पर भेजते हैं. रिटर्न किए गए फ़्यूचर की प्रतीक्षा करने से आगे किसी मेथड कॉल की आवश्यकता के बिना सीधे Feature प्रतिक्रिया प्राप्त होती है.

इसके बाद, प्रतिक्रिया से प्राप्त फ़ील्ड्स को प्रिंट करें:

println!(
    "Response = Name = \"{}\", Latitude = {}, Longitude = {}",
    response.name(),
    response.location().latitude(),
    response.location().longitude()
);

कुल मिलाकर, क्लाइंट का main() फ़ंक्शन इस प्रकार दिखना चाहिए:

#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
    // 1. Create a new gRPC channel
    let channel = Channel::builder(
        "dns:///[::1]:10000",
        Arc::new(LocalChannelCredentials::new()),
    )
    .build();

    // 2. Instantiate the generated RouteGuideClient
    let client = RouteGuideClient::new(channel);

    println!("*** SIMPLE RPC ***");
    let point = proto!(Point {
        latitude: 409_146_138,
        longitude: -746_188_906,
    });
    let response = client
        .get_feature(point)
        .await
        .expect("RPC error");

    println!(
        "Response = Name = \"{}\", Latitude = {}, Longitude = {}",
        response.name(),
        response.location().latitude(),
        response.location().longitude()
    );
    Ok(())
}

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

अपने क्लाइंट और सर्वर को चलाने के लिए, सबसे पहले यह सत्यापित करें कि दोनों बाइनरी लक्ष्य Cargo.toml में परिभाषित हैं:

[[bin]]
name = "routeguide-server"
path = "src/server/server.rs"

[[bin]]
name = "routeguide-client"
path = "src/client/client.rs"

फिर, हमारी वर्किंग डायरेक्टरी से निम्नलिखित कमांड्स को निष्पादित करें:

  1. एक ही टर्मिनल में सर्वर चलाएं:
cargo run --bin routeguide-server
  1. किसी अन्य टर्मिनल से क्लाइंट चलाएँ:
cargo run --bin routeguide-client

आपको इस तरह का आउटपुट दिखेगा. इसमें टाइमस्टैंप को हटा दिया गया है, ताकि जानकारी साफ़ तौर पर दिख सके:

*** SIMPLE RPC ***
Response = Name = "Berkshire Valley Management Area Trail, Jefferson, NJ, USA", Latitude = 409146138, Longitude = -746188906

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

9. इस कोडलाब में योगदान देने वाले

  • कैथी झाओ
  • लुसियो फ़्रैंको
  • अरविंद ब्राइट
  • नैथानियल फ़ोर्ड