Introduction to gRPC

Kathy Tang
DeepQ Research Engineering Blog
4 min readDec 16, 2019

Introduction

  • gRPC clients and servers can run and talk to each other in a variety of environments and can be written in any of gRPC’s supported languages
    e.g. you can easily create a gRPC server in Java with clients in Go, Python, or Ruby.
  • define gRPC services in proto file

Install

$ pipenv install grpcio grpcio-tools

Example

  • define gRPC service in .proto files.
// hello.protosyntax = "proto3";message HelloRequest {    string value = 1;}message HelloResponse {    string value = 1;}service Hello {    rpc Hello(HelloRequest) returns (HelloResponse) {}}$ pipenv run python -m grpc_tools.protoc -I. --python_out=. --grpc_python_out=. hello.proto// generate hello_pb2.py & hello_pb2_grpc.py, define message class and service class respectively
  • gRPC uses protoc with special gRPC plugin to get generated gRPC client and server code

Server.py

  • The server implements the methods declared by the service and runs a gRPC server to handle client calls. The gRPC infrastructure decodes incoming requests, executes service methods, and encodes service responses.
#server.pyfrom concurrent import futuresimport timeimport grpcimport hello_pb2, hello_pb2_grpcclass HelloServicer(hello_pb2_grpc.HelloServicer):    def Hello(self, request, context):       response = hello_pb2.HelloResponse()       response.value = f"Hello {request.value}"         return response
def serve(): server = grpc.server(futures.ThreadPoolExecutor(max_workers=10)) hello_pb2_grpc.add_HelloServicer_to_server(HelloServicer(), server) server.add_insecure_port('[::]:50051') server.start() try: while True: time.sleep(86400) except KeyboardInterrupt: server.stop(0)
if __name__ == '__main__': serve()

client.py

# client.pyimport grpcimport hello_pb2import hello_pb2_grpcchannel = grpc.insecure_channel('localhost:50051')stub = hello_pb2_grpc.HelloStub(channel) # create a gRPC clientrequest = hello_pb2.HelloRequest(value="World") # create a request response = stub.Hello(request) # call Hello service, return HelloResponseprint(response.value)  # "Hello World"

Types of RPC

  • Unary RPCs
    The client sends a single request to the server and gets a single response back, just like a normal function call.
  • Server-side streaming RPC
    The client sends a request to the server and gets a stream to read a sequence of messages back. The client reads from the returned stream until there are no more messages.
  • Client-side streaming RPC
    The client writes a sequence of messages and sends them to the server, again using a provided stream. Once the client has finished writing the messages, it waits for the server to read them and return its response.
  • Bidirectional streaming RPC
    Both sides send a sequence of messages using a read-write stream. The two streams operate independently, so clients and servers can read and write at the same time.
service StreamService {    // Unary 
rpc SayHello(HelloRequest) returns (HelloResponse){};

// Server Streaming
rpc LotsOfReplies(HelloRequest) returns (stream HelloResponse) {};
// Client Streaming
rpc LotsOfGreetings(stream HelloRequest) returns (HelloResponse) {};
// Bidirectional streaming
rpc BidiHello(stream HelloRequest) returns (stream HelloResponse) {};
}

gRPC-web

  • gRPC-Web is an auxiliary protocol providing a translation layer between browser requests and gRPC.
  • Goal: Provide gRPC access to browser client
  • current limitations: unary calls and server streaming only
gRPC
gRPC-web

Install

$ npm install grpc-web

Steps:

  1. define gRPC service in .proto files.
syntax = "proto3";package helloworld;service Greeter {
rpc SayHello (HelloRequest) returns (HelloReply);
}
message HelloRequest {
string name = 1;
}
message HelloReply {
string message = 1;
}

2. Implement gRPC backend server

// server.jsvar PROTO_PATH = __dirname + '/helloworld.proto';

var grpc = require('grpc');
var protoLoader = require('@grpc/proto-loader');
var packageDefinition = protoLoader.loadSync(
PROTO_PATH,
{keepCase: true,
longs: String,
enums: String,
defaults: true,
oneofs: true
});
var protoDescriptor = grpc.loadPackageDefinition(packageDefinition);
var helloworld = protoDescriptor.helloworld;

function doSayHello(call, callback) {
callback(null, {
message: 'Hello! ' + call.request.name
});
}

function getServer() {
var server = new grpc.Server();
server.addService(helloworld.Greeter.service, {
sayHello: doSayHello,
});
return server;
}

if (require.main === module) {
var server = getServer();
server.bind('0.0.0.0:9090', grpc.ServerCredentials.createInsecure());
server.start();
}

exports.getServer = getServer;

3. Configure the Envoy Proxy (envoy.yaml)

Envoy

  • client calls need to be translated into gRPC-friendly calls, and the role is now filled by Envoy.
  • Envoy translates the HTTP/1.1 calls produced by the client into HTTP/2 calls that can be handled by those services (gRPC uses HTTP/2 for transport)
  • In general this is a pretty standard HTTP configuration for Envoy. There are just a few small differences:
    1.x-grpc-web, grpc-status, and grpc-messagein headers
    2. http_filters: — name: envoy.grpc_web

4. Generate client code using the protocol buffer compiler.

$ protoc -I=. helloworld.proto \
--js_out=import_style=commonjs:. \
--grpc-web_out=import_style=commonjs,mode=grpcwebtext:.

generate helloworld_pb.js and helloworld_grpc_web_pb.js

5. Use the gRPC-Web API to write a simple client for your service.

//client.jsconst {HelloRequest, HelloReply} = require('./helloworld_pb.js');
const {GreeterClient} = require('./helloworld_grpc_web_pb.js');
var client = new GreeterClient('http://localhost:8080');
var request = new HelloRequest();
request.setName('World');
client.sayHello(request, {}, (err, response) => {
console.log(response.getMessage());
});

6. compile the client javascript code

$ npm install
$ npx webpack client.js

Then, open up the developer console and you should see the following printed out:

Hello! World

Reference

  1. https://grpc.io/docs/guides/
  2. https://medium.com/@penolove15/%E8%AD%AF-grpc-basics-python-8bde8bbb4486
  3. https://codinganimal.info/grpc-tutorial-for-python-2fa0fe2ff853
  4. https://yami.io/grpc/
  5. https://www.youtube.com/watch?v=pzxy25ho5WY
  6. https://github.com/grpc/grpc-web/tree/master/net/grpc/gateway/examples/helloworld
  7. https://www.youtube.com/watch?v=RtyKEDZipsM
  8. https://blog.envoyproxy.io/envoy-and-grpc-web-a-fresh-new-alternative-to-rest-6504ce7eb880

--

--