preface

Before you start reading, understand the concepts. rpc, gprc, protocol buffer,bff

What is the GRPC
  • GRPC is a high-performance, open source and generic RPC framework designed for mobile and HTTP/2 with multi-language support. GRPC uses Protocol Buffers as its interface definition Language (IDL) and its underlying message exchange format
  • Remote Procedure Call (RPC) Remote Procedure Call. Basically, I’m calling a function locally, or a method on an object, and I’m actually calling a function on a remote machine, or a method on a remote object, but this communication is transparent to the programmer.

GRPC call process is shown in Figure 1:

What is a BFF

In simple terms, it is to establish corresponding back-end for different front-end devices. As shown in figure:

hello grpc

After resolving the conceptual issues, let’s take a look at how NodeJS implements GRPC Server and client calls. As shown in figure 1

Define the protobuf

hello.proto

syntax = "proto3"; Proto3 package greeter; /** Package Greeter contains two services: Service Hello {RPC SayHello (SayHelloRequest) returns (SayHelloResponse) {} } service SelfIntro { rpc IntroMyself (SelfIntroRequest) returns (SelfIntroResponse) {} } message SayHelloRequest { string name = 1; } message SayHelloResponse { string message = 1; } message SelfIntroRequest { } message SelfIntroResponse { string job = 1; }Copy the code

GRPC uses Protobuf in two ways. One is to generate code dynamically at run time using protobuf.js, and the other is to generate code statically (generating corresponding structures and methods) using a Protoc compiler. This example uses the former.

Load the protobuf
const grpc = require("grpc")
const protoLoader = require("@grpc/proto-loader")
const packageDescripter = protoLoader.loadSync(
    __dirname+ '/.. /hello.proto',
    {
        keepCase: true})const gretterPackage = grpc.loadPackageDefinition(packageDescripter).greeter
Copy the code
Server implementation
// ... load proto

/** * Implements the RPC method SayHello, IntroMyself * to start the service on port 50051 */

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

function IntroMyself(call, callback) {
    callback(null, {job: 'program enginner'})}function main() {
    const server = new grpc.Server()
    server.addService(gretterPackage.Hello.service, {
        SayHello,
    })
    server.addService(gretterPackage.SelfIntro.service, {
        IntroMyself,
    })

    server.bind('0.0.0.0:50051', grpc.ServerCredentials.createInsecure())
    server.start((a)= > {
        console.log('server runing on prot 50051')})}Copy the code
Client (stub) call
// ... load proto

/** * Create server stub * call remote method */
function main () {
    // Set the remote to localhost:50051

    const stubHello = new gretterPackage.Hello('localhost:50051', grpc.credentials.createInsecure())
    const name = "joe"

    stubHello.SayHello({name}, (err, response) => {
        if (err) { return console.error(err) }
        console.log('Greeting: ', response.message)
    })


    const stubIntro = new gretterPackage.SelfIntro('localhost:50051', grpc.credentials.createInsecure())

    stubIntro.IntroMyself({}, (err, response) => {
        if (err) { return console.error(err) }
        console.log('SelfIntro: my job is', response.job)
    })
}

Copy the code

So far we have experienced a complete GRPC call. Protobuf and Server implementations are typically defined by the back end. The Node layer creates the client, gets the RPC results, and provides them to the front end in a restful API

Ts bring effect

We use Proto to know which services are defined remotely, what PRC methods are available, and the call parameter and return value types. Because JS is a weakly typed language, in practical application, there is no way to verify the validity of parameters, nor can you provide completion prompts when writing code. Therefore, TS is introduced to solve the above problems.

However, GRPC only supports NodeJS. So you need to somehow generate a TS file from an Android Proto file

Solve those problems
  1. Code prompt completion
  2. The parameter checking
quick look

types.ts

// This file is auto generated by grpc-code-gen, do not edit!
// tslint:disable
export namespace greeter {
  export interface SayHelloRequest {
    'name'? :string;
  }


  export interface SayHelloResponse {
    'message'? :string;
  }


  export interface SelfIntroRequest {
  }


  export interface SelfIntroResponse {
    'job'? :string; }}Copy the code

greeter/Hello.ts

export interface IHello {
  $FILE_NAME: string;
  new (address: string, credentials: ChannelCredentials, options? : object): IHello;/** @deprecated Please use: SayHelloV2 */SayHello( request: types.greeter.SayHelloRequest, options? : { timeout? :number; flags? :number; host? :string; }
  ): Promise<types.greeter.SayHelloResponse>;
  /** @deprecated Please use: SayHelloV2 */SayHello( request: types.greeter.SayHelloRequest, metadata: MetadataMap, options? : { timeout? :number; flags? :number; host? :string; }
  ): Promise<types.greeter.SayHelloResponse>; SayHelloV2(option: { request: types.greeter.SayHelloRequest; metadata? : MetadataMap; options? : { timeout? :number; flags? :number; host? :string; }; }) :Promise<{ response:types.greeter.SayHelloResponse, metadata: Metadata }>;
}
export const hello: IHello = new greeter.Hello(`${serviceConfig.host}:${serviceConfig.port}`, credentials);
Copy the code

clinet.ts

import {hello} from "greeter/Hello.ts"
import * as types from "types"

export sayHello = (req: types.greeter.SayHelloRequest):  Promise<types.greeter.SayHelloResponse> => {
    return hello.SayHello(req)
}

sayHello({name: 'grpc'})
Copy the code