gRPC is a high-performance RPC framework that uses HTTP/2 for transport and Protocol Buffers for serialization. This guide walks through creating a functional gRPC server and client in Go.
Prerequisites
Before starting, install the following tools:
Protocol Buffer Compiler
macOS:
brew install protobuf
Linux:
apt install -y protobuf-compiler
Verify installation:
protoc --version
# Should output: libprotoc 3.x.x or higher
Go Protocol Buffer Plugins
go install google.golang.org/protobuf/cmd/protoc-gen-go@latest
go install google.golang.org/grpc/cmd/protoc-gen-go-grpc@latest
Add the Go bin directory to your PATH if not already present:
export PATH="$PATH:$(go env GOPATH)/bin"
Initialize Go Module
mkdir go-grpc
cd go-grpc
go mod init edgarmontano.com/go-grpc
Project Structure
go-grpc/
├── proto/
│ └── hello.proto
├── server/
│ └── main.go
├── client/
│ └── main.go
└── go.mod
Defining the Service with Protocol Buffers
Create proto/hello.proto:
syntax = "proto3";
option go_package = "edgarmontano.com/go-grpc/proto";
package hello;
// The Hello service definition.
service Hello {
// Sends a greeting
rpc SayHello (HelloRequest) returns (HelloReply) {}
}
// The request message containing the user's name.
message HelloRequest {
string name = 1;
}
// The response message containing the greetings
message HelloReply {
string message = 1;
}
The proto file defines:
- A
Helloservice with one RPC methodSayHello - Request message
HelloRequestcontaining a string field - Response message
HelloReplycontaining a string field - The
go_packageoption specifies where generated code will be placed
Generating Go Code
Run the protocol buffer compiler to generate Go code:
protoc --go_out=. --go_opt=paths=source_relative \
--go-grpc_out=. --go-grpc_opt=paths=source_relative \
proto/hello.proto
This creates two files in the proto/ directory:
hello.pb.go- Contains message type definitionshello_grpc.pb.go- Contains service client and server code
Server Implementation
Create server/main.go:
package main
import (
"context"
"log"
"net"
pb "edgarmontano.com/go-grpc/proto"
"google.golang.org/grpc"
)
// server implements the Hello service
type server struct {
pb.UnimplementedHelloServer
}
// SayHello implements the SayHello RPC
func (s *server) SayHello(ctx context.Context, req *pb.HelloRequest) (*pb.HelloReply, error) {
log.Printf("Received: %v", req.GetName())
return &pb.HelloReply{Message: "Hello " + req.GetName()}, nil
}
func main() {
lis, err := net.Listen("tcp", ":50051")
if err != nil {
log.Fatalf("failed to listen: %v", err)
}
s := grpc.NewServer()
pb.RegisterHelloServer(s, &server{})
log.Printf("server listening at %v", lis.Addr())
if err := s.Serve(lis); err != nil {
log.Fatalf("failed to serve: %v", err)
}
}
Server Components
Embedding UnimplementedHelloServer:
The pb.UnimplementedHelloServer provides forward compatibility. If new methods are added to the service definition, the server continues to compile and returns “unimplemented” errors for methods not explicitly implemented.
SayHello Method:
Implements the RPC defined in the proto file. Receives a HelloRequest, logs the name, and returns a HelloReply with a greeting message.
TCP Listener: The server listens on port 50051. This port can be changed based on your infrastructure requirements.
Server Registration:
pb.RegisterHelloServer registers the service implementation with the gRPC server, making the RPC methods available to clients.
Client Implementation
Create client/main.go:
package main
import (
"context"
"log"
"time"
pb "edgarmontano.com/go-grpc/proto"
"google.golang.org/grpc"
"google.golang.org/grpc/credentials/insecure"
)
func main() {
conn, err := grpc.NewClient("localhost:50051", grpc.WithTransportCredentials(insecure.NewCredentials()))
if err != nil {
log.Fatalf("did not connect: %v", err)
}
defer conn.Close()
// Create a client
c := pb.NewHelloClient(conn)
// Call the SayHello method
ctx, cancel := context.WithTimeout(context.Background(), time.Second)
defer cancel()
r, err := c.SayHello(ctx, &pb.HelloRequest{Name: "World"})
if err != nil {
log.Fatalf("could not greet: %v", err)
}
log.Printf("Greeting: %s", r.GetMessage())
}
Client Components
Connection Setup:
grpc.NewClient establishes a connection to the server. The insecure.NewCredentials() option disables transport security (TLS). In production, use proper TLS credentials.
Client Creation:
pb.NewHelloClient creates a client stub that provides methods corresponding to the service RPCs.
Context with Timeout: The context includes a 1-second timeout. If the RPC doesn’t complete within this time, it returns a deadline exceeded error.
RPC Invocation:
Calls SayHello with a request message. The response is returned synchronously.
Installing Dependencies
go get google.golang.org/grpc
go get google.golang.org/protobuf
Run go mod tidy to clean up dependencies:
go mod tidy
Running the Application
Start the Server
go run server/main.go
Output:
server listening at [::]:50051
Run the Client
In a separate terminal:
go run client/main.go
Output:
Greeting: Hello World
The server terminal will show:
Received: World
Error Handling Considerations
The current implementation includes basic error handling. For production systems, consider:
- Connection retry logic - Implement exponential backoff for client connections
- Server-side validation - Validate request fields before processing
- Proper logging - Use structured logging with appropriate log levels
- Health checks - Implement gRPC health checking protocol
- Graceful shutdown - Handle OS signals to cleanly close connections
- Rate limiting - Protect server resources from excessive requests
Security in Production
The example uses insecure credentials for simplicity. Production deployments require:
// Server with TLS
creds, err := credentials.NewServerTLSFromFile("server.crt", "server.key")
if err != nil {
log.Fatalf("failed to load TLS keys: %v", err)
}
s := grpc.NewServer(grpc.Creds(creds))
// Client with TLS
creds, err := credentials.NewClientTLSFromFile("ca.crt", "")
if err != nil {
log.Fatalf("failed to load TLS cert: %v", err)
}
conn, err := grpc.NewClient("localhost:50051", grpc.WithTransportCredentials(creds))
Performance Characteristics
gRPC offers several advantages for service-to-service communication:
- Binary Protocol - Protocol Buffers serialization is faster than JSON
- HTTP/2 - Multiplexes multiple requests over a single connection
- Streaming Support - Enables bidirectional streaming for real-time communication
- Code Generation - Generates client and server code from proto definitions, reducing boilerplate