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

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.
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:
Google membutuhkan protokol terpadu dan berkinerja tinggi untuk komunikasi service-to-service internal.
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:
Pada tahun 2015, Google merilis gRPC sebagai open-source. Responsnya langsung. Perusahaan menyadari bahwa gRPC memecahkan masalah infrastruktur nyata.
Saat ini, gRPC menangani triliunan permintaan setiap hari di seluruh internet.
REST API menggunakan JSON melalui HTTP/1.1. Setiap siklus request-response melibatkan:
gRPC menggunakan Protocol Buffers biner melalui HTTP/2:
Untuk microservices yang melakukan ribuan panggilan antar-layanan, perbedaan ini sangat signifikan.
Respons REST tipikal:
{
"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.
REST hanya request-response. Jika Anda perlu streaming data, Anda memerlukan WebSockets atau Server-Sent Events.
gRPC memiliki dukungan native untuk empat pola komunikasi:
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.
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.
Kekuatan:
Kelemahan:
Terbaik untuk: API publik, CRUD sederhana, klien browser, ketika caching kritis.
Kekuatan:
Kelemahan:
Terbaik untuk: API yang menghadap klien, berbagai tipe klien, relasi data kompleks.
Kekuatan:
Kelemahan:
Terbaik untuk: Komunikasi microservices, sistem berkinerja tinggi, streaming real-time, komunikasi service-to-service internal.
| Skenario | Pilihan Terbaik | Mengapa |
|---|---|---|
| API Publik | REST atau GraphQL | Discoverability, dukungan browser |
| Komunikasi microservices | gRPC | Performa, streaming, typing |
| Backend aplikasi mobile | GraphQL | Fetching presisi, berbagai klien |
| Streaming data real-time | gRPC | Streaming native, latensi rendah |
| CRUD sederhana | REST | Kesederhanaan, caching |
| Relasi data kompleks | GraphQL | Single request, fetching presisi |
| High-frequency trading | gRPC | Latensi ultra-rendah |
| Perangkat IoT | REST atau gRPC | Tergantung kebutuhan bandwidth/latensi |
Protocol Buffers (protobuf) adalah format serialisasi language-neutral dan platform-neutral dari Google. Mereka lebih efisien dari JSON dan menyediakan strong typing.
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 3string, int64, bool, dll.Services mendefinisikan metode RPC. Mereka adalah interface antara client dan server.
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;
}Unary RPC - Single request, single response:
rpc GetUser(GetUserRequest) returns (User);Server Streaming - Single request, stream of responses:
rpc ListUsers(ListUsersRequest) returns (stream User);Client Streaming - Stream of requests, single response:
rpc UpdateUsers(stream UpdateUserRequest) returns (UpdateUserResponse);Bidirectional Streaming - Stream of requests dan responses:
rpc SyncUsers(stream SyncUserRequest) returns (stream SyncUserResponse);gRPC menggunakan HTTP/2, yang memungkinkan multiplexing. Beberapa permintaan dapat dikirim melalui satu koneksi tanpa menunggu respons.
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.
Metadata gRPC seperti HTTP headers. Ini membawa metadata request/response seperti token autentikasi, tracing ID, dll.
const metadata = new grpc.Metadata();
metadata.add('authorization', 'Bearer token123');
metadata.add('x-trace-id', 'trace-456');gRPC memiliki kode error standar:
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 = 16npm install @nestjs/microservices @grpc/grpc-js @grpc/proto-loader
npm install -D @types/nodesyntax = "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;
}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.protoimport { 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);
}
}Mari kita bangun sistem pembayaran praktis dengan order service, payment service, dan notification service yang berkomunikasi via gRPC.
Masalah: Mengubah nomor field atau menghapus field merusak kompatibilitas.
Solusi: Jangan pernah menggunakan kembali nomor field. Tandai field yang deprecated:
message User {
string id = 1;
string name = 2;
string email = 3;
reserved 4; // Jangan gunakan kembali nomor ini
string phone = 5;
}Masalah: Permintaan hang tanpa batas jika layanan lambat.
Solusi: Selalu set deadlines:
const deadline = new Date();
deadline.setSeconds(deadline.getSeconds() + 5);
const metadata = new grpc.Metadata();
metadata.add('grpc-timeout', '5S');Masalah: Memory leak dari stream yang tidak ditutup.
Solusi: Selalu unsubscribe:
const subscription = this.paymentService.streamPaymentUpdates(data)
.subscribe(
(update) => console.log(update),
(error) => console.error(error),
() => console.log('Stream completed')
);
// Nanti
subscription.unsubscribe();Masalah: Membuat koneksi baru untuk setiap permintaan lambat.
Solusi: Gunakan kembali koneksi:
@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;
}Masalah: Pesan error generik tidak membantu debugging.
Solusi: Gunakan kode error gRPC yang tepat:
import { status } from '@grpc/grpc-js';
throw {
code: status.INVALID_ARGUMENT,
message: 'Amount must be positive',
details: { field: 'amount' },
};syntax = "proto3";
package payment.v1;
service PaymentService {
// ...
}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;
}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);
}
}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);
},
})
);
}
}const client = new grpc.Client(
{
'grpc.lb_policy_name': 'round_robin',
'grpc.service_config': JSON.stringify({
loadBalancingConfig: [{ round_robin: {} }],
}),
}
);gRPC powerful tetapi tidak selalu pilihan yang tepat. Pertimbangkan alternatif ketika:
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: