typescript grpc server client 一问一答 简单工具类(亲测可用,带注释、测试类)

  • 工具类结构
    typescript grpc server client 一问一答 简单工具类(亲测可用,带注释、测试类)

  • 目录结构
    typescript grpc server client 一问一答 简单工具类(亲测可用,带注释、测试类)

  • package.json 依赖

主要是proto-loader grpc 缺了啥补上啥把

"dependencies": {
    "@grpc/proto-loader": "^0.6.1",
    "@nestjs/common": "^7.6.15",
    "@nestjs/config": "^0.6.3",
    "@nestjs/core": "^7.6.15",
    "@nestjs/microservices": "^7.6.15",
    "@nestjs/mongoose": "^7.2.4",
    "@nestjs/platform-express": "^7.6.15",
    "@nestjs/schedule": "^0.4.3",
    "@nestjs/swagger": "^4.8.0",
    "@types/cron": "^1.7.2",
    "class-transformer": "^0.4.0",
    "class-validator": "^0.13.1",
    "cron": "^1.8.2",
    "dayjs": "^1.10.4",
    "grpc": "^1.24.6",
    "md5": "^2.3.0",
    "mongoose": "^5.12.3",
    "nest-winston": "^1.4.0",
    "nuid": "^1.1.4",
    "reflect-metadata": "^0.1.13",
    "rimraf": "^3.0.2",
    "rxjs": "^6.6.6",
    "schedule": "^0.5.0",
    "swagger-ui-express": "^4.1.6",
    "winston": "^3.3.3",
    "winston-daily-rotate-file": "^4.5.1"
  },
  "devDependencies": {
    "@nestjs/cli": "^7.6.0",
    "@nestjs/schematics": "^7.3.0",
    "@nestjs/testing": "^7.6.15",
    "@types/express": "^4.17.11",
    "@types/jest": "^26.0.22",
    "@types/logform": "^1.10.1",
    "@types/node": "^14.14.36",
    "@types/supertest": "^2.0.10",
    "@types/winston": "^2.4.4",
    "@typescript-eslint/eslint-plugin": "^4.19.0",
    "@typescript-eslint/parser": "^4.19.0",
    "eslint": "^7.22.0",
    "eslint-config-prettier": "^8.1.0",
    "eslint-plugin-prettier": "^3.3.1",
    "jest": "^26.6.3",
    "prettier": "^2.2.1",
    "supertest": "^6.1.3",
    "ts-jest": "^26.5.4",
    "ts-loader": "^8.0.18",
    "ts-node": "^9.1.1",
    "tsconfig-paths": "^3.9.0",
    "typescript": "^4.2.3"
  }
  • 工具类代码:GrpcUtil
const path = require('path');
const grpc = require('grpc');


export class GrpcUtil {

  /**
   * 返回的client直接调用方法传入参数例如,client.hello(requestObj,function(err,res){})
   * @param protoPath proto文件的相对路径地址(当前路径为GrpcUtil类所在的路径)
   * @param ip grpc服务器IP地址
   * @param port  grpc服务器端口
   * @param packageName  proto文件里的package
   * @param serviceName proto文件里的service
   */
  static getClient(protoPath:string, ip:string, port:number, packageName:string, serviceName:string){
    const PROTO_PATH = path.join(__dirname,protoPath);
    const protoLoader = require('@grpc/proto-loader');
    const packageDefinition = protoLoader.loadSync(
      PROTO_PATH,
      {
        keepCase: true,
        longs: String,
        enums: String,
        defaults: true,
        oneofs: true
      }
    );
    const protoDescriptor = grpc.loadPackageDefinition(packageDefinition);
    const packageObj = protoDescriptor[packageName];
    // 客户端
    const client = new packageObj[serviceName](
      ip+':'+port,
      grpc.credentials.createInsecure()
    );
    return client
  }

  /**
   * 关闭客户端
   * @param client
   */
  static closeClient(client){
    if (client){
      try {
        client.close()
      }catch (e) {

      }
    }
  }
  /**
   * 启动grpc服务端
   * @param protoPath proto文件的相对路径地址(当前路径为GrpcUtil类所在的路径)
   * @param ip grpc服务器IP地址
   * @param port  grpc服务器端口
   * @param packageName  proto文件里的package
   * @param serviceName proto文件里的service
   * @param serviceImpl proto里定义的rpc函数的实现,例如hello函数的实现  {hello:(requestObj,callback)=>{console.log(requestObj.request);callback(null,'hi')}}
   */
  static startServer(protoPath:string, ip:string, port:number, packageName:string, serviceName:string,serviceImpl:object){
    const PROTO_PATH = path.join(__dirname,protoPath);
    const protoLoader = require('@grpc/proto-loader');
    const packageDefinition = protoLoader.loadSync(
      PROTO_PATH,
      {
        keepCase: true,
        longs: String,
        enums: String,
        defaults: true,
        oneofs: true
      }
    );
    const protoDescriptor = grpc.loadPackageDefinition(packageDefinition);
    const packageObj = protoDescriptor[packageName];
    // 服务端
    const server = new grpc.Server();
    server.addService(
      packageObj[serviceName].service,
      serviceImpl
    );
    server.bind(ip+':'+port, grpc.ServerCredentials.createInsecure());
    server.start();
    return server
  }

  /**
   * 关闭服务器
   * @param server
   */
  static closeServer(server){
    if (server){
      try {
        server.close()
      }catch (e) {

      }
    }
  }
}
  • 测试类
import {GrpcUtil} from "./GrpcUtil";

enum CodeType {
  CodeType_ZERO,
    SUCCESS,
    FAILED
}
export class GrpcUtilTest {
  static test(){
    const Consts = {
       serviceName : 'strategy.instances',
       protoPath : './MarketData.proto',
       ip:'192.168.0.140',
       port:5000,
       packageName : 'market_data_collector',
       grpcServiceName : 'MarketDataService'
    }
    /**
     * 注意  localhost 和 自己IP192.168.0.140是不同的,不要服务器和客户端地址不一样!!!会连不上
     */
    let server = GrpcUtil.startServer(Consts.protoPath,Consts.ip,Consts.port,Consts.packageName,Consts.grpcServiceName,{
      loadStrategyInstance:(request,callback)=>{
        console.log('服务器收到请求');
        console.log(request.request);
        callback(null,{code:CodeType.SUCCESS,msg:'我是服务端,我收到了你的消息'})
      }
    })
    let client = GrpcUtil.getClient(Consts.protoPath,Consts.ip,Consts.port,Consts.packageName,Consts.grpcServiceName)
    client.loadStrategyInstance({id:'hhhhh'},(err,res)=>{
      if (err){
        console.log(err);
      }
      console.log('客户端收到结果');
      console.log(res);
    })
  }
}
  • 测试使用到的proto文件

我这边src->grpc下的proto文件是不会自动复制到dist目录去的,所以我是手动复制过去的,复制一次就行了,修改的时候都再手动覆盖过去
typescript grpc server client 一问一答 简单工具类(亲测可用,带注释、测试类)

typescript grpc server client 一问一答 简单工具类(亲测可用,带注释、测试类)
typescript grpc server client 一问一答 简单工具类(亲测可用,带注释、测试类)

  • 运行测试
    typescript grpc server client 一问一答 简单工具类(亲测可用,带注释、测试类)
    以开发模式运行项目
    typescript grpc server client 一问一答 简单工具类(亲测可用,带注释、测试类)

  • 测试结果
    typescript grpc server client 一问一答 简单工具类(亲测可用,带注释、测试类)

 

上一篇:CentOS8.3安装grpc


下一篇:gRPC- HTTP网关 I