tutorial

31 Apa Itu Interceptor dalam gRPC?

gRPC telah menjadi backbone migrasi banyak sistem monolitik menuju arsitektur microservices. Dengan performa tinggi, dukungan multi-bahasa, dan kemudahan integrasi, tidak heran bila gRPC kini banyak digunakan sebagai transport protocol antara service-service modern. Salah satu fitur powerful namun seringkali underrated pada gRPC adalah Interceptor.

Dalam artikel ini, kita akan membahas tuntas:

  • Apa itu Interceptor dalam gRPC
  • Bagaimana prinsip kerjanya
  • Contoh penggunaan (kode)
  • Studi kasus simulasi
  • Diagram alur eksekusi

Mari kita mulai!


Apa Itu Interceptor dalam gRPC?

Secara sederhana, Interceptor gRPC serupa dengan middleware pada framework web seperti Express.js, Spring, atau Gin. Interceptor memungkinkan kita “menyisipkan logika tambahan” berupa cross-cutting concerns ke dalam siklus hidup setiap RPC yang diproses.

Contoh cross-cutting concerns yang umum diterapkan melalui Interceptor:

  • Logging
  • Authentication & Authorization
  • Metrics & Tracing (Distributed Tracing)
  • Exception Handling & Retry Logic
  • Rate Limiting

Dengan Interceptor, kode yang menangani concerns di atas bisa dipisahkan dari business logic utama service, sehingga codebase lebih clean, maintainable, dan reusable.


Konsep Dasar Interceptor di gRPC

Pada dasarnya, Interceptor menyediakan hook ke dalam eksekusi RPC baik di sisi client maupun server.

  • Client Interceptor menjalankan logika sebelum RPC dikirim ke server, atau ketika response diterima.
  • Server Interceptor menangkap request sebelum reaching handler, atau sebelum response dikembalikan ke client.

Interceptor pada server tersedia dalam dua bentuk:

  1. Unary Server Interceptor (untuk unary RPC)
  2. Stream Server Interceptor (untuk streaming RPC)

Pada client, konsepnya sama.

Diagram Alur Interceptor

Mari simulasikan alur eksekusi request dengan beberapa Interceptor pada server menggunakan diagram mermaid berikut:

flowchart LR
    Client(Request) --> |RPC Call| Interceptor1
    Interceptor1 --> Interceptor2
    Interceptor2 --> InterceptorN
    InterceptorN --> ServiceHandler
    ServiceHandler --> InterceptorN
    InterceptorN --> Interceptor2
    Interceptor2 --> Interceptor1
    Interceptor1 --> |Response| Client

Pada diagram di atas:

  • Setiap Interceptor bisa menjalankan logika sebelum request dilanjutkan ke Interceptor berikut atau ke Service Handler (logic utama handler).
  • Setelah handler selesai, Interceptor juga bisa memodifikasi response.

Implementasi Interceptor: Contoh Pada gRPC-Go

Mari kita praktik menggunakan bahasa Go dengan gRPC-Go.

1. Unary Server Interceptor Sederhana (Logging)

import (
    "context"
    "google.golang.org/grpc"
    "log"
    "time"
)

// Unary Interceptor Function Type
func LoggingInterceptor(
    ctx context.Context,
    req interface{},
    info *grpc.UnaryServerInfo,
    handler grpc.UnaryHandler,
) (resp interface{}, err error) {
    start := time.Now()
    log.Printf("gRPC method: %s; at: %v", info.FullMethod, start)
    resp, err = handler(ctx, req) // call the next interceptor / actual handler
    log.Printf("gRPC method: %s; finished in %v, err = %v", info.FullMethod, time.Since(start), err)
    return resp, err
}

Cara mendaftarkannya ke server:

grpcServer := grpc.NewServer(
    grpc.UnaryInterceptor(LoggingInterceptor),
)

2. Chaining Multiple Interceptor

gRPC v1.38+ mendukung chaining beberapa interceptor menggunakan helper:

grpc.ChainUnaryInterceptor(
    LoggingInterceptor,
    AuthInterceptor,
    TracingInterceptor,
)

Masing-masing Interceptor akan dipanggil sesuai urutan daftar di atas.

3. Client Interceptor

Mirip dengan server-side:

func ClientLoggingInterceptor(
    ctx context.Context,
    method string,
    req, reply interface{},
    cc *grpc.ClientConn,
    invoker grpc.UnaryInvoker,
    opts ...grpc.CallOption,
) error {
    log.Printf("Client calling: %s", method)
    err := invoker(ctx, method, req, reply, cc, opts...)
    log.Printf("Client finished: %s, err: %v", method, err)
    return err
}

conn, err := grpc.Dial(
    "localhost:50051",
    grpc.WithUnaryInterceptor(ClientLoggingInterceptor),
)

Studi Kasus: Auth & Logging Interceptor

Simulasi implementasi dua Interceptor pada server gRPC: satu untuk Authentication, satu lagi untuk Logging.

Auth Interceptor

func AuthInterceptor(
    ctx context.Context,
    req interface{},
    info *grpc.UnaryServerInfo,
    handler grpc.UnaryHandler,
) (interface{}, error) {
    md, ok := metadata.FromIncomingContext(ctx)
    if !ok || len(md["authorization"]) == 0 {
        return nil, status.Errorf(codes.Unauthenticated, "token missing")
    }
    token := md["authorization"][0]
    if token != "valid-token" {
        return nil, status.Errorf(codes.Unauthenticated, "invalid token")
    }
    // lanjut ke handler berikutnya
    return handler(ctx, req)
}

Kombinasi Interceptor

grpcServer := grpc.NewServer(
    grpc.ChainUnaryInterceptor(
        LoggingInterceptor,
        AuthInterceptor,
    ),
)

Simulation Table

Request FlowLogging InterceptorAuth InterceptorHandler
No Authentication HeaderLog requestGagal, UnauthenticatedTidak jalan
Wrong TokenLog requestGagal, UnauthenticatedTidak jalan
Valid TokenLog requestLanjutHandler jalan
Error di HandlerLog requestLanjutLog Error

Keunggulan Menggunakan Interceptor

  • Separation of Concern: Kode untuk logging/auth/rate limit tidak bercampur di handler utama.
  • Reusability: Interceptor dapat digunakan ulang lintas service gRPC.
  • Stackable/Composable: Bisa membuat pipeline INTERCEPTOR sesuai kebutuhan.

Best Practice & Tips

  1. Jangan bawa state lokal di Interceptor. Interceptor dipanggil per-RPC, sebaiknya gunakan context/jangan ada race condition.
  2. Handle error dengan baik. Kirim error dengan status gRPC agar mudah didiagnosis client.
  3. Hati-hati memodifikasi context. Bila perlu, gunakan context.WithValue lalu diteruskan.
  4. Streaming Interceptor berbeda dengan Unary. Untuk streaming (server/client), antarmuka interceptornya berbeda, perlu perhatian ekstra.

Kapan Sebaiknya Menggunakan Interceptor?

  • Saat Anda perlu apply validation, authentication, metrics, dsb KESELURUHAN method secara seragam.
  • Agar business logic service tetap clean dari concern lain.
  • Untuk keperluan observabilitas: logging/tracing yang konsisten.

Kesimpulan

Interceptor adalah salah satu fitur gRPC yang memberi power pada engineer untuk menjaga codebase tetap clean, scalable, dan observable. Pada microservices modern, membangun ekosistem cross-cutting concern tanpa Interceptor ibarat menyusun batu bata tanpa semen: sulit terbaca, mudah berantakan.

Apakah service gRPC Anda sudah memanfaatkan power Interceptor? Jika belum, now is the perfect time to refactor dan nikmati maintainability-nya!

Jika ada pertanyaan atau ingin sharing pengalaman, silakan tinggalkan komentar!


Referensi:

comments powered by Disqus