A simple unary gRPC demo in Go and C#
Switch branches/tags
Nothing to show
Clone or download
Latest commit 94be7fc Dec 15, 2016
Permalink
Type Name Latest commit message Commit time
Failed to load latest commit information.
csharp.client clean up Dec 13, 2016
dotnetcore.client dotnetcore client Dec 13, 2016
go.client go client Dec 13, 2016
go.server go server Dec 13, 2016
proto gen proto go file Dec 13, 2016
.gitignore Initial commit Dec 13, 2016
LICENSE Initial commit Dec 13, 2016
README.md details Dec 15, 2016

README.md

grpc.demo

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#

Table of contents

gRPC

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:

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

A gRPC example

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.

Installation process

To use gRPC you will have to install grpc, protobuf and protoc-gen-go.

  • install grpc
    • go get google.golang.org/grpc
  • install protocol buffers
  • install the go compiler plugin protoc-gen-go
    • 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

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.

gRPC Go 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)
	}
}

gRPC Go client

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

gRPC csharp client

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/Grp
c.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...

gRPC dotnet core client:

The dotnet core client was simple to build as the generated code is the same as for c#.

This is what I did:

  • create dotnet core project
  • add grpc dependencies

Include this in your package.json file:

"dependencies": {    
  "Google.Protobuf": "3.0.0",
  "Grpc": "1.0.1",
},
  • generate csharp files (same as before).
  • create the client (you can use the same client code as before)
  • build
  • run

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...

What else:

There are some more things I would like to try.

  • streaming gRPC calls
  • deploy a demo app to Google Cloud Pub/Sub to see how the deployment experience is.

Resources:

Follow me at @santiago_arias to be notified about more posts like this.