GVKun编程网logo

如何使用 grpc 的boringssl 功能来处理我的 nodejs 程序(grpc jsonrpc)

20

本文将分享如何使用grpc的boringssl功能来处理我的nodejs程序的详细内容,并且还将对grpcjsonrpc进行详尽解释,此外,我们还将为大家带来关于docker-如何使用Hashicor

本文将分享如何使用 grpc 的boringssl 功能来处理我的 nodejs 程序的详细内容,并且还将对grpc jsonrpc进行详尽解释,此外,我们还将为大家带来关于docker-如何使用Hashicorp Vault管理我的应用程序机密?、grpc golang服务端客户端和nodejs客户端关于TSL/SSL配置经验、gRPC-nodejs、gRPC实战--如何在NodeJS中有效使用gRPC流的相关知识,希望对你有所帮助。

本文目录一览:

如何使用 grpc 的boringssl 功能来处理我的 nodejs 程序(grpc jsonrpc)

如何使用 grpc 的boringssl 功能来处理我的 nodejs 程序(grpc jsonrpc)

您的问题存在多个层面的问题,并且缺少很多上下文,但根据我的了解,我可以提供以下几点:

首先,grpc-node 已被弃用,您应该转移到 grpc-js,它不再包含本机代码。

那么,grpc-node 实际上并没有使用 BoringSSL,它实际上依赖于 nodejs 将 OpenSSL 本身作为原生 API 公开。

最后,您应该能够在本机节点模块中使用 nodejs 公开的 OpenSSL API:https://nodejs.org/api/addons.html#addons_linking_to_libraries_included_with_node_js

docker-如何使用Hashicorp Vault管理我的应用程序机密?

docker-如何使用Hashicorp Vault管理我的应用程序机密?

我对Hashicorp Vault的产品感到非常兴奋,但是我很难确定它是否适合我们当前的体系结构.毫无疑问,每次部署时都需要手动打开保管库,这极大地提高了安全性,但是当保管库最初被密封时,应用程序将如何做出反应?

例如,如果应用程序A依赖于Vault生成的数据库凭证才能初始化,那么在Vault被密封的情况下部署该应用程序意味着如何反应?检查密封状态时是否等待旋转?

此外,其他人如何在生产中使用某些秘密预填充Vault?例如,我们有一个身份验证服务器,它依赖于一个一致的系统密码,该密码必须在启动时从Vault中获取.部署保险柜后,如何安全地确保此机密可用?

作为记录,我们正在使用docker-compose和ecs compose部署Vault和其他服务.

最佳答案
无需在部署之间密封您的保管库.保管箱需要未密封的保管库来更新租约,读取机密,创建凭据等.在正常使用期间,保管库通过身份验证和授权来保护.

当检测到重大入侵时,应密封您的库.密封金库可以通过丢弃重建的主密钥来最大程度地减少损坏.这样可以防止保险柜在风险减轻之前运行. Sealing不会撤销Vault颁发的凭据.

您还询问了有关导入先前存在的机密以及如何“在部署Vault后安全地确保此机密可用的信息?”:

您应为已存在的机密发布写命令,以在打开保管库后将其导入.您可以通过阅读来安全地确保该秘密存在.使用CLI或API时,读写操作通常是安全的.

$vault write secret/single-consistent-system-secret value=secret-stuff
Success! Data written to: secret/single-consistent-system-secret

$vault read secret/single-consistent-system-secret
Key             Value
lease_duration  2592000
value           secret-stuff

grpc golang服务端客户端和nodejs客户端关于TSL/SSL配置经验

grpc golang服务端客户端和nodejs客户端关于TSL/SSL配置经验

  1. 首先生成证书文件 在github上找到的一段生成脚本:

openssl genrsa -passout pass:1111 -des3 -out ca.key 4096

openssl req -passin pass:1111 -new -x509 -days 3650 -key ca.key -out ca.crt -subj "/C=FR/ST=Paris/L=Paris/O=Test/OU=Test/CN=ca"

openssl genrsa -passout pass:1111 -des3 -out server.key 4096

openssl req -passin pass:1111 -new -key server.key -out server.csr -subj  "/C=FR/ST=Paris/L=Paris/O=Test/OU=Server/CN=charmer"

openssl x509 -req -passin pass:1111 -days 3650 -in server.csr -CA ca.crt -CAkey ca.key -set_serial 01 -out server.crt

openssl rsa -passin pass:1111 -in server.key -out server.key

openssl genrsa -passout pass:1111 -des3 -out client.key 4096

openssl req -passin pass:1111 -new -key client.key -out client.csr -subj  "/C=FR/ST=Paris/L=Paris/O=Test/OU=Client/CN=charmer"

openssl x509 -passin pass:1111 -req -days 3650 -in client.csr -CA ca.crt -CAkey ca.key -set_serial 01 -out client.crt

openssl rsa -passin pass:1111 -in client.key -out client.key

执行后, 目录下会有8个文件, 2个ca, 3个client和3个server

  1. golang服务端
creds, err := credentials.NewServerTLSFromFile("./server.crt", "./server.key")
if err != nil {
    ...
}
server := grpc.NewServer(grpc.Creds(creds))
  1. golang客户端
creds, err := credentials.NewClientTLSFromFile("./keys/server.crt", "charmer")
if err != nil {
    ...
}
conn, err := grpc.Dial("xx.xx.xx.xx:xxxx", grpc.WithTransportCredentials(creds))
defer conn.Close()

注意: 这里的charmer是和生成证书时候参数/CN=xxx的xxx一致, 是ssl的common name

  1. nodejs客户端
const caCrt = fs.readFileSync(__dirname + "/ca.crt");
const clientKey = fs.readFileSync(__dirname + "/client.key");
const clientCrt = fs.readFileSync(__dirname + "/client.crt");

let client = new hello_proto.Greeter("xx.xx.xx.xx:xxxx", grpc.credentials.createSsl(caCrt, clientKey, clientCrt), { "grpc.ssl_target_name_override": "charmer", "grpc.default_authority": "charmer" });
client.sayHello...

注意这里的两个关键参数 grpc.ssl_target_name_override 和 grpc.default_authority, 也是刚刚参数的common name

gRPC-nodejs

gRPC-nodejs

1.1 RPC 框架原理

RPC 框架的目标就是让远程服务调用更加简单、透明,RPC 框架的目标就是让远程服务调用更加简单、透明,RPC 框架负责屏蔽底层的传输方式(TCP 或者 UDP)、序列化方式(XML/Json/ 二进制)和通信细节。服务调用者可以像调用本地接口一样调用远程的服务提供者,而不需要关心底层通信细节和调用过程。

原理流程如下:

2.gRPC

        gRPC 是由 Google 开发并开源的一种语言中立的 RPC 框架,当前支持 C、Java 和 Go 语言,其中 C 版本支持 C、C++、Node.js、C# 等。

2.1. gRPC 服务端创建

下面以搭建一个node服务为例介绍gRPC的使用(windows环境):

首先安装node.js(安装地址:https://nodejs.org/en/download/)

2.1.1在任意磁盘下建立文件夹rgpc-node,命令提示符敲:npm init 建立项目

2.1.2安装protoc以及grpc插件
        这个用npm直接安装 
       npm install grpc-tools --save-dev
       npm install google-protobuf --save
       npm install grpc --save
      在./node_modules/grpc-tools/bin下,你会找到 protoc.exe 和 grpc_node_plugin.exe两个文件。

如果在npm install grpc-tools --save-dev 安装时node-pre-spy 安装不成功是,下面会有 package.json文件附上,把package.json里的依赖拷进到你的依赖里去,npm install 即可,安装 以上工具的目的是我们要使用.proto文件并使用protoc 进行编译

package.json

{
  "name": "grpc-node",
  "version": "1.0.0",
  "description": "",
  "main": "index.js",
  "scripts": {
    "test": "echo \"Error: no test specified\" && exit 1"
  },
  "author": "",
  "license": "ISC",
  "dependencies": {
    "@grpc/proto-loader": "^0.1.0",
    "async": "^1.5.2",
    "google-protobuf": "^3.0.0",
    "grpc": "^1.11.0",
    "lodash": "^4.6.1",
    "minimist": "^1.2.0"
  }
}

以上都安装好了,开始创建一个  HelloWorldService.proto文件


syntax = "proto3";

option java_package = "ex.grpc";
option objc_class_prefix = "HSW";

package hellostreamingworld;

// The greeting service definition.
service MultiGreeter {
  // Sends multiple greetings
  rpc sayHello (HelloRequest) returns (stream HelloReply) {}
}

// The request message containing the user''s name and how many greetings
// they want.
message HelloRequest {
  string name = 1;
  string num_greetings = 2;
}

// A response message containing a greeting
message HelloReply {
  string message = 1;
}

 运行编译命令
./node_modules/grpc-tools/bin/protoc --js_out=import_style=commonjs,binary:./ --plugin=protoc-gen-grpc=./node_modules/grpc-tools/bin/grpc_node_plugin.exe --grpc_out=./ HelloWorldService.proto
运行完成后,会生成HelloWorldService_grpc_pb.js 和 HelloWorldServer_pb.js两个文件。(这一步要不要无关大局,只是将.proto文件转成相应的.js文件,.proto文件一样可以引入)

如果是你使用了编译命令生成了两个相应的.js文件

下面建立server.js文件(前提是你编译成功)

​

​
var services = require(''./HelloWorldService_grpc_pb.js'');
var messages = require(''./HelloWorldService_pb.js'');
var grpc = require(''grpc'')
var hello = function(call, callback) {
  var response = new messages.HelloResponse();
  response.setHellostring("hello," + call.request.getName());
  callback(null, response);
}
var server = new grpc.Server();
server.addService(
  services.HelloWorldServiceService,
  {
    hello:hello
  }
);
server.bind(''0.0.0.0:50051'', grpc.ServerCredentials.createInsecure());
server.start(function(err,data){
  console.log(err);
  console.log(data);
});

紧接着是建立client.js文件(前提是你编译成功)


var grpc = require(''grpc'');
var messages = require(''./HelloWorldService_pb.js'');
var services = require(''./HelloWorldService_grpc_pb.js'')
var request = new messages.HelloRequest();
request.setName(''world'');
var client = new services.HelloWorldServiceClient(
  ''localhost:50051'',
  grpc.credentials.createInsecure()
);
client.hello(request, function(err,data){
  if(err){
    console.error(err);
  }
  console.log(data);
  console.log(data.getHellostring());
})

倘若没有将.proto文件编译成相应的.js文件

Node.js 的类库在运行时加载 .proto 中的客户端存根并动态生成服务描述符。

要加载一个 .proto 文件,只需要 require gRPC 类库,然后使用它的 load() 方法:

步骤同上

1建立server.js文件

var PROTO_PATH = __dirname + ''/../../protos/helloworld.proto'';

var grpc = require(''grpc'');
var protoLoader = require(''@grpc/proto-loader'');
var packageDefinition = protoLoader.loadSync(
    PROTO_PATH,
    {keepCase: true,
     longs: String,
     enums: String,
     defaults: true,
     oneofs: true
    });
var hello_proto = grpc.loadPackageDefinition(packageDefinition).helloworld;

/**
 * Implements the SayHello RPC method.
 */
function sayHello(call, callback) {
  callback(null, {message: ''Hello '' + call.request.name});
}

/**
 * Starts an RPC server that receives requests for the Greeter service at the
 * sample server port
 */
function main() {
  var server = new grpc.Server();
  server.addService(hello_proto.Greeter.service, {sayHello: sayHello});
  server.bind(''0.0.0.0:50051'', grpc.ServerCredentials.createInsecure());
  server.start();
}

main();

 建立client.js文件

var PROTO_PATH = __dirname + ''/../../protos/helloworld.proto'';

var grpc = require(''grpc'');
var protoLoader = require(''@grpc/proto-loader'');
var packageDefinition = protoLoader.loadSync(
    PROTO_PATH,
    {keepCase: true,
     longs: String,
     enums: String,
     defaults: true,
     oneofs: true
    });
var hello_proto = grpc.loadPackageDefinition(packageDefinition).helloworld;

function main() {
  var client = new hello_proto.Greeter(''localhost:50051'',
                                       grpc.credentials.createInsecure());
  var user;
  if (process.argv.length >= 3) {
    user = process.argv[2];
  } else {
    user = ''world'';
  }
  client.sayHello({name: user}, function(err, response) {
    console.log(''Greeting:'', response.message);
  });
}

main();

接着就是怎么运行了,首先在你的项目根目录cmd 直接运行node server.js

接着仍然是那个根目录cmd 直接运行node client.js 你会看到打印出Greeting: hello world

 

gRPC实战--如何在NodeJS中有效使用gRPC流

gRPC实战--如何在NodeJS中有效使用gRPC流

gRPC实战包含一系列文章,包括原创和翻译。最终会形成一个完整的系列,后续会不断完善,增加新的内容:

  • gRPC简介:why,what,how?
  • gRPC服务健康检查最佳实践
  • Kubernetes中使用envoy负载均衡gRPC流量
  • 用Golang编写通过gRPC进行通信的服务
  • 如何在NodeJS中有效使用gRPC流

=============================================================

g10.png

本文将说明如何在NodeJS应用程序的GRPC中使用流。

什么是gRPC中的流

gRPC中的流可帮助我们在单个RPC调用中发送消息流。

g11.png

gRPC 的流式,分为三种类型:

  • server-side streaming RPC:服务器端流式 RPC
  • Client-side streaming RPC:客户端流式 RPC
  • Bidirectional streaming RPC:双向流式 RPC

gRPC中的流使用场景

  • 大规模数据包
  • 实时场景

在本文中,我们将重点关注以下流:

  • Server Streaming GRPC:在这种情况下,客户端向服务器发出单个请求,服务器将消息流发送回客户端。
  • Client Streaming GRPC:在这种情况下,客户端将消息流发送到服务器。然后,服务器处理流并将单个响应发送回客户端。

Server Streaming gRPC

现在让我们为服务器流gRPC创建服务器和客户端代码。

创建 .proto 文件

创建一个名为proto的文件夹。在该文件夹中创建一个名为employee.proto的文件。将以下内容复制到employee.proto中:

syntax = "proto3";

package employee;

service Employee {

  rpc paySalary (EmployeeRequest) returns (stream EmployeeResponse) {}
}


message EmployeeRequest {
  repeated int32 employeeIdList = 1;
}

message EmployeeResponse{
  string message = 1;
}

请参阅我的grpc基础文章,以了解有关.proto文件和协议缓冲区的更多信息。

在这里,我们创建一个名为paySalary的rpc,它接受EmployeeRequest作为请求并发送EmployeeResponse流作为响应。我们使用关键字流来指示服务器将发送消息流。

上面也定义了EmployeeRequestEmployeeResponse。 repeate关键字表示将发送数据列表。

在此示例中,paySalary的请求将是员工ID的列表。服务器将通过一条消息流做出响应,告知是否已向员工支付薪水。

为服务器创建虚拟数据

创建一个名为data.js的文件,并将以下代码复制到其中。

//Hardcode some data for employees
let employees = [{
    id: 1,
    email: "abcd@abcd.com",
    firstName: "First1",
    lastName: "Last1"   
},
{
    id: 2,
    email: "xyz@xyz.com",
    firstName: "First2",
    lastName: "Last2"   
},
{
    id: 3,
    email: "temp@temp.com",
    firstName: "First3",
    lastName: "Last3"   
},
];

exports.employees = employees;

我们将其用作服务器的数据源。

创建Server

创建一个名为server.js的文件。将以下代码复制到server.js中

const PROTO_PATH = __dirname + ''/proto/employee.proto'';

const grpc = require(''grpc'');
const protoLoader = require(''@grpc/proto-loader'');


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

接下来,将以下代码片段添加到server.js中

let { paySalary } = require(''./pay_salary.js'');

function main() {
  let server = new grpc.Server();
  server.addService(employee_proto.Employee.service, 
    { paySalary: paySalary }
  );
  server.bind(''0.0.0.0:4500'', grpc.ServerCredentials.createInsecure());
  server.start();
}

main();

在上面的脚本中,我们将启动GRPC Server并将Employee Service和paySalary实现一起添加到其中。

但是paySalary函数在pay_salary.js文件中定义。

让我们创建一个pay_salary.js文件。

将以下脚本添加到pay_salary.js文件中

let { employees } = require(''./data.js'');
const _ = require(''lodash'');

function paySalary(call) {
    let employeeIdList = call.request.employeeIdList;
  
    _.each(employeeIdList, function (employeeId) {
      let employee = _.find(employees, { id: employeeId });
      if (employee != null) {
        let responseMessage = "Salary paid for ".concat(
          employee.firstName,
          ", ",
          employee.lastName);
        call.write({ message: responseMessage });
      }
      else{
        call.write({message: "Employee with Id " + employeeId + " not found in record"});
      }
  
    });
    call.end();
  
}
exports.paySalary = paySalary;

paySalary函数将调用作为输入。 call.request将包含客户端发送的请求。

call.request.employeeIdList将包含客户端发送的员工ID的列表。

然后,我们遍历EmployeeId,并为每个员工ID进行一些处理。

对于每个员工ID,我们最后都调用call.write函数。 call.write将在流中将单个消息写回到客户端。

在这种情况下,对于每位员工,call.write都会发回工资是否已经支付。

处理完所有员工编号后,我们将调用call.end函数。 call.end指示流已完成。

这是最终的server.js文件

const PROTO_PATH = __dirname + ''/proto/employee.proto'';

const grpc = require(''grpc'');
const protoLoader = require(''@grpc/proto-loader'');


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

let { paySalary } = require(''./pay_salary.js'');

function main() {
  let server = new grpc.Server();
  server.addService(employee_proto.Employee.service, 
    { paySalary: paySalary }
  );
  server.bind(''0.0.0.0:4500'', grpc.ServerCredentials.createInsecure());
  server.start();
}

main();

创建Client

创建一个名为client_grpc_server_stream.js的文件。将以下代码复制到文件中。

const PROTO_PATH = __dirname + ''/proto/employee.proto'';

const grpc = require(''grpc'');
const protoLoader = require(''@grpc/proto-loader'');

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

接下来,将以下脚本片段添加到客户端。

function main() {
  let client = new employee_proto.Employee(''localhost:4500'',
                                       grpc.credentials.createInsecure());
                                       
  let employeeIdList = [1,10,2];
  let call = client.paySalary({employeeIdList: employeeIdList});

  call.on(''data'',function(response){
    console.log(response.message);
  });

  call.on(''end'',function(){
    console.log(''All Salaries have been paid'');
  });

}

main();

client变量将具有存根,这将有助于我们在服务器中调用该函数。

employeeIdList是提供给服务器的输入。

let call = client.paySalary({employeeIdList: employeeIdList}); 脚本调用服务器中的paySalary函数,并将employeeIdList作为请求传递。由于服务器将要发送消息流,因此调用对象将帮助我们侦听流事件。

我们会侦听呼叫对象中的“数据”事件,以查看流中来自服务器的任何消息。如下面的脚本所示。

call.on(''data'',function(response){
    console.log(response.message);
  });

在这里,只要我们从服务器收到任何消息,我们就只打印响应消息。

我们在调用对象中侦听“结束”事件,以了解服务器流何时结束。如下面的脚本所示。

call.on(''end'',function(){
    console.log(''All Salaries have been paid'');
  });

在此流结束时,我们正在打印“已支付所有薪水”。

这是client_gprc_server_stream.js的完整代码。

const PROTO_PATH = __dirname + ''/proto/employee.proto'';

const grpc = require(''grpc'');
const protoLoader = require(''@grpc/proto-loader'');

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

function main() {
  let client = new employee_proto.Employee(''localhost:4500'',
                                       grpc.credentials.createInsecure());
                                       
  let employeeIdList = [1,10,2];
  let call = client.paySalary({employeeIdList: employeeIdList});

  call.on(''data'',function(response){
    console.log(response.message);
  });

  call.on(''end'',function(){
    console.log(''All Salaries have been paid'');
  });

}

main();

运行代码

打开命令提示符,然后使用以下脚本启动服务器。

node server.js

打开一个新的命令提示符,并使用以下脚本运行客户端。

node client_grpc_server_stream.js   

在运行客户端时,我们将获得以下输出。

Salary paid for First1, Last1
Employee with Id 10 not found in record
Salary paid for First2, Last2
All Salaries have been paid

在这种情况下,客户端已向服务器发送了3个Id的1,10,2。服务器一一处理ID,然后将消息流发送给客户端。流中的所有消息完成后,将显示消息“已支付所有薪水”。

Client Streaming GRPC

现在,让我们为客户端流GRPC创建服务器和客户端代码。

创建.proto文件

在先前创建的employee.proto文件中,添加以下内容

service Employee {

  rpc paySalary (EmployeeRequest) returns (stream EmployeeResponse) {}

  rpc generateReport (stream ReportEmployeeRequest) returns (ReportEmployeeResponse) {}
}

message ReportEmployeeRequest {
  int32 id = 1;
}

message ReportEmployeeResponse{
  string successfulReports = 1;
  string failedReports = 2;
}

在这里,我们添加了一个名为generateReport的新rpc,它接受ReportEmployeeRequest流作为请求并返回ReportEmployeeResponse作为响应。

因此,向rpc输入的内容是员工ID的流,服务器的响应将是单个响应,其中指出生成了多少报告以及有多少报告失败。

这是我们更改后的完整的employee.proto文件:

syntax = "proto3";

package employee;

service Employee {

  rpc paySalary (EmployeeRequest) returns (stream EmployeeResponse) {}

  rpc generateReport (stream ReportEmployeeRequest) returns (ReportEmployeeResponse) {}
}


message EmployeeRequest {
  repeated int32 employeeIdList = 1;
}

message EmployeeResponse{
  string message = 1;
}

message ReportEmployeeRequest {
  int32 id = 1;
}

message ReportEmployeeResponse{
  string successfulReports = 1;
  string failedReports = 2;
}

创建Server

这是添加了新rpc的完整server.js代码:

const PROTO_PATH = __dirname + ''/proto/employee.proto'';

const grpc = require(''grpc'');
const protoLoader = require(''@grpc/proto-loader'');


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


let { paySalary } = require(''./pay_salary.js'');
let { generateReport } = require(''./generate_report.js'');

function main() {
  let server = new grpc.Server();
  server.addService(employee_proto.Employee.service, 
    { paySalary: paySalary ,
      generateReport: generateReport }
  );
  server.bind(''0.0.0.0:4500'', grpc.ServerCredentials.createInsecure());
  server.start();
}

main();

在上面的脚本中,我们可以看到我们还向grpc服务器添加了generateReport函数。我们还可以看到generateReport函数来自generate_report.js文件。

创建一个名为generate_report.js的文件。

将以下脚本添加到文件中:

let { employees } = require(''./data.js'');
const _ = require(''lodash'');

function generateReport(call, callback){

    let successfulReports = [];
    let failedReports = [];
    call.on(''data'',function(employeeStream){
        let employeeId = employeeStream.id;
        let employee = _.find(employees, { id: employeeId });
        if (employee != null) {
          successfulReports.push(employee.firstName);
        }
      else{
          failedReports.push(employeeId);
      }

    });
    call.on(''end'',function(){
        callback(null,{
            successfulReports: successfulReports.join(),
            failedReports: failedReports.join()
        })
    })
}

exports.generateReport = generateReport;

generateReport函数接受两个输入,即调用和回调

为了从客户端获取消息流,我们需要在调用对象中监听数据事件。这是在以下脚本中完成的。

call.on(''data'',function(employeeStream){
        let employeeId = employeeStream.id;
        let employee = _.find(employees, { id: employeeId });
        if (employee != null) {
          successfulReports.push(employee.firstName);
        }
      else{
          failedReports.push(employeeId);
      }

    });

来自客户端的每条消息都会调用data事件。该消息存在于employeeStream变量中。收到消息后,我们尝试生成报告,并确定报告是成功还是失败。

调用对象上的结束事件表示客户端流已结束。以下代码显示了如何监听结束事件。

call.on(''end'',function(){
        callback(null,{
            successfulReports: successfulReports.join(),
            failedReports: failedReports.join()
        })
    })

在这种情况下,当结束事件发生时,我们将所有成功和失败报告组合到一个响应对象中,并使用回调对象将其发送回客户端。

创建Client

创建一个名为client_grpc_client_stream.js的文件。将以下脚本添加到其中。

const PROTO_PATH = __dirname + ''/proto/employee.proto'';

const grpc = require(''grpc'');
const protoLoader = require(''@grpc/proto-loader'');
const _ = require(''lodash'');

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

上面的脚本具有与服务器代码相同的功能。

将以下脚本也添加到client_grpc_client_stream.js

function main() {
  let client = new employee_proto.Employee(''localhost:4500'',
    grpc.credentials.createInsecure());

  let call = client.generateReport(function (error, response) {
    console.log("Reports successfully generated for: ", response.successfulReports);
    console.log("Reports failed since Following Employee Id''s do not exist: ", response.failedReports);
  });

  let employeeIdList = [1, 10, 2];
  _.each(employeeIdList, function (employeeId) {
        call.write({ id: employeeId });
  })

  call.end();
}

main();

让我们看看上面的脚本在做什么。

let call = client.generateReport(function (error, response) {
    console.log("Reports successfully generated for: ", response.successfulReports);
    console.log("Reports failed since Following Employee Id''s do not exist: ", response.failedReports);
  });

在脚本的这一部分中,我们正在创建一个调用对象并调用generateReport函数。同样在generateReport函数内部,我们指示客户端一旦收到服务器的响应,应该怎么做。在这种情况下,我们将打印服务器发送回的成功和失败报告。

let employeeIdList = [1, 10, 2];
  _.each(employeeIdList, function (employeeId) {
        call.write({ id: employeeId });
  })

在脚本的以上部分中,我们遍历了员工ID,并将消息流发送到服务器。我们使用call.write将消息以流的形式发送到服务器。

最后,一旦我们在流中发送了所有消息,就可以使用call.end函数指示流已完成,如下所示:

call.end();

下面给出了client_grpc_client_stream的完整代码。

const PROTO_PATH = __dirname + ''/proto/employee.proto'';

const grpc = require(''grpc'');
const protoLoader = require(''@grpc/proto-loader'');
const _ = require(''lodash'');

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

function main() {
  let client = new employee_proto.Employee(''localhost:4500'',
    grpc.credentials.createInsecure());

  let call = client.generateReport(function (error, response) {
    console.log("Reports successfully generated for: ", response.successfulReports);
    console.log("Reports failed since Following Employee Id''s do not exist: ", response.failedReports);
  });

  let employeeIdList = [1, 10, 2];
  _.each(employeeIdList, function (employeeId) {
        call.write({ id: employeeId });
  })

  call.end();
}

main();

运行代码

打开命令提示符,然后使用以下脚本启动服务器。

node server.js

打开一个新的命令提示符,并使用以下脚本运行客户端。

node client_grpc_server_stream.js   

在运行客户端时,我们将获得以下输出。

Reports successfully generated for:  First1,First2
Reports failed since Following Employee Id\''s do not exist:  10

在这种情况下,客户端已向服务器发送了3个Id的1,10,2作为消息流。然后,服务器处理流中的消息,并将单个响应发送回客户端,以显示成功的报告数量和失败的报告数量。

关于如何使用 grpc 的boringssl 功能来处理我的 nodejs 程序grpc jsonrpc的介绍现已完结,谢谢您的耐心阅读,如果想了解更多关于docker-如何使用Hashicorp Vault管理我的应用程序机密?、grpc golang服务端客户端和nodejs客户端关于TSL/SSL配置经验、gRPC-nodejs、gRPC实战--如何在NodeJS中有效使用gRPC流的相关知识,请在本站寻找。

本文标签: