I will be using Golang in this post. Let’s install the software needed. There are equivalent commands to try on Raspberry-PI.
Multipass
Create a new instance grpc
and open a shell into this instance using multipass as follows:
$ multipass launch -n grpc
$ multipass shell grpc
...
...
ubuntu@grpc:~$
Install Golang
Download latest tar for your platform from official site
wget https://dl.google.com/go/go1.14.4.linux-amd64.tar.gz (Ubuntu x86-64)
wget https://dl.google.com/go/go1.14.3.linux-armv6l.tar.gz (Raspberry PI)
Untar the tar file to /usr/local
sudo tar -C /usr/local -xzf go1.14.4.linux-amd64.tar.gz (Ubuntu x86-64)
sudo tar -C /usr/local -xzf go1.14.3.linux-armv6l.tar.gz (Raspberry PI)
rm go1.14.4.linux-amd64.tar.gz (Ubuntu x86-64)
rm go1.14.3.linux-armv6l.tar.gz (Raspberry PI)
Set Golang paths in ~/.profile
export PATH=$PATH:/usr/local/go/bin
export GOPATH=$HOME/golang
Build directory to work
mkdir golang
mkdir golang/src
Logout and login back to shell to verify Go is installed.
ubuntu@grpc:~$ go version
go version go1.14.4 linux/amd64
Google Protocol Buffers
Google Protocol buffer
Installation
To install protobuf
In order to generated code from protocol buffers definition files, do the following:
- Download and install protoc compiler from official website. From the download section take
protoc-$VERSION-osx.zip
. For example:
- For Golang users on OSX :
protoc-3.11.4-osx-x86_64.zip
- For Golang users on Linux:
protoc-3.12.3-linux-x86_64.zip
ubuntu@grpc:~$ wget https://github.com/protocolbuffers/protobuf/releases/download/v3.12.3/protoc-3.12.3-linux-x86_64.zip
ubuntu@grpc:~$ PROTOC_ZIP=protoc-3.12.3-linux-x86_64.zip
ubuntu@grpc:~$ sudo unzip -o $PROTOC_ZIP -d /usr/local bin/protoc
ubuntu@grpc:~$ sudo unzip -o $PROTOC_ZIP -d /usr/local 'include/*'
ubuntu@grpc:~$ rm -f $PROTOC_ZIP
- Add the location of protoc binary file into PATH environment variable so that you can invoke protoc compiler from any location. Also, you may have to add execute permission for all.
ubuntu@grpc:~$ export PATH=$PATH:/usr/local/bin/
ubuntu@grpc:~$ sudo chmod a+x /usr/local/bin/protoc
- Install the protoc plugin for your language. For Go, run the go get command to install the protoc plugin for Go:
go get -u github.com/golang/protobuf/proto
go get -u github.com/golang/protobuf/protoc-gen-go
Make sure that $GOPATH/bin is on your environment path so that you can use the protoc tool.
export PATH=$PATH:$GOPATH/bin
GRPC
Introduction
gRPC is a modern, open source remote procedure call (RPC) framework that can run anywhere.
RPC has been there from long time, used for something that we use within distributed systems that allow us to communicate between applications. More specifically, it allows us to expose methods within our application that we want other applications to be able to invoke.
It is similar to REST API that exposes functionality within app to other apps using a HTTP connection. Though REST and gRPC are similar, there are some basic differences as well:
- gRPC uses HTTP/2 vs. REST which uses HTTP 1.1
- gRPC uses the protocol buffer data format vs. JSON data format that typically used within REST APIs
- With gRPC you can use HTTP/2 capabilities
- server-side streaming
- client-side streaming
- bidirectional-streaming
Building GRPC server in Golang
Step 1: To being, here is a simple server Go program that listens on TCP Port (e.g. 9000) for incoming connections.
gprc-demo/server.go
package main
import (
"log"
"net"
)
func main() {
_, err := net.Listen("tcp", ":9000")
if err != nil {
log.Fatalf("failed to listen: %v", err)
}
}
Step 2: Import grpc package from golang.org to create a gRPC server and start serving.
gprc-demo/server.go
package main
import (
"log"
"net"
"google.golang.org/grpc"
)
func main() {
lis, err := net.Listen("tcp", ":9000")
if err != nil {
log.Fatalf("failed to listen: %v", err)
}
grpcServer := grpc.NewServer()
if err := grpcServer.Serve(lis); err != nil {
log.Fatalf("failed to serve: %s", err)
}
}
Note, you have to get grpc package if not already present.
ubuntu@grpc: go get google.golang.org/grpc
Step 3: Add Functionality
Let’s add a chat service to this server. Service is defined in a chat.proto file using protobuf, exposing single service ChatService to be called by gRPC client.
gprc-demo/chat.proto
syntax = "proto3";
package chat;
message Message {
string body = 1;
}
service ChatService {
rpc SayHello(Message) returns (Message) {}
}
Step 4: Generate Go specific gRPC code for service using the protoc tool
ubuntu@grpc:~/golang/src/gprc-demo$ mkdir chat
ubuntu@grpc:~/golang/src/gprc-demo$ protoc --go_out=plugins=grpc:chat chat.proto
ubuntu@grpc:~/golang/src/gprc-demo$ ls -R
.:
chat chat.proto server.go
./chat:
chat.pb.go
Step 5: Use the generated code in server.go as follows:
- Implement Chat interface
type ChatServer struct {
}
func (s *ChatServer) SayHello(ctx context.Context, in *chat.Message) (*chat.Message, error) {
log.Printf("Receive message body from client: %s", in.Body)
return &chat.Message{Body: "Hello From the Server!"}, nil
}
- Use the chat service with grpcSever
// register chat service with gRPCServer
chat.RegisterChatServiceServer(grpcServer, &ChatServer{})
Final server code is:
gprc-demo/server.go
package main
import (
"fmt"
"log"
"net"
"golang.org/x/net/context"
"google.golang.org/grpc"
"gprc-demo/chat"
)
type ChatServer struct {
}
func (s *ChatServer) SayHello(ctx context.Context, in *chat.Message) (*chat.Message, error) {
log.Printf("Receive message body from client: %s", in.Body)
return &chat.Message{Body: "Hello From the Server!"}, nil
}
func main() {
fmt.Println("Go GRPC Tutorial!")
lis, err := net.Listen("tcp", ":9000")
if err != nil {
log.Fatalf("failed to listen: %v", err)
}
grpcServer := grpc.NewServer()
// register chat service with gRPCServer
chat.RegisterChatServiceServer(grpcServer, &ChatServer{})
if err := grpcServer.Serve(lis); err != nil {
log.Fatalf("failed to serve: %s", err)
}
}
Step 6: Implement the client
After testing that server is up and running, let’s write the client code.
gprc-demo/client.go
package main
import (
"log"
"golang.org/x/net/context"
"google.golang.org/grpc"
"gprc-demo/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)
}
Step 8: Let’s have fun
Open two terminals and run server in one terminal followed by client in another window. Outputs should look like this:
Server
ubuntu@grpc:~/golang/src/gprc-demo$ go run server.go
Go GRPC Tutorial!
2020/06/21 13:20:17 Receive message body from client: Hello From Client!
Client
ubuntu@grpc:~/golang/src/gprc-demo$ go run client.go
2020/06/21 13:20:17 Response from server: Hello From the Server!
We now have a working gRPC server and client example, which can be extended to your imagination.
That’s all for now :-)