grpc的Any类型在node中应用

Author Avatar
jiandandkl 8月 24, 2019
本文总阅读量
  • 在其它设备中阅读本文章

关于grpc就不介绍了,网上资料还是蛮多的,但是关于在node中的应用就比较少了,也不太友好

  1. 没有google.protobuf.Any类型
    无需编译proto文件,引用grpc和@grpc/proto-loader即可
const grpc = require('grpc')
const protoLoader = require('@grpc/proto-loader')
const path = require('path')

function createClient(
  { protoPath, packageName, serviceName, options },
  address,
  creds = grpc.credentials.createInsecure()
) {
  const pkgDef = grpc.loadPackageDefinition(protoLoader.loadSync(protoPath, options))
  const proto = getProtoFromPackageDefinition(pkgDef, packageName)
  return new proto[serviceName](address, creds)
}

function getProtoFromPackageDefinition(packageDefinition, packageName) {
  const pathArr = packageName.split('.')
  return pathArr.reduce((obj, key) => (obj && obj[key] !== 'undefined') ? obj[key] : undefined, packageDefinition)
}

const client = createClient({
  protoPath: path.resolve(__dirname, './xxx.proto'),
  // proto中package字段
  packageName: 'packageName',
  // proto中service方法
  serviceName: 'serviceName',
}, '10.0.0.0')

client.Report({
  // proto中定义的各字段
  type: 'HTTP',
  name: 'XXX',
  country: 'xxx',
  age: 20,
}, err => {
  if (err) {
    console.log(err)
  }
})
  1. 某个字段为google.protobuf.Any类型

@grpc/proto-loader已不支持,需要先将proto文件编译,使用的是grpc_tools_node_protoc_ts (也能编译d.ts文件)

hello.proto

syntax = "proto3";
package hello;

import "google/protobuf/any.proto";

// 类型
enum Type {
    UNKNOWN_TYPE = 0;

    HTTP = 1;

    TCP = 2;
}

// 单条消息体定义
message Request {

    Type type = 1;

    string name = 2;

    string country = 3;

    int32 age = 4;

    google.protobuf.Any extra = 5;
}

message HttpProto { 

    string header = 1;

    string url = 2;

    int32 code = 3;

}

// 响应消息体
message Response {
    int32 code = 1;
}

service ReportService {
    // 非流式gRPC请求
    rpc Report (Request) returns (Response) {
    }
}
const grpc = require('grpc')
const googleProtobufAnyPb = require('google-protobuf/google/protobuf/any_pb.js')
const services = require('./proto/hello_grpc_pb.js')
const messages = require('./proto/hello_pb.js')

const client = new services.ReportServiceClient(ip, grpc.credentials.createInsecure())

const request = new messages.Request()
// 可设置到Any类型extra的message
const requestHttp = new messages.HttpProto()
const anyPb = new googleProtobufAnyPb.Any()
// 获取message中的key
const keysHttp = requestHttp.toObject()

function grpcSend(data) {
    const { type } = data
    // typeUrl为Any类型对应的url,类似http中请求的url,规则为package + 对应的message
    let typeUrlAny = 'hello.HttpProto'

    Object.keys(data).forEach(key => {
        // 现有proto中extra字段为Any类型
        if (key === 'extra') {
         Object.keys(data.extra).forEach(keyAny => {
           if (keyAny in keysHttp) {
             // 先赋值requestHttp
             requestHttp[`set${upperCase(keyAny)}`](data.extra[keyAny])
           }
         })
         // 再将requestHttp序列化,并设置对应的typeUrl给anyPb
         anyPb.pack(requestHttp.serializeBinary(), typeUrlAny)
        } else {
         // 其他非Any类型,直接使用编译后文件中的如request.setName
         // 若为Type枚举类型,传对应的数字即可
         request[`set${upperCase(key)}`](data[key])
        }
    })
    // 最后将anyPb赋值给request
    request.setExtra(anyPb)
    // 调用rpc方法发送数据
    client.report(request, (err, res) => {
        if (err) {
          console.log('grpc error', err)
        }
  })
}

在github上传了一个抽取出来较为通用的示例(方便现有extra字段的扩展),仅供参考,grpc-any-node-demo