tutorial

  1. gRPC Name Resolver Kustom


title: 65. gRPC Name Resolver Kustom: Mengoptimalkan Service Discovery dengan Gaya Sendiri
date: 2024-06-11
tags: [golang, grpc, networking, distributed systems, service discovery]

gRPC telah lama menjadi andalan developer dalam membangun microservices yang cepat, ringan, dan mudah di-maintain. Salah satu komponen krusial yang sering terlupakan adalah Name Resolver. Secara default, gRPC menghadirkan beberapa resolver seperti dns:// atau passthrough://, namun kebutuhan di lingkungan distributed modern — seperti orchestrator custom, service mesh, atau environment hybrid — kerap membutuhkan sentuhan “kustom”. Masalahnya, sangat sedikit engineer yang benar-benar memahami betapa powerful dan fleksibelnya membuat Name Resolver Kustom sendiri.

Pada artikel ini, saya akan membahas lebih dalam tentang gRPC Name Resolver Kustom — mulai dari arsitektur, alur kerja, hingga implementasi sederhana dan use case-nya di sistem nyata.


Mengapa Perlu Name Resolver Kustom?

Saat bekerja dengan microservices, cara layanan menemukan endpoint satu sama lain (service discovery) adalah life and death. Dalam konteks gRPC, target endpoint seperti dns:///service.mydomain.com akan diproses oleh name resolver. Tapi bagaimana jika source layanan datangnya dari registry unik, file lokal, atau bahkan API bespoke internal? Di sinilah Custom Name Resolver dibutuhkan.

Beberapa skenario umum:

  • Integrasi dengan registry internal (HashiCorp Consul, ETCD, dsb)
  • Dynamic microservice pada Kubernetes dengan label/filter khusus
  • Service discovery via API Gateway khusus yang tidak support DNS A/AAAA secara native

Alur Kerja Name Resolver di gRPC

Mari jabarkan diagram sederhana tentang proses name resolution di gRPC.

sequenceDiagram
  participant Client
  participant gRPC Library
  participant NameResolver
  participant LoadBalancer

  Client->>gRPC Library: Dial ("myres://user-service/id-1234")
  gRPC Library->>NameResolver: Build("myres")
  NameResolver->>gRPC Library: UpdateState(Resolved addresses)
  gRPC Library->>LoadBalancer: Pass endpoint list
  LoadBalancer->>Client: Ready to RPC!

Summary:

  • Client memulai koneksi dengan target khusus via proto://target
  • gRPC memilih resolver sesuai schema (proto)
  • Name Resolver mem-fetch dan mengkalkulasi endpoint dari source kustom
  • Update state ke gRPC core untuk diteruskan ke load balancer dan dial RPC

Anatomy Name Resolver pada gRPC-Go

Name resolver adalah implementasi dari interface berikut (simplified):

type Builder interface {
    Scheme() string
    Build(target Target, cc ClientConn, opts BuildOptions) (Resolver, error)
}

type Resolver interface {
    ResolveNow(ResolveNowOptions)
    Close()
}
  • Builder: Registrasi awal yang menentukan “scheme” resolver
  • Resolver: Proses backend fetching/monitoring ke source endpoint

Studi Kasus: Custom In-Memory Resolver

Katakan kita punya service discovery berbasis YAML lokal.

File: services.yaml

user-service:
  - 10.0.0.2:50051
  - 10.0.0.3:50051
order-service:
  - 10.0.0.4:50052

Langkah 1: Definisikan Scheme Resolver

const scheme = "yamlmem"

func init() {
    resolver.Register(&yamlMemBuilder{})
}

type yamlMemBuilder struct{}

func (b *yamlMemBuilder) Scheme() string { return scheme }

func (b *yamlMemBuilder) Build(target resolver.Target, cc resolver.ClientConn, opts resolver.BuildOptions) (resolver.Resolver, error) {
    // Fetch endpoints dari YAML berdasarkan target.Endpoint
    addrs := resolveFromYaml(target.Endpoint)
    cc.UpdateState(resolver.State{Addresses: addrs})
    return &yamlMemResolver{cc: cc}, nil
}

Langkah 2: Proses Resolving YAML

func resolveFromYaml(endpoint string) []resolver.Address {
    // 1. Load file YAML
    // 2. Parse endpoint
    // 3. Return []resolver.Address dengan format host:port
    // Di sini saya simplifikasi hardcoded
    if endpoint == "user-service" {
        return []resolver.Address{
            {Addr: "10.0.0.2:50051"},
            {Addr: "10.0.0.3:50051"},
        }
    }
    return nil
}

type yamlMemResolver struct{ cc resolver.ClientConn }

func (r *yamlMemResolver) ResolveNow(o resolver.ResolveNowOptions) {}
func (r *yamlMemResolver) Close() {}

Langkah 3: Dial dengan Scheme Kustom

conn, err := grpc.Dial("yamlmem:///user-service", grpc.WithInsecure())
if err != nil {
    log.Fatalf("dial failed: %v", err)
}

Dengan pattern ini, setiap kali kita menjalankan grpc.Dial dengan scheme yamlmem://, resolver kustom akan otomatis mengambil dan memperbarui endpoint dari YAML.


Simulasi Hasil: Tabel Resolusi

Client Dial TargetgRPC Scheme ResolverEndpoints Didapatkan
yamlmem:///user-serviceyamlmem10.0.0.2:50051, 10.0.0.3:50051
yamlmem:///order-serviceyamlmem10.0.0.4:50052
dns:///api.example.orgdns

Pengembangan Lanjutan

a. Live Update

Tentu, di dunia nyata resolver akan memonitor sumber (file, registry, REST API) dan memanggil cc.UpdateState() saat ada perubahan.

go func() {
    for changes := range watchServiceChange() {
        cc.UpdateState(resolver.State{Addresses: changes})
    }
}()

b. Error Handling

cc.ReportError(err) bisa dipakai saat proses resolve gagal. Ini akan mengirim feedback ke client/error balancer.

c. Advanced Use Case: Resolver untuk Service Mesh Labels

Bayangkan Anda ingin hanya service dengan label blue atau canary yang resolve-able. Custom resolver dapat melakukan filtering endpoint sebelum meng-update state.


Kapan Harus Membuat Resolver Kustom?

Use CaseHarus Kustom?Alasan
DNS biasaTidakBuilt-in resolver sudah cukup
Load dari file/registry sendiriYaDiperlukan access logic non-standar
Koneksi dengan API eksternalYaSumber data endpoints bukan lewat DNS
Filtering berdasarkan business logicYaFiltering/logic hanya di sisi aplikasi
Single static hostTidakBisa pakai passthrough://

Kesimpulan

Kemampuan menciptakan gRPC Name Resolver Kustom bisa jadi superpower bagi engineer yang ingin microservice yang lebih fleksibel, scalable, dan sesuai kebutuhan distribusi layanan modern. Baik itu registry internal, service mesh, atau hybrid environment — dengan resolver kustom, kita bisa mendefinisikan cara service discovery sendiri, tanpa harus menunggu provider atau “hack” DNS.


Referensi & Tools

Selamat bereksperimen dan eksplor gRPC lebih dalam dengan resolver kustom!
Tinggalkan komentar kalau ada ide atau pertanyaan seputar resolver unik atau integrasi service discovery di perusahaan Anda 👍


Follow saya untuk artikel selanjutnya tentang distributed systems & scalable microservices!

comments powered by Disqus