Практика grpc-node в bff(on)

gRPC

предисловие

Прежде чем приступить к чтению, ознакомьтесь с понятиями. rpc, gprc, буферы протоколов, bff

что такое грпс
  • gRPC — это высокопроизводительная платформа RPC общего назначения с открытым исходным кодом, разработанная для мобильных устройств и HTTP/2 с многоязычной поддержкой. gRPC использует буферы протокола в качестве языка определения интерфейса (IDL) и базового формата обмена сообщениями.
  • RPC (удаленный вызов процедур) удаленный вызов процедур. Проще говоря, я вызываю функцию локально или метод объекта и фактически вызываю функцию на удаленной машине или метод удаленного объекта, но этот процесс связи прозрачен для программиста.

Процесс вызова grpc показан на рисунке 1:

grpc-call

что такое лучший друг

Самый простой способ — установить соответствующие серверные части для разных интерфейсных устройств. Как показано на рисунке:

Using one server-side BFF per user interface

hello grpc

После решения концептуальной проблемы давайте посмотрим, как nodejs реализует серверные и клиентские вызовы grpc. фигура 1

определить протобуф

hello.proto

syntax = "proto3"; // 语法proto3

package greeter; // 包名

/**
package greeter 包含两个service:Hello和 SelfIntro
message 定义了rpc方法参数和返回值的结构
*/

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;
}

У grpc есть два способа использования protobuf. Один из них — использовать Protobuf.js для динамической генерации кода во время выполнения, а другой — использовать компилятор protoc для генерации статического кода (для создания соответствующих структур и методов). В этом примере используется первый.

загрузить протобуф
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
реализация сервера
// ... load proto

/**
 * 实现rpc方法 SayHello,IntroMyself
 * 在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(() => {
        console.log('server runing on prot 50051')
    })
}
Клиентский (заглушка) вызов
// ... load proto

/**
 * 创建服务端存根(stub)
 * 调用远程方法
 */
function main () {
    // 指定远端为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)
    })
}

До сих пор мы испытывали полный процесс вызова grpc. Обычно определение реализации protobuf и сервера выполняется бэкендом. Уровень узла создает клиента, получает результат rpc и предоставляет его внешнему интерфейсу в виде спокойного API.

эффективность

Мы используем proto, чтобы узнать, какие сервисы определены на удаленном конце, какие существуют методы prc, а также типы параметров вызова и возвращаемых значений. Поскольку js — слабо типизированный язык, в практических приложениях нет возможности проверить правильность параметров, и больше нельзя предоставлять подсказки о завершении при написании кода. С этой целью введение ТС для решения вышеуказанных проблем.

Однако grpc поддерживает только nodejs. Для этого требуется каким-то образом сгенерировать соответствующий ts-файл из прото-файла Android.

решить эти проблемы
  1. завершение подсказки кода
  2. проверка параметров
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;
  }


}

greeter/Hello.ts

export interface IHello {
  $FILE_NAME: string;
  new (address: string, credentials: ChannelCredentials, options?: object): IHello;

  /** @deprecated 请使用: SayHelloV2 */
  SayHello(
    request: types.greeter.SayHelloRequest,
    options?: { timeout?: number; flags?: number; host?: string; }
  ): Promise<types.greeter.SayHelloResponse>;
  /** @deprecated 请使用: 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);

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'})