gRPC [Node.js] Master Class Build Modern API & Microservices

01 - gRPC Course Overview

01.01 gRPC Introduction












01.02 Course Objective



03 - [Theory] gRPC Internals Deep Dive

介绍 grpc 的,在 golang-grpc 有,这里不重复看了

04 - [Hands-On] gRPC Project Overview & Setup

04.03 Setup gRPC Node.js Project - Create Service and Code Generation

npm i -g request
npm i -g grpc --unsafe-perm
npm i -g grpc-tools --unsafe-perm

npm i google-protobuf
npm install @grpc/grpc-js
# 如果是yarn
yarn add global xxx

根据最新的 grpc 文档,提供了@grpc/grpc-js,以下是封装的 grpc 加载 proto 文件的代码

// proto/proto.js
const path = require("path");
const grpc = require("@grpc/grpc-js");
const protoLoader = require("@grpc/proto-loader");

module.exports = (name) => {
  const PROTO_PATH = path.join(__dirname, `${name}.proto`);

  const packageDefinition = protoLoader.loadSync(PROTO_PATH, {
    keepCase: true,
    longs: String,
    enums: String,
    defaults: true,
    oneofs: true,
  });
  const protoDescriptor = grpc.loadPackageDefinition(packageDefinition);

  return protoDescriptor;
};
// proto/Dummy.proto
syntax = "proto3";

package dummy;

message DummyMessage {

}

service DummyService {
}

04.04 Server Setup and Running It

// server.js
const grpc = require("@grpc/grpc-js");

function main() {
  var server = new grpc.Server();
  // Not implemented. Use bindAsync() instead (用bind报错,需要用bindAsync)
  server.bindAsync(
    "0.0.0.0:50051",
    grpc.ServerCredentials.createInsecure(),
    () => {
      server.start();
      console.log("grpc server started");
    }
  );
}

main();

04.05 Client Setup - Boilerplate Code


知乎,node-grpc

05 - [Hands-On] gRPC Unary

05.01 What’s an Unary API


05.02 Greet API Definition

syntax = "proto3";

package greet;
option go_package="./proto/greet";

message Greeting {
    string first_name = 1;
    string last_name = 2;
}

message GreetRequest {
    Greeting greeting = 1;
}

message GreetResponse {
    string result = 1;
}

service GreetService {
    // unary
    rpc Greet (GreetRequest) returns (GreetResponse) {}
}
grpc_tools_node_protoc --js_out=import_style=commonjs,binary:./ --grpc_out=grpc_js:./ proto/Greet.proto

05.03 Unary API Server Implementation

// server.js
var grpc = require("@grpc/grpc-js");
var greets = require("../proto/greet/Greet_pb");
var service = require("../proto/greet/Greet_grpc_pb");

function greet(call, callback) {
  var greeting = new greets.GreetResponse();

  greeting.setResult(
    // greeting(对象)里的数据存入request.array数组里
    "Hello " +
      call.request.getGreeting().getFirstName() +
      " " +
      call.request.getGreeting().getLastName()
  );

  callback(null, greeting);
}

function main() {
  var server = new grpc.Server();
  server.addService(service.GreetServiceClient.service, { greet: greet });
  // Not implemented. Use bindAsync() instead (用bind报错,需要用bindAsync)
  server.bindAsync(
    "0.0.0.0:50051",
    grpc.ServerCredentials.createInsecure(),
    () => {
      server.start();
      console.log("grpc server started");
    }
  );
}

main();

05.04 Unary API Client Implementation

// client.js
var grpc = require("@grpc/grpc-js");
var greets = require("../proto/greet/Greet_pb");
var service = require("../proto/greet/Greet_grpc_pb");

function main() {
  var client = new service.GreetServiceClient(
    "localhost:50051",
    grpc.credentials.createInsecure()
  );

  unary(client);
}

function unary(client) {
  var request = new greets.GreetRequest();
  var greeting = new greets.Greeting();

  greeting.setFirstName("Jerry");
  greeting.setLastName("Tom");

  request.setGreeting(greeting);

  client.greet(request, (error, response) => {
    if (!error) {
      console.log("Greeting Response: " + response.getResult());
    } else {
      console.error(error);
    }
  });
}

main();

05.05 Side Note - Install nodemon

05.07 [Solution] Sum API

// calculator.proto
syntax = "proto3";

package calculator;

message SumRequest {
    int32 first_number = 1;
    int32 second_number = 2;
}

message SumResponse {
    int32 result = 1;
}

service CalculatorService {
    // unary
    rpc Sum (SumRequest) returns (SumResponse) {};
}
// server.js
var grpc = require("@grpc/grpc-js");
var calc = require("../proto/calculator/calculator_pb");
var service = require("../proto/calculator/calculator_grpc_pb");

function sum(call, callback) {
  var sumResponse = new calc.SumResponse();

  sumResponse.setResult(
    call.request.getFirstNumber() + call.request.getSecondNumber()
  );

  callback(null, sumResponse);
}

function main() {
  var server = new grpc.Server();
  server.addService(service.CalculatorServiceService, { sum: sum });
  // Not implemented. Use bindAsync() instead (用bind报错,需要用bindAsync)
  server.bindAsync(
    "0.0.0.0:50051",
    grpc.ServerCredentials.createInsecure(),
    () => {
      server.start();
      console.log("grpc server started");
    }
  );
}

main();
// client.js
var grpc = require("@grpc/grpc-js");
var calc = require("../proto/calculator/calculator_pb");
var service = require("../proto/calculator/calculator_grpc_pb");

function main() {
  var client = new service.CalculatorServiceClient(
    "localhost:50051",
    grpc.credentials.createInsecure()
  );

  callSum(client);
}

function callSum(client) {
  var request = new calc.SumRequest();
  request.setFirstNumber(10);
  request.setSecondNumber(15);

  client.sum(request, (error, response) => {
    if (!error) {
      console.log("Sum Response: " + response.getResult());
    } else {
      console.error(error);
    }
  });
}

main();

06 - [OPTIONAL] Statically & Dynamically Code Generation


  目录