I heard about gRPC some months ago and decided to learn a bit about it. Here is a collection of the information I found about it as well as a simple gRPC demo that uses Go and C#.
I created a github repo grpc.demo with all the code in this article.
gRPC is an open source RPC library from google. It is an alternative to REST for microservices. It is based on the HTTP2 standard, and uses protocol buffers (Proto3).
gRPC is available in many languages, some of them have there own implementation (C, Go, Java) and some a wrapper around the C implementation so you are not tied to any language.
gRPC extends the Go programming model over the network.It is an excellent fit for building parallel, distributed, and streaming systems.Sameer Ajmani
gRPC aims to be more efficient than JSON/HTTP. It encodes data with more efficiency thanks to Protocol Buffers and HTTP/2 makes the transport faster.
You can use Unary RPC (request, response) or Streaming RPC (send one or more messages).
To learn more about the motivations behind it you should read this web page: gRPC Motivation and Design Principles
Protocol buffers are used to define a mechanism to serialize structured data. You define the structure of the data (messages) and a service that you want to use to communicate. Then generate the source code for the message(s) and service(s) you defined to use in the server or client.
Multiple applications written in different programming languages can exchange a large number of messages quickly and reliably without overloading the network. Practical guide to protocol buffers
I wanted to build a simple example to test the Unary RPC use case. Test how the exchange between different programming languages worked/felt.
I decided to create a Go server and a Go client to start. Then extend it to a C# client that calls the same Go server as before. And finally try a dotnet core client as well.
To use gRPC you will have to install grpc
, protobuf
and protoc-gen-go
.
go get google.golang.org/grpc
go get -u github.com/golang/protobuf/protoc-gen-go
protoc-gen-go
is a compiler to generate Go code from a proto file.The proto file is where you define the gRPC service and the messages that will be used to communicate.
syntax = "proto3";
package reverse;
service ReverseService {
rpc ReverseString (ReverseRequest) returns (ReverseReply) {}
}
message ReverseRequest {
string data = 1;
}
message ReverseReply {
string reversed = 2;
}
To generate the gRPC code run the following command.
> ...\grpc.demo> protoc -I .\proto\ .\proto\reverse.proto --go_out=plugins=grpc:proto
This generates a reverse.pb.go
file that holds the ReverseRequest
and ReverseReply
messages types as well as the ReverseService
client and server.
Create a server
type that implements the ReverseString
function and make the server serve the gRPC service:
package main
import (
"log"
"net"
pb "github.com/santiaago/grpc.demo/proto"
"golang.org/x/net/context"
"google.golang.org/grpc"
"google.golang.org/grpc/reflection"
)
const (
port = ":50051"
)
type server struct{}
func (s *server) ReverseString(ctx context.Context, in *pb.ReverseRequest) (*pb.ReverseReply, error) {
// your reverse string implementation here
return &pb.ReverseReply{Reversed: reversed}, nil
}
func main() {
lis, err := net.Listen("tcp", port)
if err != nil {
log.Fatalf("failed to listen: %v", err)
}
s := grpc.NewServer()
pb.RegisterReverseServiceServer(s, &server{})
reflection.Register(s)
if err := s.Serve(lis); err != nil {
log.Fatalf("failed to server: %v", err)
}
}
Create a Go client that dials the gRPC server. Get the client object and call ReverseString
on it.
package main
import (
"log"
pb "github.com/santiaago/grpc.demo/proto"
"golang.org/x/net/context"
"google.golang.org/grpc"
)
const (
address = "localhost:50051"
)
func main() {
conn, err := grpc.Dial(address, grpc.WithInsecure())
if err != nil {
log.Fatalf("did not connect: %v", err)
}
defer conn.Close()
c := pb.NewReverseServiceClient(conn)
r, err := c.ReverseString(context.Background(), &pb.ReverseRequest{
Data: "Hello, world",
})
if err != nil {
log.Fatalf("could not greet: %v", err)
}
log.Println("Reversed string: ", r.Reversed)
}
Output:
server:
...\go.server> go run .\main.go
2016/12/13 14:28:10 transport: http2Server.HandleStreams failed to read frame: read tcp [::1]:50051->[::1]:1195: wsarecv: An existing connection was forcibly closed by the remote host.
client:
...\go.client> go run .\main.go
2016/12/13 14:29:20 Reversed string: dlrow ,olleH
On a c# project install Grpc.Core
, Grpc.Tools
and Google.Protobuf
from NuGet.
Then generate the c# classes using protoc.exe
and grpc_csharp_plugin.exe
...\csharp.client> .\packages\Grpc.Tools.1.0.1\tools\windows_x86\protoc.exe -I..\proto --csharp_out . --grpc_out . ..\proto\reverse.proto --plugin=protoc-gen-grpc=packages/Grpc.Tools.1.0.1/tools/windows_x86/grpc_csharp_plugin.exe
This generates Reverse.cs
and ReverseGrpc.cs
files. Include them in your project.
You can now create a client that calls the Go server
using System;
using Grpc.Core;
using Reverse;
namespace csharp.client
{
internal class Program
{
private static void Main()
{
var channel = new Channel("127.0.0.1:50051", ChannelCredentials.Insecure);
var client = new ReverseService.ReverseServiceClient(channel);
var reply = client.ReverseString(new ReverseRequest
{
Data = "Hello, World"
});
Console.WriteLine("Got: " + reply.Reversed);
channel.ShutdownAsync().Wait();
Console.WriteLine("Press any key to exit...");
Console.ReadKey();
}
}
}
Output
Got: dlroW ,olleH
Press any key to exit...
The dotnet core client was simple to build as the generated code is the same as for c#.
This is what I did:
Include this in your package.json
file:
"dependencies": {
"Google.Protobuf": "3.0.0",
"Grpc": "1.0.1",
},
Output:
...\dotnetcore.client> dotnet run
Project dotnetcore.client (.NETCoreApp,Version=v1.0) was previously compiled. Skipping compilation.
Got: dlroW ,olleH
Press any key to exit...
There are some more things I would like to try in the near future.
Follow me at @santiago_arias to be notified about more posts like this.
Santiaago