How to Supercharge Go Microservices: Replace HTTP/JSON with gRPC Using easyms.golang
This article walks through a complete performance upgrade of a Go microservice by defining protobuf contracts, generating gRPC code, running both HTTP and gRPC servers side‑by‑side, and using gRPC‑Gateway to keep REST compatibility, resulting in lower latency and a future‑ready architecture.
The original easyms.golang project used an API gateway that called the authentication service (auth‑svc) via HTTP/JSON, which became a performance hotspot as token verification scaled.
Step 1 – Define the protobuf contract
A auth.proto file is created under api/proto/auth describing the VerifyToken RPC, its request and response messages, and the service definition.
syntax = "proto3";
package auth;
option go_package = "easyms.golang/api/proto/auth";
message VerifyRequest {
string token = 1;
}
message VerifyResponse {
bool valid = 1;
// ... additional user info
}
service AuthService {
rpc VerifyToken(VerifyRequest) returns (VerifyResponse);
}Step 2 – Generate Go code from the .proto file
The protobuf compiler is invoked to produce Go structs and gRPC stubs:
protoc --proto_path=. \
--go_out=. --go_opt=paths=source_relative \
--go-grpc_out=. --go-grpc_opt=paths=source_relative \
api/proto/auth/auth.protoOn Windows, the required protoc binary can be downloaded from https://github.com/protocolbuffers/protobuf/releases/ and placed in the Go module cache. The command then includes additional include paths:
protoc -I . \
-I third_party/googleapis \
-I "D:/app/go/include" \
--go_out . --go_opt paths=source_relative \
--go-grpc_out . --go-grpc_opt paths=source_relative \
api/proto/auth/auth.protoStep 3 – Implement the gRPC server
A new file grpc_server.go implements the generated AuthServiceServer interface while reusing the existing TokenService business logic.
package service
import (
pb "easyms.golang/api/proto/auth"
// ... other imports
)
type grpcServer struct {
pb.UnimplementedAuthServiceServer
tokenService TokenService // reuse existing logic
}
func (s *grpcServer) VerifyToken(ctx context.Context, req *pb.VerifyRequest) (*pb.VerifyResponse, error) {
_, err := s.tokenService.GetOAuth2DetailsByAccessToken(req.Token)
if err != nil {
return &pb.VerifyResponse{Valid: false}, nil
}
return &pb.VerifyResponse{Valid: true}, nil
}Step 4 – Run both HTTP and gRPC servers
The service’s main.go is modified to start the gRPC server in a separate goroutine while the original Gin HTTP server continues to run in the main goroutine.
func main() {
// ... existing DI setup
// Start gRPC server (goroutine)
go func() {
grpcPort := appConfig.Server.Port + 10000 // gRPC port
lis, err := net.Listen("tcp", fmt.Sprintf(":%d", grpcPort))
// error handling omitted for brevity
s := grpc.NewServer()
pb.RegisterAuthServiceServer(s, service.NewGrpcServer(...))
s.Serve(lis)
}()
// Start HTTP server (main goroutine)
g := gin.Default()
// ... route registration
g.Run(...)
}Step 5 – Upgrade the API gateway to use gRPC
A gRPC client is created in gateway.go (via grpc.Dial) and the existing HTTP call to auth‑svc is replaced with a gRPC call inside AuthMiddleware:
func (g *Gateway) AuthMiddleware(next http.Handler) http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
// token extraction omitted
if _, found := g.authCache.Get(tokenStr); found {
next.ServeHTTP(w, r)
return
}
// gRPC verification
resp, err := g.authSvcClient.VerifyToken(r.Context(), &pb.VerifyRequest{Token: tokenStr})
if err != nil || !resp.Valid {
// error handling
return
}
g.authCache.Set(tokenStr, true, 5*time.Minute)
next.ServeHTTP(w, r)
})
}Step 6 – Expose a RESTful endpoint with gRPC‑Gateway
By adding google.api.http annotations to the .proto file and running the grpc‑gateway plugin, an HTTP POST endpoint /v1/auth/token is automatically mapped to the gRPC method.
import "google/api/annotations.proto";
service AuthService {
rpc GrantToken(TokenRequest) returns (TokenResponse) {
option (google.api.http) = {
post: "/v1/auth/token"
body: "*"
};
}
}The generated gateway handler is mounted on the Gin router, giving the service both a modern gRPC API and a backward‑compatible REST API that share the same business logic.
Result
Performance boost : Core token verification now uses gRPC/Protobuf, reducing latency and CPU overhead.
Unified contract : Protobuf becomes the single source of truth for service interfaces.
Backward compatibility : Existing HTTP clients continue to work unchanged.
Gradual migration : The pattern can be applied service‑by‑service without a full rewrite.
Source code for the complete implementation is available at:
GitHub: https://github.com/louis-xie-programmer/easyms.golang
Gitee: https://gitee.com/louis_xie/easyms.golang
Signed-in readers can open the original source through BestHub's protected redirect.
This article has been distilled and summarized from source material, then republished for learning and reference. If you believe it infringes your rights, please contactand we will review it promptly.
Code Wrench
Focuses on code debugging, performance optimization, and real-world engineering, sharing efficient development tips and pitfall guides. We break down technical challenges in a down-to-earth style, helping you craft handy tools so every line of code becomes a problem‑solving weapon. 🔧💻
How this landed with the community
Was this worth your time?
0 Comments
Thoughtful readers leave field notes, pushback, and hard-won operational detail here.
