کیوں جدید مائیکرو سروسز REST پر gRPC کو ترجیح دیتی ہیں۔

کیوں جدید مائیکرو سروسز REST پر gRPC کو ترجیح دیتی ہیں۔

یک سنگی فن تعمیر میں، اجزاء ان میموری میتھڈ کالز کے ذریعے بات چیت کرتے ہیں، جو کہ فوری اور انتہائی قابل اعتماد ہوتے ہیں۔ مائیکرو سروسز آرکیٹیکچر میں منتقل ہونے پر، تاہم، یہ اجزاء نیٹ ورک کی حدود سے الگ ہوتے ہیں۔ مواصلات ایک آؤٹ آف پراسیس نیٹ ورک کال بن جاتی ہے (انٹر پروسیس کمیونیکیشن، یا IPC)۔

برسوں سے، JSON پے لوڈز کے ساتھ HTTP/1.1 پر **REST (نمائندہ ریاست کی منتقلی) ویب APIs بنانے کے لیے پہلے سے طے شدہ معیار رہا ہے۔ اگرچہ REST عوام کا سامنا کرنے والی ویب سروسز اور کلائنٹ سے سرور کے تعامل کے لیے بہترین ہے، لیکن جب اعلی تعدد، کم تاخیر، اندرونی سروس سے سروس مواصلت کے لیے استعمال کیا جاتا ہے تو یہ اہم رکاوٹوں کو متعارف کراتی ہے۔

یہی وجہ ہے کہ جدید مائیکرو سروسز تیزی سے gRPC (Google Remote Procedure Call) کی طرف منتقل ہو رہی ہیں۔ اس مضمون میں، ہم REST کی حدود کا تجزیہ کریں گے، gRPC کے آرکیٹیکچرل ستونوں کو الگ کریں گے، اور Java میں gRPC سروس کے مکمل نفاذ کے ذریعے چلیں گے۔


1. مائیکرو سروسز میں REST کی رکاوٹیں۔

REST نے دو دہائیوں سے زیادہ عرصے سے ویب کو طاقتور بنایا ہے۔ تاہم، اس کی بنیادی ٹیکنالوجیز اندرونی تقسیم شدہ فن تعمیر کے لیے موزوں نہیں ہیں:

A. سادہ متن JSON کا اوور ہیڈ

JSON انسانی پڑھنے کے قابل ہے، جو ڈیبگنگ کو آسان بناتا ہے، لیکن یہ مشین سے مشین مواصلات کے لیے انتہائی غیر موثر ہے:

  • سیریلائزیشن/ڈی سیریلائزیشن لاگت: ٹیکسٹ سٹرنگ ٹوکنز کو پارس کرنے اور انہیں اشیاء میں تبدیل کرنے میں کافی CPU سائیکل خرچ ہوتے ہیں۔
  • بڑے پے لوڈ سائز: JSON کیز کو ہر ایک درخواست میں دہرایا جاتا ہے (جیسے، {"transactionId": "123", "amount": 99.99})۔ فی سیکنڈ لاکھوں درخواستوں پر کارروائی کرنے والے سسٹمز کے لیے، یہ بے کار میٹا ڈیٹا بہت زیادہ نیٹ ورک بینڈوتھ کو ضائع کرتا ہے۔

B. HTTP/1.1 کنکشن کی حدود

REST عام طور پر HTTP/1.1 پر چلتا ہے، جو کئی ساختی ناکاریوں کو ظاہر کرتا ہے:

  • ہیڈ آف لائن (HoL) بلاکنگ: ایک TCP کنکشن پر، ایک کلائنٹ کو اگلی درخواست بھیجنے سے پہلے موجودہ درخواست کے جواب کا انتظار کرنا ہوگا۔
  • کنکشن پولنگ تھکن: ایک ساتھ درخواستوں کو ہینڈل کرنے کے لیے، کلائنٹس کو متعدد TCP کنکشن کھولنے چاہئیں۔ اس کے نتیجے میں اہم OS ریسورس اوور ہیڈ، ساکٹ تھکن، اور سست رفتار کارکردگی کے جرمانے ہوتے ہیں کیونکہ کنکشن مسلسل کھلے اور بند ہوتے ہیں۔

C. کمزور API معاہدے

REST API میں بلٹ ان، کمپائل ٹائم کنٹریکٹ کی کمی ہے۔ جبکہ OpenAPI/Swagger دستاویز APIs میں مدد کرتا ہے، وہ کوڈبیس سے الگ ہیں۔ بیک اینڈ ڈویلپر کے لیے JSON فیلڈ کا نام تبدیل کرنا اور کمپائل ٹائم وارننگ کے بغیر حادثاتی طور پر ڈاؤن اسٹریم سروسز کو توڑنا آسان ہے۔


2. gRPC کے آرکیٹیکچرل ستون

2015 میں گوگل کے ذریعہ متعارف کرایا گیا، gRPC ایک اوپن سورس، اعلی کارکردگی کا RPC فریم ورک ہے جو خاص طور پر کلاؤڈ مقامی ایپلی کیشنز کے لیے ڈیزائن کیا گیا ہے۔ یہ تین اہم ٹیکنالوجیز کا استعمال کرتے ہوئے REST کی رکاوٹوں کو حل کرتا ہے:

gRPC کے 3 آرکیٹیکچرل ستون: پروٹوکول بفرز، HTTP/2 ٹرانسپورٹ، اور کوڈ جنریشن

A. پروٹوکول بفرز (Protobuf)

JSON کے بجائے، gRPC پروٹوکول بفرز کو اپنی انٹرفیس ڈیفینیشن لینگویج (IDL) اور میسج سیریلائزیشن فارمیٹ کے طور پر استعمال کرتا ہے۔

  • پروٹوبف ایک انتہائی بہتر بائنری سیریلائزیشن میکانزم ہے۔
  • فیلڈز کو متن کے ناموں کے بجائے چھوٹے عددی ٹیگ کے طور پر ترتیب دیا جاتا ہے۔
  • پے لوڈز JSON سے 70-80% چھوٹے تک ہیں، اور سیریلائزیشن 10x تیز تک ہے، جس سے CPU اور بینڈوتھ کے استعمال میں نمایاں کمی واقع ہوتی ہے۔

B. HTTP/2 ملٹی پلیکسنگ

gRPC HTTP/2 کو اپنے ٹرانسپورٹ پروٹوکول کے طور پر استعمال کرتا ہے، جس سے کارکردگی کی متعدد اصلاح ہوتی ہے:

  • سچ ملٹی پلیکسنگ: ایک ہی * طویل عرصے تک چلنے والے TCP کنکشن پر متعدد درخواستوں اور جوابات کو ایک ساتھ ملایا جا سکتا ہے، جس سے ہیڈ آف لائن بلاکنگ کو مکمل طور پر ختم کیا جا سکتا ہے۔
  • **ہیڈر کمپریشن (HPACK): HTTP ہیڈر کمپریسڈ ہوتے ہیں، درخواست کے سائز کو مزید کم کرتے ہیں۔
  • سٹریمنگ سپورٹ: HTTP/2 مقامی طور پر یون ڈائریکشنل کلائنٹ اسٹریمنگ، سرور اسٹریمنگ، اور مکمل دو طرفہ اسٹریمنگ کو سپورٹ کرتا ہے۔

C. کوڈ جنریشن اور سخت معاہدے

.proto فائل میں API ڈھانچہ کی وضاحت کرکے، gRPC درجنوں زبانوں (جاوا، گو، ازگر، C++، وغیرہ) میں کلائنٹ اسٹبس اور سرور بیس کلاسز تیار کرتا ہے۔ معاہدہ مرتب وقت محفوظ ہے۔ اگر سرور اپنے انٹرفیس کو اپ ڈیٹ کرتا ہے، کلائنٹ کی تعمیرات فوری طور پر ناکام ہوجاتی ہیں اگر وہ نئے معاہدے سے میل نہیں کھاتے ہیں۔


3. جاوا کا نفاذ: ایک gRPC سروس بنانا

آئیے ایک عملی جاوا gRPC ایپلی کیشن بنائیں۔ ہم ایک اعلی تھرو پٹ پیمنٹ پروسیسنگ سروس نافذ کریں گے جہاں ایک کلائنٹ ادائیگی کی درخواست جمع کرائے گا اور لین دین کی تصدیق حاصل کرے گا۔

مرحلہ 1: API معاہدے کی وضاحت کریں (payment.proto)

ہم پروٹوکول بفر فائل میں اپنے سروس انٹرفیس اور پیغام کے ڈھانچے کی وضاحت کرتے ہوئے شروع کرتے ہیں۔

syntax = "proto3";

option java_multiple_files = true;
option java_package = "com.ghaznix.grpc.payment";
option java_outer_classname = "PaymentProto";

package payment;

// The Payment Service definition
service PaymentService {
  // Unary RPC: Sends a single payment request and receives a confirmation
  rpc ProcessPayment (PaymentRequest) returns (PaymentResponse);
}

// The Payment Request message
message PaymentRequest {
  string transaction_id = 1;
  string customer_id = 2;
  double amount = 3;
  string currency = 4;
}

// The Payment Response message
message PaymentResponse {
  string transaction_id = 1;
  string status = 2;
  string message = 3;
  string timestamp = 4;
}

مرحلہ 2: سرور سائیڈ منطق کو نافذ کریں۔

.proto فائل کو مرتب کرنے کے بعد (عام طور پر Maven یا Gradle gRPC پلگ ان کے ذریعہ خود بخود ہینڈل کیا جاتا ہے)، ٹول PaymentServiceImplBase کلاس تیار کرتا ہے۔ ہم کاروباری منطق کو نافذ کرنے کے لیے اس کلاس کو بڑھاتے ہیں۔

package com.ghaznix.grpc.payment;

import io.grpc.stub.StreamObserver;
import java.time.Instant;

public class PaymentServiceImpl extends PaymentServiceGrpc.PaymentServiceImplBase {

    @Override
    public void processPayment(PaymentRequest request, StreamObserver<PaymentResponse> responseObserver) {
        System.out.println("Received payment request for customer: " + request.getCustomerId() 
                + " with amount: " + request.getAmount() + " " + request.getCurrency());

        // Process payment logic (simulation)
        String status = request.getAmount() > 0 ? "SUCCESS" : "DECLINED";
        String message = status.equals("SUCCESS") ? "Payment processed successfully." : "Invalid payment amount.";

        PaymentResponse response = PaymentResponse.newBuilder()
                .setTransactionId(request.getTransactionId())
                .setStatus(status)
                .setMessage(message)
                .setTimestamp(Instant.now().toString())
                .build();

        // Send the response to the client
        responseObserver.onNext(response);
        
        // Signal that the RPC execution is complete
        responseObserver.onCompleted();
    }
}

مرحلہ 3: جی آر پی سی سرور کو بوٹسٹریپ کریں۔

اس کے بعد، ہم ایک مخصوص پورٹ پر gRPC سرور شروع کرنے اور اپنی سروس کے نفاذ کو رجسٹر کرنے کے لیے ایک سرور کلاس بناتے ہیں۔

package com.ghaznix.grpc.payment;

import io.grpc.Server;
import io.grpc.ServerBuilder;
import java.io.IOException;
import java.util.concurrent.TimeUnit;

public class PaymentServer {

    private Server server;

    public void start() throws IOException {
        int port = 50051;
        server = ServerBuilder.forPort(port)
                .addService(new PaymentServiceImpl())
                .build()
                .start();
        
        System.out.println("gRPC Server started, listening on port " + port);

        Runtime.getRuntime().addShutdownHook(new Thread(() -> {
            System.err.println("*** Shutting down gRPC server since JVM is shutting down");
            try {
                PaymentServer.this.stop();
            } catch (InterruptedException e) {
                e.printStackTrace(System.err);
            }
            System.err.println("*** Server shut down");
        }));
    }

    public void stop() throws InterruptedException {
        if (server != null) {
            server.shutdown().awaitTermination(30, TimeUnit.SECONDS);
        }
    }

    public void blockUntilShutdown() throws InterruptedException {
        if (server != null) {
            server.awaitTermination();
        }
    }

    public static void main(String[] args) throws IOException, InterruptedException {
        final PaymentServer server = new PaymentServer();
        server.start();
        server.blockUntilShutdown();
    }
}

مرحلہ 4: کلائنٹ کو لاگو کریں (بلاکنگ اور Async Stubs)

gRPC کا ایک بڑا فائدہ یہ ہے کہ یہ کلائنٹ کے لیے دو قسم کے اسٹب تیار کرتا ہے:

  1. BlockingStub: ہم وقت ساز، بلاکنگ ایگزیکیوشن (معیاری REST کالز کی طرح)۔
  2. Stub: کال بیک مبصرین کا استعمال کرتے ہوئے غیر مطابقت پذیر، غیر مسدود عمل۔
package com.ghaznix.grpc.payment;

import io.grpc.ManagedChannel;
import io.grpc.ManagedChannelBuilder;
import java.util.concurrent.TimeUnit;

public class PaymentClient {

    private final ManagedChannel channel;
    private final PaymentServiceGrpc.PaymentServiceBlockingStub blockingStub;

    public PaymentClient(String host, int port) {
        // Create a communication channel to the server
        this.channel = ManagedChannelBuilder.forAddress(host, port)
                .usePlaintext() // Disables TLS for local development
                .build();
        
        // Create the synchronous blocking stub
        this.blockingStub = PaymentServiceGrpc.newBlockingStub(channel);
    }

    public void shutdown() throws InterruptedException {
        channel.shutdown().awaitTermination(5, TimeUnit.SECONDS);
    }

    public void executePayment(String txId, String customerId, double amount) {
        PaymentRequest request = PaymentRequest.newBuilder()
                .setTransactionId(txId)
                .setCustomerId(customerId)
                .setAmount(amount)
                .setCurrency("USD")
                .build();

        System.out.println("Sending payment request for: " + txId);
        
        // Execute unary synchronous call
        PaymentResponse response = blockingStub.processPayment(request);
        
        System.out.println("Server Response: " + response.getStatus() + " | " + response.getMessage());
    }

    public static void main(String[] args) throws InterruptedException {
        PaymentClient client = new PaymentClient("localhost", 50051);
        try {
            client.executePayment("TX-99081", "customer-abc-123", 250.75);
        } finally {
            client.shutdown();
        }
    }
}

4. تعمیراتی موازنہ: gRPC بمقابلہ REST

نیچے دی گئی جدول دونوں تمثیلوں کی بنیادی خصوصیات کو بیان کرتی ہے:

آرکیٹیکچرل فیچر REST (نمائندہ ریاست کی منتقلی) gRPC (گوگل ریموٹ پروسیجر کال)
پروٹوکول HTTP/1.1 (معیاری)، HTTP/2 (اختیاری) HTTP/2 (سخت ضرورت)
پے لوڈ فارمیٹ سادہ متن JSON, XML, HTML بائنری پروٹوکول بفرز (پروٹوبف)
API پیراڈائم وسائل (URI راستے + GET/POST/PUT/DELETE) ریموٹ طریقہ کار (طریقے/فنکشنز کالز)
معاہدے کا معیار ڈھیلا (OpenAPI/Swagger اختیاری/بیرونی ہے) سخت (تالیف میں .proto فائلوں میں بیان کردہ)
ملٹی پلیکسنگ نہیں (HTTP/1.1 میں ہیڈ آف لائن بلاکنگ) ہاں (ایک TCP کنکشن پر ایک سے زیادہ سلسلے)
سٹریمنگ کی اقسام یون ڈائریکشنل سرور سے بھیجے گئے ایونٹس (SSE) صرف کلائنٹ، سرور، اور دو طرفہ سلسلہ بندی
کوڈ جنریشن بیرونی ٹولز درکار ہیں (جیسے، سویگر کوڈجن) بلٹ ان بذریعہ پروٹوبف کمپائلر (protoc)
براؤزر سپورٹ یونیورسل (براہ راست ویب کلائنٹ تک رسائی) محدود (grpc-web پراکسی ترجمہ درکار ہے)

5. REST پر gRPC کب منتخب کریں؟

gRPC کے بڑے پیمانے پر کارکردگی کے فوائد کے باوجود، یہ سلور بلٹ نہیں ہے۔ پروٹوکول کا انتخاب آپ کے سسٹم کے فن تعمیر کے سیاق و سباق پر منحصر ہے:

  • اندرونی مائیکرو سرویس میش کے لیے gRPC کا استعمال کریں: اعلیٰ والیوم سروس ٹو سروس کمیونیکیشن کے لیے، gRPC کی کم تاخیر، بائنری کمپیکٹنس، اور اسٹریمنگ کی صلاحیتیں اسے بہترین انتخاب بناتی ہیں۔ یہ اندرونی سرور CPU اوور ہیڈ کو کم کرتا ہے اور مجموعی ردعمل کے اوقات کو تیز کرتا ہے۔
  • Edge/Public APIs کے لیے REST کا استعمال کریں: چونکہ ویب براؤزرز ترجمے کی تہوں کے بغیر مقامی طور پر gRPC کال نہیں کرسکتے ہیں، اس لیے REST عوامی APIs، فریق ثالث ویب ہکس کے ساتھ انضمام، اور کلائنٹ براؤزرز سے آپ کے فن تعمیر کے کنارے تک براہ راست مواصلت کے لیے بہترین انتخاب ہے۔

دونوں کو ملا کر — API گیٹ وے کی سطح پر REST کی نمائش اور اندرونی مائیکرو سرویس کمیونیکیشن کے لیے gRPC کا فائدہ اٹھا کر — معمار ایسے نظام بنا سکتے ہیں جو انتہائی قابل عمل اور تیز رفتار ہوں۔


غزنکس بلاگ پر مزید سافٹ ویئر ڈویلپمنٹ اور بیک اینڈ انجینئرنگ کی بصیرتیں دریافت کریں →