101. Studi Kasus: Mengkonsumsi gRPC Server Go dari Python Client
gRPC (Google Remote Procedure Call) adalah framework komunikasi open source yang telah menjadi fondasi berbagai sistem microservices modern. Arsitektur gRPC mempercepat pengembangan layanan lintas platform dengan performa tinggi, terutama ketika komunikasi antar bahasa diperlukan. Pada artikel kali ini, kita akan membahas tentang integrasi gRPC antara server Go dan client Python, lengkap dengan studi kasus, contoh kode, dan diagram alur.
Mengapa gRPC?
Beberapa alasan gRPC digemari:
- Performance: Berbasis HTTP/2 yang mendukung multiplexing dan kompresi header.
- IDL (Interface Definition Language): Protobuf memudahkan definisi kontrak layanan.
- Interoperabilitas: Mendukung berbagai bahasa populer seperti Go, Python, Java, C#, dan lain-lain.
- Streaming: Mendukung client, server, dan bidirectional streaming.
- Scalability: Cocok untuk arsitektur microservices di production.
Studi Kasus
Mari kita buat sebuah layanan sederhana UserService yang menyediakan dua operasi utama:
- GetUserByID(id) - Mendapatkan informasi user berdasarkan ID.
- ListUsers() - Mendapatkan list seluruh user yang tersedia.
Layanan ini akan dibangun dengan server Go dan dikonsumsi oleh client Python.
1. Mendesain Kontrak Layanan dengan Proto
Kita mulai dengan mendefinisikan API-nya menggunakan protobuf.
// user.proto
syntax = "proto3";
package user;
service UserService {
rpc GetUserByID(UserIDRequest) returns (UserResponse);
rpc ListUsers(Empty) returns (UserListResponse);
}
message Empty {}
message UserIDRequest {
int32 id = 1;
}
message UserResponse {
int32 id = 1;
string name = 2;
string email = 3;
}
message UserListResponse {
repeated UserResponse users = 1;
}
2. Implementasi Server Go
Setup Project
Struktur folder:
grpc-demo/
├── proto/
│ └── user.proto
├── server/
└── main.go
Generate Go Code dari Proto
Jalankan:
protoc --go_out=. --go-grpc_out=. proto/user.proto
Sample Code Server
package main
import (
"context"
"log"
"net"
pb "grpc-demo/proto"
"google.golang.org/grpc"
)
type userServer struct {
pb.UnimplementedUserServiceServer
users map[int32]*pb.UserResponse
}
func (s *userServer) GetUserByID(ctx context.Context, req *pb.UserIDRequest) (*pb.UserResponse, error) {
user, ok := s.users[req.Id]
if !ok {
return nil, status.Errorf(codes.NotFound, "user not found")
}
return user, nil
}
func (s *userServer) ListUsers(_ context.Context, _ *pb.Empty) (*pb.UserListResponse, error) {
var userList []*pb.UserResponse
for _, user := range s.users {
userList = append(userList, user)
}
return &pb.UserListResponse{Users: userList}, nil
}
func main() {
lis, err := net.Listen("tcp", ":50051")
if err != nil {
log.Fatalf("failed to listen: %v", err)
}
server := grpc.NewServer()
pb.RegisterUserServiceServer(server, &userServer{
users: map[int32]*pb.UserResponse{
1: {Id: 1, Name: "Alice", Email: "alice@example.com"},
2: {Id: 2, Name: "Bob", Email: "bob@example.com"},
},
})
log.Println("gRPC server listening on :50051")
if err := server.Serve(lis); err != nil {
log.Fatalf("failed to serve: %v", err)
}
}
3. Generate Python Client Stub
Install dependency:
pip install grpcio grpcio-tools
Compile proto untuk Python:
python -m grpc_tools.protoc -I./proto --python_out=. --grpc_python_out=. proto/user.proto
Akan menghasilkan user_pb2.py dan user_pb2_grpc.py.
4. Implementasi Python Client
Sample Code
import grpc
import user_pb2
import user_pb2_grpc
def get_user_by_id(stub, user_id):
req = user_pb2.UserIDRequest(id=user_id)
try:
res = stub.GetUserByID(req)
print(f"User {user_id}: {res.name} ({res.email})")
except grpc.RpcError as e:
print(f"Failed to get user {user_id}: {e.details()}")
def list_users(stub):
res = stub.ListUsers(user_pb2.Empty())
for u in res.users:
print(f"- {u.id}: {u.name} ({u.email})")
def main():
with grpc.insecure_channel('localhost:50051') as channel:
stub = user_pb2_grpc.UserServiceStub(channel)
print("== List Users ==")
list_users(stub)
print("\n== Get User By ID ==")
get_user_by_id(stub, 1)
get_user_by_id(stub, 99) # Example: user tidak ada
if __name__ == '__main__':
main()
5. Diagram Alur
Untuk memperjelas, berikut diagram alur interaksi antar komponen menggunakan mermaid:
sequenceDiagram
participant Client as Python Client
participant Server as Go gRPC Server
Client->>Server: Connect (TCP :50051)
Client->>Server: GetUserByID(1)
Server-->>Client: UserResponse (Alice)
Client->>Server: GetUserByID(99)
Server-->>Client: Error (not found)
Client->>Server: ListUsers()
Server-->>Client: UserListResponse ([Alice, Bob])
6. Simulasi Output
Mari lihat tabel data user di server dan kemungkinan output client:
| ID | Name | |
|---|---|---|
| 1 | Alice | alice@example.com |
| 2 | Bob | bob@example.com |
Output client saat dijalankan:
== List Users ==
- 1: Alice (alice@example.com)
- 2: Bob (bob@example.com)
== Get User By ID ==
User 1: Alice (alice@example.com)
Failed to get user 99: user not found
7. Pembahasan dan Best Practice
- Error Handling: Client harus menangani error gRPC RpcError untuk kasus tidak ditemukan, timeout, dsb.
- Contract-First: Dengan menggunakan proto sebagai sumber kebenaran (source of truth), baik client maupun server bisa dikembangkan independen selama kontrak tidak berubah.
- Interop Testing: Selalu lakukan integrasi test lintas bahasa dengan protokol yang sama, pastikan serialization/deserialization berjalan benar.
- Secure Channel: Untuk production, gunakan TLS pada insecure_channel.
- Proto Versioning: Jika API bertambah/berubah, jaga backward compatibility (mis: gunakan field baru dengan tag unik).
8. Kesimpulan
Studi kasus ini menunjukkan bagaimana production-ready microservices dapat dikembangkan dengan stack lintas bahasa menggunakan gRPC. Dengan kontrak API terdefinisi di proto, kita bisa membangun server Go dan melayani request dari berbagai platform, seperti Python client. Pendekatan ini bukan hanya meningkatkan performa dan konsistensi data, namun juga mempercepat pengembangan lintas tim dan bahasa.
Adopsi gRPC membuka peluang mengembangkan polyglot systems tanpa kompromi pada maintainability dan scalability.
Referensi: