Basic Go gRPC

VARDAAN MITTAL
Saltside Engineering
4 min readJul 14, 2022

Prerequisites

Before you can complete this tutorial, you will have to have the following installed on your machine:

  • Protocol Buffers v3 installed — this can be done by running go get -u github.com/golang/protobuf/protoc-gen-go

You will have to ensure that $GOPATH/bin is on your environment path so that you can use the protoc tool.

gRPC Introduction:

gRPC is a robust open-source RPC (Remote Procedure Call) framework used to build scalable and fast APIs. It allows the client and server applications to communicate transparently and develop connected systems. This framework relies on HTTP/2, protocol buffers, and other modern technology stacks to ensure maximum API security, performance, and scalability.

Differences between gRPC and REST

Whilst REST and gRPC are somewhat similar, there are some fundamental differences in how they work that you should be aware of.

  1. gRPC utilizes HTTP/2 whereas REST utilizes HTTP 1.1
  2. gRPC utilizes the protocol buffer data format as opposed to the standard JSON data format that is typically used within REST APIs
  3. With gRPC you can utilize HTTP/2 capabilities such as server-side streaming, client-side streaming or even bidirectional-streaming should you wish.

Strengths of gRPC:

  • Performance: gRPC offers up to 10x faster performance and API-security than REST+JSON communication as it uses Protobuf and HTTP/2. Protobuf serializes the messages on the server and client sides quickly, resulting in small and compact message payloads.
  • Streaming: gRPC supports client- or server-side streaming semantics, which are already incorporated in the service definition. This makes it much simpler to build streaming services or clients. A gRPC service supports different streaming combinations through HTTP/2:
  • Security: The use of HTTP/2 over the TLS end-to-end encryption connection in gRPC ensures API security
  • Interoperability: gRPC tools and libraries are designed to work with multiple platforms and programming languages, including Java, JavaScript, Ruby, Python, Go, Dart, Objective-C, C#, and more.

Challenges with gRPC

  • Non-human Readable Format: Protobuf compresses gRPC messages into a non-human readable format. This compiler needs the message’s interface description in the file to deserialize correctly. So, developers need additional tools like the gRPC command-line tool to analyze Protobuf payloads on the wire, write manual requests, and perform debugging.
  • No edge caching: While HTTP supports mediators for edge caching, gRPC calls use the POST method, which is a threat to API-security. The responses can’t be cached through intermediaries. Moreover, the gRPC specification doesn’t make any provisions and even indicates the wish for cache semantics between server and client.

Building a gRPC Server in Go

Let’s start by defining the chat.proto file which will act as our contract:

chat.proto

syntax = "proto3";
package chat;
message Message {
string body = 1;
}
service ChatService {
rpc SayHello(Message) returns (Message) {}
}

This .proto file exposes our ChatService which features a solitary SayHello function which can be called by any gRPC client written in any language.

These .proto definitions are typically shared across clients of all shapes and sizes so that they can generate their own code to talk to our gRPC server.

Let’s generate the Go specific gRPC code using the protoc tool:

$ protoc --go_out=plugins=grpc:chat chat.proto

You’ll see this will have generated a chat/chat.pb.go file which will contain generated code for us to easily call within our code.

server.go

package mainimport (
"fmt"
"log"
"net"
"github.com/tutorialedge/go-grpc-beginners-tutorial/chat"
"google.golang.org/grpc"
)
func main() { fmt.Println("Go gRPC Beginners Tutorial!") lis, err := net.Listen("tcp", fmt.Sprintf(":%d", 9000))
if err != nil {
log.Fatalf("failed to listen: %v", err)
}
s := chat.Server{} grpcServer := grpc.NewServer() chat.RegisterChatServiceServer(grpcServer, &s) if err := grpcServer.Serve(lis); err != nil {
log.Fatalf("failed to serve: %s", err)
}
}

We are then going to have to define the SayHello method which will take in a Message, read the Body of the message and then return a Message of it’s own:

chat/chat.go

package chatimport (
"log"
"golang.org/x/net/context"
)
type Server struct {
}
func (s *Server) SayHello(ctx context.Context, in *Message) (*Message, error) {
log.Printf("Receive message body from client: %s", in.Body)
return &Message{Body: "Hello From the Server!"}, nil
}

If we wanted to define more advanced functionality for our gRPC server then we could do so by defining a new method built off our Server struct and then add the name of that function to our chat.proto file so that our application can expose this as something that other gRPC clients can hit.

With these final changes in place, let’s try running our server:

$ go run server.go
Go gRPC Beginners Tutorial!

New gRPC server up and running on localhost:9000 on our machine!

Building a gRPC Client in Go

Now that we have our server up and running, let’s take a look at how we can build up a simple client that will be able to interact with it.

client.go

package mainimport (
"log"
"golang.org/x/net/context"
"google.golang.org/grpc"
"github.com/tutorialedge/go-grpc-beginners-tutorial/chat"
)
func main() { var conn *grpc.ClientConn
conn, err := grpc.Dial(":9000", grpc.WithInsecure())
if err != nil {
log.Fatalf("did not connect: %s", err)
}
defer conn.Close()
c := chat.NewChatServiceClient(conn) response, err := c.SayHello(context.Background(), &chat.Message{Body: "Hello From Client!"})
if err != nil {
log.Fatalf("Error when calling SayHello: %s", err)
}
log.Printf("Response from server: %s", response.Body)
}

When we go to run this, we should see that our client gets a very nice Hello message back from the server like so:

$ go run client.go
2020/04/30 20:10:09 Response from server: Hello From the Server!

--

--

VARDAAN MITTAL
Saltside Engineering

Golang developer specializing in microservices. Building robust, scalable backends and delving deep into concurrent system design.