Go
Completed
-
Section 1: Getting Started
-
Section 2: Core Syntax
-
Section 3: Functions and Methods
-
Section 4: Concurrency
-
Section 5: Packages and Tooling
-
Section 6: More Standard Library
-
Section 7: Building Services
-
Section 8: Advanced Go
-
Section 9: Go in the Cloud-Native Ecosystem
-
Section 10: Data Structures and Algorithms in Go
-
Section 11: Testing and Deployment
-
Section 12: Practical Projects
-
Section 13: More Standard Library Practice
-
Section 14: More Practice Projects
-
Section 15: Design Patterns in Go
-
Section 16: Interview Practice
-
Section 17: Package fmt In Depth
-
Section 18: Package strings and strconv
-
Section 19: Package os and io
-
Section 20: Package time
-
Section 21: Package sort and container
-
Section 22: Package encoding
-
Section 23: Package net/http In Depth
-
Section 24: Package context
-
Section 25: Package regexp and bytes
-
Section 26: Package errors In Depth
-
Section 27: Package crypto and hash
-
Section 28: Package flag and log
-
Section 29: Package sync In Depth
-
Section 30: More Practice Exercises
-
Section 31: Go Modules and Workspaces In Depth
-
Section 32: Generics Deep Dive (Go 1.18+)
-
Section 33: Testing Package In Depth
-
Section 34: More Interview and Whiteboard Practice
-
Section 35: Package math and unicode
-
Section 36: Package path and filepath
-
Section 37: Package database/sql
-
Section 38: Package text/template and html/template
-
Section 39: Package archive and compress
-
Section 40: Lower-Level net Package
-
Section 41: Package runtime and reflect
-
Section 42: Package embed and io/fs
-
Section 43: Go Assembly and CGO Basics
-
Section 44: Building CLIs and TUIs
-
Section 45: Go Performance Tuning
-
Section 46: More Real-World Projects
-
Section 47: Go in Production
-
Section 48: Go Design Patterns
-
Section 49: Go Interfaces Deep Dive
-
Section 50: Final Practice and Review
56: gRPC Services in Go
Imagine you work in a massive corporate headquarters. You need a specific report from the Finance department on the 10th floor. You can't just wander into their office and start chatting; they have a strict protocol. To get what you need, you must fill out a very specific "Request Form" (Form 10-A). This form has exact fields: a Request ID, the Date Range, and the Department Code. If you leave a field blank or write "yesterday" instead of a formatted date, the clerk will simply hand the form back to you without processing it.
Once you submit that form via an internal courier, the courier doesn't just carry a piece of paper; they use a high-speed pneumatic tube system to zip it across the building. The Finance clerk receives the form, does the work, fills out a "Response Form" (Form 10-B), and zips it back to you. You didn't need to know how the Finance department is organized or what software they use—you just needed the form and the courier.
That's exactly how gRPC works. The "Request Form" is your Protocol Buffer (.proto) file. The "Pneumatic Tube" is HTTP/2. And the act of the clerk processing the request is the gRPC Service implementation in Go. Unlike a REST API where you send JSON (which is like sending a handwritten letter that might be misinterpreted), gRPC is strictly typed. If the proto file says a field is an integer, you cannot send a string. Period.
Defining the Handshake with Protocol Buffers
Before we write a single line of Go, we have to define the contract. I've found that most developers rush this part, but in gRPC, the .proto file is the single source of truth. Let's imagine we are building a Payment Processing Service.
syntax = "proto3";
package payment;
// The Go package path where the generated code will live
option go_package = "./pb";
service PaymentService {
// A simple Unary RPC: one request, one response
rpc ProcessPayment (PaymentRequest) returns (PaymentResponse) {}
}
message PaymentRequest {
string order_id = 1;
double amount = 2;
string currency = 3;
}
message PaymentResponse {
bool success = 1;
string transaction_id = 2;
}
Notice those numbers (= 1, = 2) next to the fields? Those aren't values; they are "tags." Because gRPC sends data in a binary format rather than text, it uses these tags to identify fields. This is why gRPC is so much faster and smaller than JSON—it doesn't have to send the words "order_id" over the wire every single time.
Turning the Interface into Action
Once you run the protoc compiler, Go generates an interface. Your job is to create a struct that satisfies that interface. I like to think of this as filling in the blanks of a template. You don't worry about the networking or the serialization; you just write the business logic.
type paymentServer struct {
pb.UnimplementedPaymentServiceServer
}
func (s *paymentServer) ProcessPayment(ctx context.Context, req *pb.PaymentRequest) (*pb.PaymentResponse, error) {
log.Printf("Processing payment for Order: %s", req.OrderId)
// In a real app, you'd hit a database or a payment gateway here
if req.Amount <= 0 {
return nil, status.Errorf(codes.InvalidArgument, "Amount must be greater than zero")
}
return &pb.PaymentResponse{
Success: true,
TransactionId: "txn_abc123",
}, nil
}
A quick tip: notice the status.Errorf. In gRPC, you don't just return a generic error string. You use specific gRPC codes (like InvalidArgument or NotFound). This allows the client to know exactly why the request failed without having to parse a text message.
Connecting the Client to the Wire
On the client side, you don't manually construct HTTP requests. You create a "stub"—a local object that looks and acts like the remote service. When you call a method on that stub, the gRPC library handles the magic of turning your Go struct into binary, sending it over HTTP/2, and waiting for the response.
conn, err := grpc.Dial("localhost:50051", grpc.WithInsecure())
if err != nil {
log.Fatalf("did not connect: %v", err)
}
defer conn.Close()
client := pb.NewPaymentServiceClient(conn)
resp, err := client.ProcessPayment(context.Background(), &pb.PaymentRequest{
OrderId: "order_99",
Amount: 49.99,
Currency: "USD",
})
One thing to keep in mind: because gRPC uses HTTP/2, it keeps the connection open. You aren't opening and closing a socket for every single single call. This is a huge performance win when you have hundreds of microservices talking to each other constantly.
📋 Practical Task
Build a Distributed Inventory Stock Checker
Your task is to implement a small gRPC system that checks if an item is in stock across different warehouses.
Requirements:
- The Contract: Create a
.protofile defining anInventoryService. It should have one RPC method calledCheckStock. The request should take anitem_id(string) and the response should returnin_stock(boolean) andquantity(int32). - The Server: Implement the server in Go. Instead of a database, use a simple
map[string]intto store a few hardcoded items (e.g., "laptop": 5, "mouse": 0). If anitem_idisn't in the map, return a gRPCNotFounderror. - The Client: Write a client that requests the stock for one existing item and one non-existent item, printing the results (or the error) to the console.
Goal: Successfully compile the proto file, start the server on port :50052, and verify that the client receives both a valid quantity and a NotFound error.
There are no comments for now.