Go dan gRPC di 2026: Microservices Berkinerja Tinggi dan Pertanyaan Wawancara
Pembahasan mendalam tentang gRPC dengan Go di 2026. Protocol Buffers, RPC unary dan streaming, interceptor, pola tingkat produksi, serta pertanyaan wawancara umum untuk backend engineer.

gRPC telah menjadi lapisan komunikasi default untuk microservices Go yang membutuhkan latensi rendah dan kontrak API yang ketat. Dengan grpc-go v1.81, Go 1.26, dan kematangan ekosistem go-grpc-middleware, membangun layanan gRPC tingkat produksi di Go menjadi lebih mudah dari sebelumnya — dan topik ini tetap menjadi salah satu yang paling sering diuji dalam wawancara backend.
gRPC menggunakan HTTP/2 dan Protocol Buffers untuk serialisasi biner, menghasilkan payload hingga 10x lebih kecil serta streaming dua arah secara native. REST tetap lebih cocok untuk API publik; gRPC unggul pada komunikasi antarlayanan di mana performa dan keamanan tipe menjadi prioritas.
Mengapa gRPC Mendominasi Komunikasi Backend Go
Tiga sifat menjadikan gRPC pilihan alami untuk microservices Go. Pertama, Protocol Buffers menghasilkan kode Go bertipe kuat saat kompilasi, sehingga ketidakcocokan kontrak tertangkap sebelum deployment. Kedua, multiplexing HTTP/2 menghilangkan head-of-line blocking dan mendukung streaming full-duplex melalui satu koneksi TCP. Ketiga, model interceptor selaras dengan filosofi composition-over-inheritance Go, sehingga aspek lintas-potong seperti autentikasi dan tracing dapat disusun secara komposabel.
Ekosistem gRPC di Go telah terkonsolidasi pada beberapa pustaka yang teruji. grpc-go menangani inti transport dan codegen. Paket go-grpc-middleware v2 menyediakan interceptor produksi untuk logging, metrik, autentikasi, dan recovery. Stats handler gRPC dari OpenTelemetry mencakup distributed tracing tanpa kode interceptor khusus.
Mendefinisikan Layanan dengan Protocol Buffers
Setiap layanan gRPC dimulai dari sebuah berkas .proto. Skema tersebut mendefinisikan kontrak layanan, tipe pesan, dan metode RPC. Contoh berikut memodelkan layanan pengguna dengan pencarian unary dan metode server-streaming untuk feed aktivitas.
syntax = "proto3";
package user.v1;
option go_package = "gen/user/v1;userv1";
service UserService {
// Unary RPC: single request, single response
rpc GetUser(GetUserRequest) returns (GetUserResponse);
// Server streaming: single request, stream of responses
rpc StreamActivity(StreamActivityRequest) returns (stream ActivityEvent);
}
message GetUserRequest {
string user_id = 1;
}
message GetUserResponse {
string user_id = 1;
string email = 2;
string display_name = 3;
int64 created_at_unix = 4;
}
message StreamActivityRequest {
string user_id = 1;
int32 limit = 2;
}
message ActivityEvent {
string event_id = 1;
string action = 2;
string resource = 3;
int64 timestamp_unix = 4;
}Menjalankan protoc --go_out=. --go-grpc_out=. user_service.proto menghasilkan dua berkas: satu berisi tipe pesan dan satu berisi antarmuka client/server gRPC. Antarmuka server yang dihasilkan inilah yang harus dipenuhi oleh implementasi Go.
Mengimplementasikan Server gRPC di Go
Implementasi server menyematkan struct UnimplementedUserServiceServer yang dihasilkan, yang menyediakan kompatibilitas maju — menambahkan RPC baru ke berkas proto tidak akan merusak kode server yang ada sampai metode tersebut diimplementasikan secara eksplisit.
package server
import (
"context"
"fmt"
"time"
userv1 "myapp/gen/user/v1"
"google.golang.org/grpc/codes"
"google.golang.org/grpc/status"
)
type UserServer struct {
userv1.UnimplementedUserServiceServer
store UserStore // interface for DB access
}
func NewUserServer(store UserStore) *UserServer {
return &UserServer{store: store}
}
// GetUser handles the unary RPC
func (s *UserServer) GetUser(ctx context.Context, req *userv1.GetUserRequest) (*userv1.GetUserResponse, error) {
if req.GetUserId() == "" {
return nil, status.Error(codes.InvalidArgument, "user_id is required")
}
user, err := s.store.FindByID(ctx, req.GetUserId())
if err != nil {
return nil, status.Errorf(codes.Internal, "lookup failed: %v", err)
}
if user == nil {
return nil, status.Error(codes.NotFound, "user not found")
}
return &userv1.GetUserResponse{
UserId: user.ID,
Email: user.Email,
DisplayName: user.DisplayName,
CreatedAtUnix: user.CreatedAt.Unix(),
}, nil
}
// StreamActivity sends activity events as a server stream
func (s *UserServer) StreamActivity(req *userv1.StreamActivityRequest, stream userv1.UserService_StreamActivityServer) error {
events, err := s.store.GetActivity(stream.Context(), req.GetUserId(), int(req.GetLimit()))
if err != nil {
return status.Errorf(codes.Internal, "activity fetch failed: %v", err)
}
for _, evt := range events {
if err := stream.Send(&userv1.ActivityEvent{
EventId: evt.ID,
Action: evt.Action,
Resource: evt.Resource,
TimestampUnix: evt.Timestamp.Unix(),
}); err != nil {
return fmt.Errorf("stream send: %w", err)
}
}
return nil
}Dua pola yang perlu dicermati: status.Error dan status.Errorf menghasilkan error gRPC-native dengan kode status yang tepat, yang dapat diperiksa client secara terprogram. Penyematan UnimplementedUserServiceServer memastikan keamanan saat kompilasi ketika proto berkembang.
Bootstrap Server Produksi dengan Interceptor
Server gRPC polos memang menangani permintaan, tetapi minim observabilitas, autentikasi, dan recovery panic. Layanan produksi membutuhkan rantai interceptor. Pustaka go-grpc-middleware v2 menyediakan interceptor komposabel yang dirangkai dengan grpc.ChainUnaryInterceptor.
package main
import (
"log"
"net"
"os"
"os/signal"
"syscall"
"google.golang.org/grpc"
"google.golang.org/grpc/reflection"
"go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc"
"github.com/grpc-ecosystem/go-grpc-middleware/v2/interceptors/logging"
"github.com/grpc-ecosystem/go-grpc-middleware/v2/interceptors/recovery"
"github.com/grpc-ecosystem/go-grpc-middleware/v2/interceptors/ratelimit"
userv1 "myapp/gen/user/v1"
"myapp/server"
)
func main() {
// Interceptor order matters: recovery first, then metrics, then auth
srv := grpc.NewServer(
// OpenTelemetry tracing via stats handler (not interceptor)
grpc.StatsHandler(otelgrpc.NewServerHandler()),
grpc.ChainUnaryInterceptor(
recovery.UnaryServerInterceptor(), // catch panics
ratelimit.UnaryServerInterceptor(limiter), // rate limiting
logging.UnaryServerInterceptor(logger), // structured logging
authInterceptor, // token validation
),
grpc.ChainStreamInterceptor(
recovery.StreamServerInterceptor(),
ratelimit.StreamServerInterceptor(limiter),
logging.StreamServerInterceptor(logger),
),
)
// Register service implementation
userv1.RegisterUserServiceServer(srv, server.NewUserServer(store))
// Enable reflection for grpcurl and debugging
reflection.Register(srv)
lis, err := net.Listen("tcp", ":50051")
if err != nil {
log.Fatalf("listen: %v", err)
}
// Graceful shutdown on SIGTERM
go func() {
sig := make(chan os.Signal, 1)
signal.Notify(sig, syscall.SIGTERM, syscall.SIGINT)
<-sig
log.Println("shutting down gRPC server")
srv.GracefulStop()
}()
log.Printf("gRPC server listening on :50051")
if err := srv.Serve(lis); err != nil {
log.Fatalf("serve: %v", err)
}
}Urutan interceptor menentukan prioritas eksekusi. Recovery berjalan lebih dulu untuk menangkap panic dari interceptor mana pun di hilir. Rate limiting berjalan sebelum autentikasi untuk melindungi lapisan autentikasi itu sendiri dari penyalahgunaan. Tracing OpenTelemetry menggunakan StatsHandler alih-alih interceptor — inilah pendekatan yang direkomendasikan sejak grpc-go v1.81, karena stats handler memiliki akses ke peristiwa transport tingkat lebih rendah.
Siap menguasai wawancara Go Anda?
Berlatih dengan simulator interaktif, flashcards, dan tes teknis kami.
Pola Penanganan Error gRPC di Go
Penanganan error yang tepat membedakan layanan gRPC produksi dari sekadar prototipe. gRPC mendefinisikan sekumpulan kode status kanonis yang dipahami setiap client. Paket status di Go memetakan kode-kode ini menjadi error.
Pola-pola utamanya:
- Kembalikan
codes.InvalidArgumentuntuk permintaan yang salah bentuk (bug client) - Kembalikan
codes.NotFoundketika sumber daya tidak ada - Kembalikan
codes.Internaluntuk kegagalan server yang tak terduga - Kembalikan
codes.Unauthenticatedketika kredensial hilang atau tidak valid - Kembalikan
codes.PermissionDeniedketika kredensial valid tetapi tidak mencukupi
package rpcerr
import (
"google.golang.org/grpc/codes"
"google.golang.org/grpc/status"
)
// NotFound wraps a resource-not-found error with a consistent message
func NotFound(resource, id string) error {
return status.Errorf(codes.NotFound, "%s %q not found", resource, id)
}
// InvalidArg wraps a validation error
func InvalidArg(field, reason string) error {
return status.Errorf(codes.InvalidArgument, "%s: %s", field, reason)
}
// Internal wraps an unexpected error, hiding internals from the client
func Internal(err error) error {
// Log the real error server-side; return generic message to client
return status.Error(codes.Internal, "internal server error")
}Membungkus error dalam konstruktor spesifik-domain menjaga konsistensi kode status di seluruh RPC. Client dapat melakukan switch pada status.Code(err) untuk menangani setiap kasus tanpa mem-parsing string error.
Menguji Layanan gRPC dengan bufconn
Pengujian integrasi layanan gRPC biasanya memerlukan menjalankan server TCP nyata. Paket bufconn menyediakan listener dalam-memori yang menghilangkan alokasi port dan overhead jaringan sepenuhnya.
package server_test
import (
"context"
"net"
"testing"
"google.golang.org/grpc"
"google.golang.org/grpc/credentials/insecure"
"google.golang.org/grpc/test/bufconn"
userv1 "myapp/gen/user/v1"
"myapp/server"
)
const bufSize = 1024 * 1024
func setupServer(t *testing.T) userv1.UserServiceClient {
t.Helper()
lis := bufconn.Listen(bufSize) // in-memory listener
srv := grpc.NewServer()
userv1.RegisterUserServiceServer(srv, server.NewUserServer(mockStore{}))
go func() {
if err := srv.Serve(lis); err != nil {
t.Errorf("server exited: %v", err)
}
}()
t.Cleanup(srv.GracefulStop)
// Dial the in-memory listener
conn, err := grpc.NewClient(
"passthrough:///bufconn",
grpc.WithContextDialer(func(ctx context.Context, _ string) (net.Conn, error) {
return lis.DialContext(ctx)
}),
grpc.WithTransportCredentials(insecure.NewCredentials()),
)
if err != nil {
t.Fatalf("dial bufconn: %v", err)
}
t.Cleanup(func() { conn.Close() })
return userv1.NewUserServiceClient(conn)
}
func TestGetUser_NotFound(t *testing.T) {
client := setupServer(t)
_, err := client.GetUser(context.Background(), &userv1.GetUserRequest{
UserId: "nonexistent",
})
// Verify gRPC status code
st, ok := status.FromError(err)
if !ok || st.Code() != codes.NotFound {
t.Errorf("expected NotFound, got %v", err)
}
}bufconn membuat koneksi gRPC nyata dengan serialisasi penuh dan eksekusi interceptor, hanya saja tanpa TCP. Pengujian berjalan lebih cepat dan dapat dijalankan paralel tanpa konflik port. Ini adalah pola standar yang digunakan di dalam repositori grpc-go itu sendiri.
Mengamankan gRPC dengan mTLS dan Autentikasi Token
Layanan gRPC produksi memerlukan keamanan transport. Dua pendekatan mendominasi: mTLS untuk autentikasi antarlayanan di mana kedua sisi menyajikan sertifikat, dan autentikasi berbasis token (JWT atau API key) untuk identitas client dalam kanal yang sudah aman.
Konfigurasi mTLS memuat sertifikat saat server dijalankan:
package main
import (
"crypto/tls"
"crypto/x509"
"os"
"google.golang.org/grpc/credentials"
)
func loadTLSCredentials(certFile, keyFile, caFile string) (credentials.TransportCredentials, error) {
cert, err := tls.LoadX509KeyPair(certFile, keyFile)
if err != nil {
return nil, err
}
caPool := x509.NewCertPool()
caPEM, err := os.ReadFile(caFile)
if err != nil {
return nil, err
}
caPool.AppendCertsFromPEM(caPEM)
tlsCfg := &tls.Config{
Certificates: []tls.Certificate{cert},
ClientAuth: tls.RequireAndVerifyClientCert, // enforce mTLS
ClientCAs: caPool,
MinVersion: tls.VersionTLS13, // TLS 1.3 minimum in 2026
}
return credentials.NewTLS(tlsCfg), nil
}TLS 1.3 menjadi baseline untuk layanan gRPC pada 2026, menawarkan handshake lebih cepat (1-RTT) dan suite cipher yang lebih kuat dibanding TLS 1.2. Untuk autentikasi berbasis token yang dilapis di atas TLS, sebuah interceptor unary mengekstrak token dari metadata gRPC dan memvalidasinya sebelum handler dieksekusi.
Jalur sertifikat yang di-hardcode mengharuskan restart server untuk rotasi. Deployment produksi sebaiknya menggunakan tls.Config.GetCertificate atau sidecar seperti Envoy untuk menangani pembaruan sertifikat otomatis tanpa downtime.
Pertanyaan Wawancara: gRPC dan Microservices Go
Pertanyaan-pertanyaan berikut sering muncul dalam wawancara backend engineering yang berfokus pada Go dan sistem terdistribusi. Setiap jawaban menyasar kedalaman yang diharapkan di level senior.
Apa empat tipe RPC dalam gRPC dan kapan masing-masing sesuai?
Unary (satu permintaan, satu respons) mencakup sebagian besar operasi CRUD. Server streaming cocok untuk skenario di mana server mendorong banyak hasil — feed aktivitas, hasil pencarian, pembaruan real-time. Client streaming berlaku untuk skenario unggah atau penulisan batch di mana client mengirim banyak pesan sebelum server merespons. Bidirectional streaming melayani chat, penyuntingan kolaboratif, atau protokol apa pun di mana kedua sisi mengirim secara independen.
Bagaimana gRPC menangani kompatibilitas mundur ketika sebuah field proto dihapus?
Proto3 tidak pernah menggunakan ulang nomor field. Menghapus sebuah field berarti server mengabaikan nilainya jika client lama masih mengirimnya, dan client lama yang membaca respons cukup melihat nilai zero untuk field yang dihapus. Kata kunci reserved mencegah penggunaan ulang nomor atau nama field secara tidak sengaja. Ini secara fundamental berbeda dari API JSON di mana penghapusan field dapat menyebabkan kegagalan deserialisasi.
Jangan pernah mengubah tipe atau nomor field yang sudah ada. Tambahkan field baru dengan nomor baru. Gunakan reserved untuk memensiunkan nomor lama. Aturan ini berlaku di semua bahasa dan menjamin deployment tanpa downtime.
Jelaskan peran interceptor dalam layanan gRPC produksi.
Interceptor adalah lapisan middleware gRPC. Mereka dieksekusi sebelum dan sesudah handler, dalam urutan registrasinya. Rantai produksi yang umum: recovery (menangkap panic) > rate limiting > logging > autentikasi > validasi. Pustaka go-grpc-middleware v2 menyediakan interceptor komposabel yang mengikuti pola ini. Interceptor tidak dapat memodifikasi lapisan transport (TLS, port) — mereka hanya beroperasi pada level RPC.
Apa perbedaan antara grpc.StatsHandler dan sebuah interceptor?
Interceptor membungkus handler RPC dan beroperasi di level aplikasi. Stats handler menerima peristiwa level transport: koneksi dimulai, pengiriman/penerimaan pesan, penyelesaian RPC. OpenTelemetry menggunakan stats handler karena mereka menangkap metrik yang tak terlihat oleh interceptor, seperti jumlah byte dan peristiwa siklus hidup koneksi. Sejak grpc-go v1.66+, rekomendasi resmi adalah menggunakan stats handler untuk observabilitas dan interceptor untuk logika bisnis.
Bagaimana Anda mengimplementasikan graceful shutdown untuk server gRPC?
GracefulStop() berhenti menerima koneksi baru dan menunggu RPC yang sedang berjalan selesai. Polanya: dengarkan SIGTERM dalam sebuah goroutine, panggil GracefulStop(), lalu panggilan Serve() akan kembali. Untuk RPC streaming yang mungkin berjalan tanpa batas, tetapkan deadline dengan pembatalan context atau gunakan health check yang berhenti mengembalikan SERVING sebelum shutdown dimulai, memberi load balancer waktu untuk menguras trafik.
Kapan sebuah tim sebaiknya memilih gRPC ketimbang REST untuk layanan internal?
gRPC adalah pilihan yang lebih kuat ketika: layanan membutuhkan kontrak API ketat yang diberlakukan saat kompilasi; sistem memerlukan streaming (server push, dua arah); latensi penting dan serialisasi biner mengurangi ukuran payload; tim mengelola kode client maupun server. REST lebih disukai untuk API publik yang dikonsumsi browser (JSON native, tanpa proxy), API dengan caching berat via semantik HTTP, atau tim yang membutuhkan kompatibilitas tooling maksimum. Banyak arsitektur menggunakan keduanya — gRPC secara internal dengan gateway REST untuk konsumen eksternal.
Mulai berlatih!
Uji pengetahuan Anda dengan simulator wawancara dan tes teknis kami.
Kesimpulan
- Protocol Buffers memberlakukan kontrak API saat kompilasi — perubahan yang merusak muncul saat code generation, bukan saat runtime
- Pola
UnimplementedServermenyediakan kompatibilitas maju ketika menambahkan RPC baru ke layanan yang berkembang - Urutan interceptor penting: recovery dulu, lalu rate limiting, lalu autentikasi — pustaka go-grpc-middleware v2 menangani perangkaian
- Gunakan
grpc.StatsHandlerdengan OpenTelemetry untuk tracing; sisakan interceptor untuk logika bisnis seperti autentikasi dan validasi bufconnmemungkinkan pengujian integrasi yang cepat dan bebas port yang menjalankan seluruh stack gRPC termasuk serialisasi dan interceptor- mTLS dengan TLS 1.3 adalah baseline 2026 untuk keamanan antarlayanan; lapisi autentikasi token di atasnya untuk identitas dalam mesh
- Persiapkan diri untuk pertanyaan wawancara gRPC dengan memahami empat tipe RPC, aturan kompatibilitas mundur proto, dan perbedaan interceptor vs stats handler
Mulai berlatih!
Uji pengetahuan Anda dengan simulator wawancara dan tes teknis kami.
Tag
Bagikan
Artikel terkait

Go Error Handling di 2026: Pola, Wrapping, dan Pertanyaan Wawancara Teknis
Panduan lengkap tentang error handling di Go: sentinel error, custom error type, errors.Is, errors.As, error wrapping dengan fmt.Errorf %w, serta pertanyaan wawancara teknis yang sering muncul.

Go 1.26 Wawancara Teknis: Green Tea GC, go fix, dan Optimasi Stack
Panduan persiapan wawancara teknis Go 1.26: Green Tea garbage collector dengan pengurangan overhead 10-40%, alat go fix dengan modernizers, alokasi slice di stack, deteksi kebocoran goroutine, dan keamanan pasca-kuantum.

Design Pattern Go: Pattern Esensial dan Pertanyaan Wawancara untuk Developer Go
Kuasai design pattern Go: Functional Options, Strategy, Factory, dan Observer. Contoh kode praktis, praktik terbaik yang idiomatis, dan pertanyaan wawancara umum untuk developer Go.