How do you write a Golang server using gRPC
from scratch? Heres how to do it.
Introduction
Before we begin, I’d like to mention that the code below was taken from the /example
folder in the gRPC
package.
As a textile learner, I like to write things out even if provided - I don’t feel confident being handed code and be expected to learn - I learn better by writing it from scratch; as a result, the gRPC
documentation has not been good to me, to remedy this issue, I’ve written this article to help me understand better by writing out the steps from scratch.
Instructions
Let’s begin by going to our golang
home directory.
$ cd ~/go/src/github.com/bartmika
Create our project and initialize our module.
$ mkdir simple-grpc
$ cd simple-grpc
$ go mod init github.com/bartmika/simple-grpc
Get our project dependencies
$ export GO111MODULE=on # Enable module mode
$ go get google.golang.org/protobuf/cmd/protoc-gen-go
$ go get google.golang.org/grpc/cmd/protoc-gen-go-grpc
Before we begin, make sure we export our path (if you haven’t done this before).
$ export PATH="$PATH:$(go env GOPATH)/bin"
To get started with gRPC
we will need to setup our proto
file. The file is responsible for defining the interface of our project. Proto is a short form for protocol buffers - Would you like to know more?
$ mkdir proto
$ vi proto/greeter.proto
Copy and paste the following code into the file you have open.
syntax = "proto3";
option go_package = "github.com/bartmika/simple-server"; // Don't forget to change!
package helloworld;
// The greeting service definition.
service Greeter {
// 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;
}
Now that we defined our interface, we’ll need to generate our client and server files.
$ protoc --go_out=. --go_opt=paths=source_relative \
--go-grpc_out=. --go-grpc_opt=paths=source_relative \
proto/greeter.proto
We have our server and client code generated. We can now write our own golang
code to utilize them. Start by creating our server.
$ mkdir -p cmd/server
$ touch cmd/server/main.go
Copy and paste the following code into our server:
package main
import (
"context"
"log"
"net"
"google.golang.org/grpc"
pb "github.com/bartmika/simple-grpc/proto"
)
const (
port = ":50051"
)
// server is used to implement helloworld.GreeterServer.
type server struct {
pb.UnimplementedGreeterServer
}
// SayHello implements helloworld.GreeterServer
func (s *server) SayHello(ctx context.Context, in *pb.HelloRequest) (*pb.HelloReply, error) {
log.Printf("Received: %v", in.GetName())
return &pb.HelloReply{Message: "Hello " + in.GetName()}, nil
}
func main() {
lis, err := net.Listen("tcp", port)
if err != nil {
log.Fatalf("failed to listen: %v", err)
}
s := grpc.NewServer()
pb.RegisterGreeterServer(s, &server{})
if err := s.Serve(lis); err != nil {
log.Fatalf("failed to serve: %v", err)
}
}
Next create our client file.
$ mkdir -p cmd/client
$ touch cmd/client/main.go
Copy and paste the following code into our server:
package main
import (
"context"
"log"
"os"
"time"
"google.golang.org/grpc"
pb "github.com/bartmika/simple-grpc/proto"
)
const (
address = "localhost:50051"
defaultName = "world"
)
func main() {
// Set up a connection to the server.
conn, err := grpc.Dial(address, grpc.WithInsecure(), grpc.WithBlock())
if err != nil {
log.Fatalf("did not connect: %v", err)
}
defer conn.Close()
c := pb.NewGreeterClient(conn)
// Contact the server and print out its response.
name := defaultName
if len(os.Args) > 1 {
name = os.Args[1]
}
ctx, cancel := context.WithTimeout(context.Background(), time.Second)
defer cancel()
r, err := c.SayHello(ctx, &pb.HelloRequest{Name: name})
if err != nil {
log.Fatalf("could not greet: %v", err)
}
log.Printf("Greeting: %s", r.GetMessage())
}
Finally, run the server and client in seperate terminals via the commands:
$ go run cmd/server
$ go run cmd/client "Bart"