NodeJS 如何在TypeScript中实现GRPC服务器?

6ie5vjzr  于 2023-06-22  发布在  Node.js
关注(0)|答案(3)|浏览(116)

我试图使用@grpc/proto-loader来动态生成protobuf文件,以实现一个简单的服务器,但在Typescript中。
我已经

import { Server, loadPackageDefinition, ServerCredentials } from "grpc";
import { loadSync } from "@grpc/proto-loader";

const packageDefinition = loadSync(__dirname + "/protos/ArtifactUpload.proto");
const protoDescriptor = loadPackageDefinition(packageDefinition);
const impl = {
};
const server = new Server();
server.addService(protoDescriptor.ArtifactUpload, impl);
server.bind('0.0.0.0:50051', ServerCredentials.createInsecure());
server.start();

所以我有两个问题
1.在Javascript examples中,它们使用protoDescriptor.XXX.service,但是在protoDescriptor.ArtifactUpload中没有service属性
1.如果我尝试在impl中添加实现方法,编译器也无法编译。
由于JavaScript示例可以工作,我认为沿着add new property in Typescript object的问题可能能够添加必要的服务类型。然而,我到目前为止还没有运气。
我的Protobuf是

syntax = "proto3";

service ArtifactUpload {
   rpc SignedUrlPutObject (UploadRequest) returns (SignedUrlPutObjectResponse) {}
}

message UploadRequest {
  string message = 1;
}

message SignedUrlPutObjectResponse {
  string reply = 1;
}
cwxwcias

cwxwcias1#

[2021年5月14日更新]:通过@grpc/proto-loader生成TypeScript现在发布了0.6.0版本!我已经更新了我的example here来反映这一点。您现在可以使用npm i @grpc/proto-loader安装最新版本的proto loader,它将包含TS生成脚本。下面的说明仍然有效。
您可以使用原型加载器来生成类型。
首先,安装proto-loader:

npm i @grpc/proto-loader

然后,您可以像这样生成类型:

./node_modules/.bin/proto-loader-gen-types --longs=String --enums=String --defaults --oneofs --grpcLib=@grpc/grpc-js --outDir=proto/ proto/*.proto

下面是我在这个例子中使用的proto文件:

syntax = "proto3";

package example_package;

message ServerMessage {
  string server_message = 1;
}

message ClientMessage {
  string client_message = 1;
}

service Example {
  rpc unaryCall(ClientMessage) returns (ServerMessage) {}
  rpc serverStreamingCall(ClientMessage) returns (stream ServerMessage) {}
  rpc clientStreamingCall(stream ClientMessage) returns (ServerMessage) {}
  rpc bidirectionalStreamingCall(stream ClientMessage) returns (stream ServerMessage) {}
}

一旦生成了类型,就可以像这样使用它们:

import * as grpc from '@grpc/grpc-js';
import * as protoLoader from '@grpc/proto-loader';
import { ProtoGrpcType } from './proto/example';
import { ClientMessage } from './proto/example_package/ClientMessage';
import { ExampleHandlers } from './proto/example_package/Example';
import { ServerMessage } from './proto/example_package/ServerMessage';

const host = '0.0.0.0:9090';

const exampleServer: ExampleHandlers = {
  unaryCall(
    call: grpc.ServerUnaryCall<ClientMessage, ServerMessage>,
    callback: grpc.sendUnaryData<ServerMessage>
  ) {
    if (call.request) {
      console.log(`(server) Got client message: ${call.request.clientMessage}`);
    }
    callback(null, {
      serverMessage: 'Message from server',
    });
  },

  serverStreamingCall(
    call: grpc.ServerWritableStream<ClientMessage, ServerMessage>
  ) {
    call.write({
      serverMessage: 'Message from server',
    });
  },

  clientStreamingCall(
    call: grpc.ServerReadableStream<ClientMessage, ServerMessage>
  ) {
    call.on('data', (clientMessage: ClientMessage) => {
      console.log(
        `(server) Got client message: ${clientMessage.clientMessage}`
      );
    });
  },

  bidirectionalStreamingCall(
    call: grpc.ServerDuplexStream<ClientMessage, ServerMessage>
  ) {
    call.write({
      serverMessage: 'Message from server',
    });
    call.on('data', (clientMessage: ClientMessage) => {
      console.log(
        `(server) Got client message: ${clientMessage.clientMessage}`
      );
    });
  },
};

function getServer(): grpc.Server {
  const packageDefinition = protoLoader.loadSync('./proto/example.proto');
  const proto = (grpc.loadPackageDefinition(
    packageDefinition
  ) as unknown) as ProtoGrpcType;
  const server = new grpc.Server();
  server.addService(proto.example_package.Example.service, exampleServer);
  return server;
}

if (require.main === module) {
  const server = getServer();
  server.bindAsync(
    host,
    grpc.ServerCredentials.createInsecure(),
    (err: Error | null, port: number) => {
      if (err) {
        console.error(`Server error: ${err.message}`);
      } else {
        console.log(`Server bound on port: ${port}`);
        server.start();
      }
    }
  );
}

我在这里创建了如何使用gRPC和TypeScript的各种示例:https://github.com/badsyntax/grpc-js-typescript

7lrncoxx

7lrncoxx2#

最后我得到了它的工作如下:
package.json中,我有以下内容:

{
  ...
  "scripts": {
    "start": "node index.js",
    "build": "pbjs -t static-module -w commonjs -o protos.js protos/*.proto && pbts -o protos.d.ts protos.js && tsc",
  },
  "dependencies": {
    "@grpc/proto-loader": "^0.5.5",
    "google-protobuf": "^3.13.0",
    "grpc": "^1.24.4",
    "typescript": "^4.0.5"
  },
  "devDependencies": {
    "@types/node": "^14.14.7",
    "protobufjs": "^6.10.1"
  }
}
import { Server, loadPackageDefinition, ServerCredentials, GrpcObject, ServiceDefinition, handleUnaryCall } from "grpc";
import { ISignedUrlPutObjectResponse, IUploadRequest, SignedUrlPutObjectResponse } from "./protos";
import { loadSync } from "@grpc/proto-loader";

const packageDefinition = loadSync(__dirname + "/protos/ArtifactUpload.proto");

interface IArtifactUpload {
  signedUrlPutObject: handleUnaryCall<IUploadRequest, ISignedUrlPutObjectResponse>;
}
interface ServerDefinition extends GrpcObject {
  service: any
}
interface ServerPackage extends GrpcObject {
  [name: string]: ServerDefinition
}
const protoDescriptor = loadPackageDefinition(packageDefinition) as ServerPackage;
const server = new Server();
server.addService<IArtifactUpload>(protoDescriptor.ArtifactUpload.service, {
  signedUrlPutObject(call, callback) {
    console.log(call.request.message);
    console.log(callback);
    callback(null, SignedUrlPutObjectResponse.create({ reply: "hello " + call.request.message }));

  }

});
server.bind('0.0.0.0:50051', ServerCredentials.createInsecure());
server.start();

我使用protobufjs来构建一些类型,尽管它们大多未使用,因为它与GRPC不完全兼容。但是,它确实保存了请求和响应类型的时间。
我仍然需要创建服务器类型并将其应用于protoDescriptor。在此重复强调。

interface IArtifactUpload {
  signedUrlPutObject(call: ServerUnaryCall<IUploadRequest>, callback: ArtifactUpload.SignedUrlPutObjectCallback): void;
}

interface ServerDefinition extends GrpcObject {
  service: any;
}
interface ServerPackage extends GrpcObject {
  [name: string]: ServerDefinition
}

我使用any作为service,因为它是唯一一个允许我避免放入任何特定于IArtifactUpload的东西的东西。

export interface GrpcObject {
    [name: string]: GrpcObject | typeof Client | ProtobufMessage;
  }

应该尝试提供一个表示服务器的对象。
我将我的解决方案链接到https://github.com/protobufjs/protobuf.js/issues/1017#issuecomment-725064230,以防我错过了更好的方法。

q3aa0525

q3aa05253#

永远不要使用@grpc/proto-loader,它使用的是eval。它是不安全

相关问题