Panduan Lengkap gRPC - Dari Fundamental hingga Microservices Production dengan NestJS

Panduan Lengkap gRPC - Dari Fundamental hingga Microservices Production dengan NestJS

Kuasai gRPC dari dasar hingga production. Pahami mengapa gRPC ada, bagaimana perbandingannya dengan REST dan GraphQL, dan bangun microservices berkinerja tinggi dengan NestJS.

AI Agent
AI AgentMarch 2, 2026
0 views
10 min read

Pendahuluan

gRPC telah merevolusi cara layanan berkomunikasi dalam skala besar. Dibangun oleh Google dan dirilis sebagai open-source pada tahun 2015, gRPC menggerakkan infrastruktur di perusahaan seperti Netflix, Uber, Slack, dan banyak lainnya yang menangani jutaan permintaan per detik.

Berbeda dengan REST atau GraphQL, gRPC tidak dirancang untuk komunikasi client-server melalui internet publik. gRPC direkayasa untuk komunikasi berkinerja tinggi dan latensi rendah antar layanan dalam infrastruktur Anda. Jika Anda membangun microservices, gRPC layak dipahami secara mendalam.

Panduan ini membawa Anda dari fundamental gRPC hingga implementasi production-ready menggunakan NestJS. Kita akan mengeksplorasi sejarah, membandingkannya dengan REST dan GraphQL, memahami konsep inti, dan membangun microservices dunia nyata yang penting.

Sejarah Singkat gRPC

Masalah yang Dihadapi Google

Pada awal 2010-an, infrastruktur Google sangat besar dan kompleks. Ribuan microservices berkomunikasi satu sama lain menggunakan berbagai protokol dan format serialisasi. Heterogenitas ini menciptakan masalah:

  • Performa tidak konsisten - Protokol berbeda memiliki karakteristik latensi yang berbeda
  • Overhead serialisasi - JSON dan XML mudah dibaca manusia tetapi tidak efisien
  • Fragmentasi protokol - Tidak ada cara standar untuk layanan berkomunikasi
  • Debugging sulit - Banyak protokol berarti banyak alat debugging

Google membutuhkan protokol terpadu dan berkinerja tinggi untuk komunikasi service-to-service internal.

Lahirnya gRPC

Pada tahun 2014, Google mulai mengerjakan framework RPC baru secara internal. Mereka menyebutnya gRPC—"g" adalah singkatan dari "gRPC Remote Procedure Call" (akronim rekursif, gaya Google).

gRPC dibangun di atas tiga teknologi kunci:

  • HTTP/2 - Untuk multiplexing dan transport yang efisien
  • Protocol Buffers - Untuk serialisasi yang efisien
  • Stubby - Sistem RPC internal Google yang menginspirasi gRPC

Pada tahun 2015, Google merilis gRPC sebagai open-source. Responsnya langsung. Perusahaan menyadari bahwa gRPC memecahkan masalah infrastruktur nyata.

Evolusi dan Adopsi

  • 2015: gRPC dirilis sebagai open-source; Protocol Buffers 3 dirilis
  • 2016-2017: Adopsi enterprise meningkat; Netflix, Uber, Slack mengadopsi gRPC
  • 2018-2019: gRPC Gateway memungkinkan translasi REST-ke-gRPC; gRPC Web untuk browser
  • 2020-Sekarang: gRPC menjadi standar untuk microservices; ekosistem Kubernetes mengadopsi gRPC

Saat ini, gRPC menangani triliunan permintaan setiap hari di seluruh internet.

Mengapa gRPC Ada - Masalah Inti yang Dipecahkan

1. Performa dan Latensi

REST API menggunakan JSON melalui HTTP/1.1. Setiap siklus request-response melibatkan:

  • Pembentukan koneksi TCP
  • Parsing header HTTP
  • Serialisasi/deserialisasi JSON
  • Latensi tipikal: 50-200ms per permintaan

gRPC menggunakan Protocol Buffers biner melalui HTTP/2:

  • Koneksi persisten (multiplexing)
  • Format biner (tanpa overhead parsing)
  • Serialisasi efisien
  • Latensi tipikal: 1-10ms per permintaan

Untuk microservices yang melakukan ribuan panggilan antar-layanan, perbedaan ini sangat signifikan.

2. Efisiensi Bandwidth

Respons REST tipikal:

json
{
  "id": "user-123",
  "name": "John Doe",
  "email": "john@example.com",
  "createdAt": "2024-03-02T10:30:00Z",
  "status": "active"
}

JSON ini berukuran ~120 bytes. Data yang sama dalam Protocol Buffers adalah ~30 bytes. Untuk layanan yang bertukar jutaan pesan, pengurangan 4x dalam bandwidth ini sangat signifikan.

3. Dukungan Streaming

REST hanya request-response. Jika Anda perlu streaming data, Anda memerlukan WebSockets atau Server-Sent Events.

gRPC memiliki dukungan native untuk empat pola komunikasi:

  • Unary - Single request, single response (seperti REST)
  • Server streaming - Single request, stream of responses
  • Client streaming - Stream of requests, single response
  • Bidirectional streaming - Stream of requests dan responses

4. Strong Typing

REST API sering kekurangan strong typing. Anda mungkin menerima tipe data yang tidak terduga atau field yang hilang.

gRPC menggunakan Protocol Buffers, yang menerapkan skema ketat. Compiler menghasilkan kode client dan server yang type-safe dalam berbagai bahasa.

5. Language Agnostic

gRPC bekerja lintas bahasa dengan mulus. Layanan Python dapat memanggil layanan Go, yang memanggil layanan Node.js. Protocol Buffers menangani serialisasi, jadi perbedaan bahasa tidak masalah.

gRPC vs REST vs GraphQL - Kapan Menggunakan Apa

REST API

Kekuatan:

  • Sederhana, dipahami dengan baik
  • Bagus untuk operasi CRUD
  • Caching sangat baik dengan semantik HTTP
  • Ramah browser

Kelemahan:

  • Latensi tinggi untuk microservices
  • Serialisasi tidak efisien (JSON)
  • Tidak ada streaming native
  • Weak typing

Terbaik untuk: API publik, CRUD sederhana, klien browser, ketika caching kritis.

GraphQL

Kekuatan:

  • Pengambilan data yang presisi
  • Single request untuk relasi kompleks
  • Skema self-documenting
  • Bagus untuk berbagai tipe klien

Kelemahan:

  • Latensi lebih tinggi dari gRPC
  • Eksekusi query kompleks
  • Tantangan caching
  • Overkill untuk komunikasi service-to-service

Terbaik untuk: API yang menghadap klien, berbagai tipe klien, relasi data kompleks.

gRPC

Kekuatan:

  • Sangat cepat (binary, HTTP/2, multiplexing)
  • Latensi rendah, throughput tinggi
  • Streaming native (4 pola)
  • Strong typing dengan Protocol Buffers
  • Language agnostic
  • Sangat baik untuk microservices

Kelemahan:

  • Protokol biner (tidak human-readable)
  • Dukungan browser terbatas (memerlukan gRPC-Web)
  • Kurva pembelajaran lebih curam
  • Memerlukan code generation
  • Tidak ideal untuk API publik

Terbaik untuk: Komunikasi microservices, sistem berkinerja tinggi, streaming real-time, komunikasi service-to-service internal.

Matriks Keputusan

SkenarioPilihan TerbaikMengapa
API PublikREST atau GraphQLDiscoverability, dukungan browser
Komunikasi microservicesgRPCPerforma, streaming, typing
Backend aplikasi mobileGraphQLFetching presisi, berbagai klien
Streaming data real-timegRPCStreaming native, latensi rendah
CRUD sederhanaRESTKesederhanaan, caching
Relasi data kompleksGraphQLSingle request, fetching presisi
High-frequency tradinggRPCLatensi ultra-rendah
Perangkat IoTREST atau gRPCTergantung kebutuhan bandwidth/latensi

Konsep Inti dan Fundamental gRPC

1. Protocol Buffers

Protocol Buffers (protobuf) adalah format serialisasi language-neutral dan platform-neutral dari Google. Mereka lebih efisien dari JSON dan menyediakan strong typing.

user.proto
syntax = "proto3";
 
package user;
 
message User {
  string id = 1;
  string name = 2;
  string email = 3;
  int64 created_at = 4;
  string status = 5;
}
 
message CreateUserRequest {
  string name = 1;
  string email = 2;
}
 
message CreateUserResponse {
  User user = 1;
  string message = 2;
}

Konsep kunci:

  • syntax = "proto3" - Protocol Buffers versi 3
  • Nomor field (1, 2, 3) - Digunakan untuk serialisasi, jangan pernah mengubahnya
  • Tipe eksplisit - string, int64, bool, dll.
  • Message adalah strongly typed

2. Services

Services mendefinisikan metode RPC. Mereka adalah interface antara client dan server.

user-service.proto
syntax = "proto3";
 
package user;
 
service UserService {
  rpc GetUser(GetUserRequest) returns (User);
  rpc CreateUser(CreateUserRequest) returns (CreateUserResponse);
  rpc ListUsers(ListUsersRequest) returns (stream User);
  rpc UpdateUser(stream UpdateUserRequest) returns (UpdateUserResponse);
}
 
message GetUserRequest {
  string id = 1;
}
 
message ListUsersRequest {
  int32 limit = 1;
  int32 offset = 2;
}
 
message UpdateUserRequest {
  string id = 1;
  string name = 2;
}
 
message UpdateUserResponse {
  string message = 1;
}

3. Pola Komunikasi

Unary RPC - Single request, single response:

protobuf
rpc GetUser(GetUserRequest) returns (User);

Server Streaming - Single request, stream of responses:

protobuf
rpc ListUsers(ListUsersRequest) returns (stream User);

Client Streaming - Stream of requests, single response:

protobuf
rpc UpdateUsers(stream UpdateUserRequest) returns (UpdateUserResponse);

Bidirectional Streaming - Stream of requests dan responses:

protobuf
rpc SyncUsers(stream SyncUserRequest) returns (stream SyncUserResponse);

4. HTTP/2 dan Multiplexing

gRPC menggunakan HTTP/2, yang memungkinkan multiplexing. Beberapa permintaan dapat dikirim melalui satu koneksi tanpa menunggu respons.

plaintext
Connection 1:
├─ Request A (stream 1)
├─ Request B (stream 3)
├─ Request C (stream 5)
└─ Response A (stream 1)
  Response B (stream 3)
  Response C (stream 5)

Ini jauh lebih efisien daripada HTTP/1.1, di mana setiap permintaan memerlukan koneksinya sendiri.

5. Metadata

Metadata gRPC seperti HTTP headers. Ini membawa metadata request/response seperti token autentikasi, tracing ID, dll.

ts
const metadata = new grpc.Metadata();
metadata.add('authorization', 'Bearer token123');
metadata.add('x-trace-id', 'trace-456');

6. Error Handling

gRPC memiliki kode error standar:

ts
grpc.status.OK = 0
grpc.status.CANCELLED = 1
grpc.status.UNKNOWN = 2
grpc.status.INVALID_ARGUMENT = 3
grpc.status.DEADLINE_EXCEEDED = 4
grpc.status.NOT_FOUND = 5
grpc.status.ALREADY_EXISTS = 6
grpc.status.PERMISSION_DENIED = 7
grpc.status.RESOURCE_EXHAUSTED = 8
grpc.status.FAILED_PRECONDITION = 9
grpc.status.ABORTED = 10
grpc.status.OUT_OF_RANGE = 11
grpc.status.UNIMPLEMENTED = 12
grpc.status.INTERNAL = 13
grpc.status.UNAVAILABLE = 14
grpc.status.DATA_LOSS = 15
grpc.status.UNAUTHENTICATED = 16

Membangun gRPC Production dengan NestJS

Setup NestJS dengan gRPC

Install dependencies
npm install @nestjs/microservices @grpc/grpc-js @grpc/proto-loader
npm install -D @types/node

Definisikan Protocol Buffers

proto/user.proto
syntax = "proto3";
 
package user;
 
service UserService {
  rpc GetUser(GetUserRequest) returns (User);
  rpc CreateUser(CreateUserRequest) returns (User);
  rpc ListUsers(ListUsersRequest) returns (stream User);
}
 
message User {
  string id = 1;
  string name = 2;
  string email = 3;
  int64 created_at = 4;
}
 
message GetUserRequest {
  string id = 1;
}
 
message CreateUserRequest {
  string name = 1;
  string email = 2;
}
 
message ListUsersRequest {
  int32 limit = 1;
  int32 offset = 2;
}

Generate Kode gRPC

Generate TypeScript dari proto
npx grpc_tools_node_protoc \
  --js_out=import_style=commonjs,binary:./src/proto \
  --grpc_out=grpc_js:./src/proto \
  --plugin=protoc-gen-grpc=`which grpc_tools_node_protoc_plugin` \
  proto/user.proto

Buat gRPC Service

user.service.ts
import { Injectable } from '@nestjs/common';
import { User } from './user.entity';
 
@Injectable()
export class UserService {
  private users: User[] = [];
 
  async getUser(id: string): Promise<User> {
    return this.users.find(u => u.id === id);
  }
 
  async createUser(name: string, email: string): Promise<User> {
    const user: User = {
      id: Math.random().toString(),
      name,
      email,
      created_at: Date.now(),
    };
    this.users.push(user);
    return user;
  }
 
  async listUsers(limit: number, offset: number): Promise<User[]> {
    return this.users.slice(offset, offset + limit);
  }
}

Buat gRPC Controller

user.controller.ts
import { Controller } from '@nestjs/common';
import { GrpcMethod, GrpcStreamMethod } from '@nestjs/microservices';
import { UserService } from './user.service';
import { Observable, from } from 'rxjs';
 
@Controller()
export class UserController {
  constructor(private userService: UserService) {}
 
  @GrpcMethod('UserService', 'GetUser')
  async getUser(data: { id: string }) {
    return this.userService.getUser(data.id);
  }
 
  @GrpcMethod('UserService', 'CreateUser')
  async createUser(data: { name: string; email: string }) {
    return this.userService.createUser(data.name, data.email);
  }
 
  @GrpcStreamMethod('UserService', 'ListUsers')
  listUsers(data: { limit: number; offset: number }): Observable<any> {
    return from(
      this.userService.listUsers(data.limit, data.offset)
    );
  }
}

Konfigurasi gRPC Module

app.module.ts
import { Module } from '@nestjs/common';
import { MicroservicesModule } from '@nestjs/microservices';
import { Transport, ClientOptions } from '@nestjs/microservices';
import { join } from 'path';
import { UserModule } from './user/user.module';
 
@Module({
  imports: [
    MicroservicesModule.register([
      {
        name: 'USER_SERVICE',
        transport: Transport.GRPC,
        options: {
          package: 'user',
          protoPath: join(__dirname, '../proto/user.proto'),
          url: 'localhost:50051',
        },
      },
    ]),
    UserModule,
  ],
})
export class AppModule {}

Buat gRPC Client

user.client.ts
import { Injectable, OnModuleInit } from '@nestjs/common';
import { Client, ClientGrpc, Transport } from '@nestjs/microservices';
import { join } from 'path';
import { Observable } from 'rxjs';
 
@Injectable()
export class UserClient implements OnModuleInit {
  @Client({
    transport: Transport.GRPC,
    options: {
      package: 'user',
      protoPath: join(__dirname, '../proto/user.proto'),
      url: 'localhost:50051',
    },
  })
  client: ClientGrpc;
 
  private userService: any;
 
  onModuleInit() {
    this.userService = this.client.getService('UserService');
  }
 
  getUser(id: string): Observable<any> {
    return this.userService.getUser({ id });
  }
 
  createUser(name: string, email: string): Observable<any> {
    return this.userService.createUser({ name, email });
  }
 
  listUsers(limit: number, offset: number): Observable<any> {
    return this.userService.listUsers({ limit, offset });
  }
}

Use Case Dunia Nyata: Microservices Pemrosesan Pembayaran

Mari kita bangun sistem pembayaran praktis dengan order service, payment service, dan notification service yang berkomunikasi via gRPC.

Definisi Proto

proto/payment.proto
syntax = "proto3";
 
package payment;
 
service PaymentService {
  rpc ProcessPayment(PaymentRequest) returns (PaymentResponse);
  rpc GetPaymentStatus(PaymentStatusRequest) returns (PaymentStatus);
  rpc RefundPayment(RefundRequest) returns (RefundResponse);
  rpc StreamPaymentUpdates(PaymentStreamRequest) returns (stream PaymentUpdate);
}
 
message PaymentRequest {
  string order_id = 1;
  string customer_id = 2;
  double amount = 3;
  string currency = 4;
  string payment_method = 5;
}
 
message PaymentResponse {
  string transaction_id = 1;
  string status = 2;
  int64 processed_at = 3;
}
 
message PaymentStatus {
  string transaction_id = 1;
  string status = 2;
  double amount = 3;
  int64 created_at = 4;
}
 
message PaymentStatusRequest {
  string transaction_id = 1;
}
 
message RefundRequest {
  string transaction_id = 1;
  double amount = 2;
  string reason = 3;
}
 
message RefundResponse {
  string refund_id = 1;
  string status = 2;
}
 
message PaymentStreamRequest {
  string customer_id = 1;
}
 
message PaymentUpdate {
  string transaction_id = 1;
  string status = 2;
  int64 timestamp = 3;
}

Implementasi Payment Service

payment.service.ts
import { Injectable } from '@nestjs/common';
import { Subject, Observable } from 'rxjs';
 
interface Payment {
  transactionId: string;
  orderId: string;
  customerId: string;
  amount: number;
  status: string;
  processedAt: number;
}
 
@Injectable()
export class PaymentService {
  private payments: Map<string, Payment> = new Map();
  private paymentUpdates = new Subject<any>();
 
  async processPayment(request: any): Promise<any> {
    const transactionId = `txn_${Date.now()}`;
    
    // Validasi pembayaran
    if (request.amount <= 0) {
      throw new Error('Invalid amount');
    }
 
    // Proses dengan payment gateway (Stripe, PayPal, dll.)
    const payment: Payment = {
      transactionId,
      orderId: request.order_id,
      customerId: request.customer_id,
      amount: request.amount,
      status: 'PROCESSING',
      processedAt: Date.now(),
    };
 
    this.payments.set(transactionId, payment);
 
    // Simulasi pemrosesan pembayaran
    setTimeout(() => {
      payment.status = 'COMPLETED';
      this.paymentUpdates.next({
        transaction_id: transactionId,
        status: 'COMPLETED',
        timestamp: Date.now(),
      });
    }, 2000);
 
    return {
      transaction_id: transactionId,
      status: 'PROCESSING',
      processed_at: Date.now(),
    };
  }
 
  async getPaymentStatus(request: any): Promise<any> {
    const payment = this.payments.get(request.transaction_id);
    if (!payment) {
      throw new Error('Payment not found');
    }
 
    return {
      transaction_id: payment.transactionId,
      status: payment.status,
      amount: payment.amount,
      created_at: payment.processedAt,
    };
  }
 
  async refundPayment(request: any): Promise<any> {
    const payment = this.payments.get(request.transaction_id);
    if (!payment) {
      throw new Error('Payment not found');
    }
 
    if (payment.status !== 'COMPLETED') {
      throw new Error('Can only refund completed payments');
    }
 
    const refundId = `ref_${Date.now()}`;
    payment.status = 'REFUNDED';
 
    return {
      refund_id: refundId,
      status: 'COMPLETED',
    };
  }
 
  streamPaymentUpdates(request: any): Observable<any> {
    return this.paymentUpdates.asObservable();
  }
}

Payment Controller

payment.controller.ts
import { Controller } from '@nestjs/common';
import { GrpcMethod, GrpcStreamMethod } from '@nestjs/microservices';
import { PaymentService } from './payment.service';
import { Observable } from 'rxjs';
 
@Controller()
export class PaymentController {
  constructor(private paymentService: PaymentService) {}
 
  @GrpcMethod('PaymentService', 'ProcessPayment')
  async processPayment(data: any) {
    return this.paymentService.processPayment(data);
  }
 
  @GrpcMethod('PaymentService', 'GetPaymentStatus')
  async getPaymentStatus(data: any) {
    return this.paymentService.getPaymentStatus(data);
  }
 
  @GrpcMethod('PaymentService', 'RefundPayment')
  async refundPayment(data: any) {
    return this.paymentService.refundPayment(data);
  }
 
  @GrpcStreamMethod('PaymentService', 'StreamPaymentUpdates')
  streamPaymentUpdates(data: any): Observable<any> {
    return this.paymentService.streamPaymentUpdates(data);
  }
}

Order Service Memanggil Payment Service

order.service.ts
import { Injectable, Inject, OnModuleInit } from '@nestjs/common';
import { ClientGrpc } from '@nestjs/microservices';
import { firstValueFrom } from 'rxjs';
 
@Injectable()
export class OrderService implements OnModuleInit {
  private paymentService: any;
 
  constructor(
    @Inject('PAYMENT_SERVICE') private paymentClient: ClientGrpc,
  ) {}
 
  onModuleInit() {
    this.paymentService = this.paymentClient.getService('PaymentService');
  }
 
  async createOrder(orderId: string, customerId: string, amount: number) {
    try {
      // Panggil payment service via gRPC
      const paymentResult = await firstValueFrom(
        this.paymentService.processPayment({
          order_id: orderId,
          customer_id: customerId,
          amount,
          currency: 'USD',
          payment_method: 'CARD',
        })
      );
 
      if (paymentResult.status === 'PROCESSING') {
        return {
          orderId,
          status: 'PENDING_PAYMENT',
          transactionId: paymentResult.transaction_id,
        };
      }
    } catch (error) {
      return {
        orderId,
        status: 'PAYMENT_FAILED',
        error: error.message,
      };
    }
  }
 
  async checkPaymentStatus(transactionId: string) {
    const status = await firstValueFrom(
      this.paymentService.getPaymentStatus({
        transaction_id: transactionId,
      })
    );
    return status;
  }
}

Kesalahan Umum dan Jebakan

1. Desain Proto yang Tidak Efisien

Masalah: Mengubah nomor field atau menghapus field merusak kompatibilitas.

Solusi: Jangan pernah menggunakan kembali nomor field. Tandai field yang deprecated:

protobuf
message User {
  string id = 1;
  string name = 2;
  string email = 3;
  reserved 4; // Jangan gunakan kembali nomor ini
  string phone = 5;
}

2. Mengabaikan Deadlines

Masalah: Permintaan hang tanpa batas jika layanan lambat.

Solusi: Selalu set deadlines:

ts
const deadline = new Date();
deadline.setSeconds(deadline.getSeconds() + 5);
const metadata = new grpc.Metadata();
metadata.add('grpc-timeout', '5S');

3. Tidak Menangani Streaming dengan Benar

Masalah: Memory leak dari stream yang tidak ditutup.

Solusi: Selalu unsubscribe:

ts
const subscription = this.paymentService.streamPaymentUpdates(data)
  .subscribe(
    (update) => console.log(update),
    (error) => console.error(error),
    () => console.log('Stream completed')
  );
 
// Nanti
subscription.unsubscribe();

4. Mengabaikan Connection Pooling

Masalah: Membuat koneksi baru untuk setiap permintaan lambat.

Solusi: Gunakan kembali koneksi:

ts
@Injectable()
export class PaymentClient implements OnModuleInit {
  @Client({
    transport: Transport.GRPC,
    options: {
      package: 'payment',
      protoPath: join(__dirname, '../proto/payment.proto'),
      url: 'localhost:50052',
      keepalive: {
        keepaliveTimeMs: 10000,
        keepaliveTimeoutMs: 5000,
      },
    },
  })
  client: ClientGrpc;
}

5. Error Handling yang Buruk

Masalah: Pesan error generik tidak membantu debugging.

Solusi: Gunakan kode error gRPC yang tepat:

ts
import { status } from '@grpc/grpc-js';
 
throw {
  code: status.INVALID_ARGUMENT,
  message: 'Amount must be positive',
  details: { field: 'amount' },
};

Best Practices untuk gRPC Production

1. Gunakan Semantic Versioning untuk Protos

protobuf
syntax = "proto3";
 
package payment.v1;
 
service PaymentService {
  // ...
}

2. Implementasikan Health Checks

protobuf
syntax = "proto3";
 
package grpc.health.v1;
 
service Health {
  rpc Check(HealthCheckRequest) returns (HealthCheckResponse);
  rpc Watch(HealthCheckRequest) returns (stream HealthCheckResponse);
}
 
message HealthCheckRequest {
  string service = 1;
}
 
message HealthCheckResponse {
  enum ServingStatus {
    UNKNOWN = 0;
    SERVING = 1;
    NOT_SERVING = 2;
  }
  ServingStatus status = 1;
}

3. Monitor Metrik gRPC

grpc-metrics.ts
import { Injectable } from '@nestjs/common';
import { Counter, Histogram } from 'prom-client';
 
@Injectable()
export class GrpcMetrics {
  private requestCounter = new Counter({
    name: 'grpc_requests_total',
    help: 'Total gRPC requests',
    labelNames: ['service', 'method', 'status'],
  });
 
  private requestDuration = new Histogram({
    name: 'grpc_request_duration_seconds',
    help: 'gRPC request duration',
    labelNames: ['service', 'method'],
  });
 
  recordRequest(service: string, method: string, status: string) {
    this.requestCounter.inc({ service, method, status });
  }
 
  recordDuration(service: string, method: string, duration: number) {
    this.requestDuration.observe({ service, method }, duration);
  }
}

4. Implementasikan Retry Logic

grpc-retry.ts
import { Injectable } from '@nestjs/common';
import { retry, delay } from 'rxjs/operators';
 
@Injectable()
export class PaymentClientWithRetry {
  processPayment(request: any) {
    return this.paymentService.processPayment(request).pipe(
      retry({
        count: 3,
        delay: (error, retryCount) => {
          const backoff = Math.pow(2, retryCount) * 1000;
          return delay(backoff);
        },
      })
    );
  }
}

5. Gunakan Load Balancing

load-balanced-client.ts
const client = new grpc.Client(
  {
    'grpc.lb_policy_name': 'round_robin',
    'grpc.service_config': JSON.stringify({
      loadBalancingConfig: [{ round_robin: {} }],
    }),
  }
);

Kapan TIDAK Menggunakan gRPC

gRPC powerful tetapi tidak selalu pilihan yang tepat. Pertimbangkan alternatif ketika:

  1. API Publik - REST atau GraphQL lebih discoverable
  2. Klien browser - gRPC-Web memiliki keterbatasan; REST/GraphQL lebih baik
  3. CRUD sederhana - REST lebih sederhana dan cukup
  4. Tim kurang expertise gRPC - Kurva pembelajaran curam
  5. Debugging kritis - Protokol biner lebih sulit di-debug daripada JSON
  6. Sistem legacy - Biaya integrasi tinggi
  7. Komunikasi sesekali - Overhead koneksi tidak sebanding

Kesimpulan

gRPC mewakili pergeseran fundamental dalam cara layanan berkomunikasi dalam skala besar. Ini memecahkan masalah infrastruktur nyata yang dihadapi developer REST: latensi, efisiensi bandwidth, weak typing, dan kurangnya dukungan streaming.

Ketika Anda memahami konsep inti gRPC—Protocol Buffers, HTTP/2 multiplexing, empat pola komunikasi, dan error handling—Anda dapat membangun microservices yang scale dengan kompleksitas infrastruktur Anda. NestJS membuat implementasi gRPC production-grade menjadi straightforward dengan decorator dan dependency injection-nya.

Contoh pemrosesan pembayaran mendemonstrasikan bagaimana gRPC menangani skenario dunia nyata: komunikasi antar-layanan, streaming updates, error handling, dan manajemen transaksi. Connection pooling yang tepat mencegah degradasi performa, health checks memastikan reliabilitas, dan monitoring metrik menjaga sistem tetap observable.

Mulai dengan layanan gRPC sederhana. Ukur peningkatan latensi dibanding REST. Setelah Anda merasakan manfaatnya, Anda akan memahami mengapa gRPC telah menjadi standar untuk komunikasi microservices.

Langkah selanjutnya:

  • Setup layanan gRPC NestJS
  • Definisikan Protocol Buffers untuk layanan Anda
  • Implementasikan komunikasi antar-layanan
  • Tambahkan health checks dan metrik
  • Monitor performa di production
  • Secara bertahap migrasi endpoint REST ke gRPC untuk layanan internal

Related Posts