gRPC是什么
gRPC(gRPC Remote Procedure Calls,gRPC 远程过程调用)最初由谷歌(Google)研发,是一款现代化、开源且高性能的 RPC 框架,具备跨环境运行的特性。该框架核心遵循 “服务定义优先” 的设计理念,采用 Protocol Buffers(简称 ProtoBuf)作为接口定义语言(IDL)—— 开发人员通过编写.proto 文件即可清晰定义服务接口与消息数据结构,再借助 gRPC 编译器自动生成多语言的客户端和服务端代码,大幅简化了跨语言远程调用的开发成本与复杂度。
背景
项目基于 gRPC 框架,为用户提供多语言的客户端调用支持。作为团队唯一的前端,Node.js 版本的开发工作自然责无旁贷。看了一圈node源码,哔哩吧哩、、、那么好!为了无脑搬运、自己封装一圈!!!!
使用
请确保您的系统或容器已经安装‘openssl’
主要包含三种模式
1、Request -> Response
2、stream Request -> Response
3、Request -> stream Response
安装
npm install @fxs0819/grpc-client
proto
// Hello.proto
syntax = "proto3";
package hello;
// The greeting service definition.
service MainGreeter {
// Sends a greeting
rpc SayHello (HelloRequest) returns (HelloReply) {}
// Sends a stream greeting
rpc SayMultiHello (stream HelloRequest) returns (HelloReply) {}
// Receive a stream greeting
rpc ReceiveMultiHello (HelloRequest) returns (stream HelloReply) {}
}
// The request message containing the user's name.
message HelloRequest {
string name = 1;
}
// The response message containing the greetings
message HelloReply {
string message = 1;
}
服务端
# server.js
const grpc = require('@grpc/grpc-js');
const path = require('path');
const protoLoader = require('@grpc/proto-loader');
import * as async from 'async';
import * as lodash from 'lodash';
const config = {
host: '0.0.0.0',
port: 50051,
packageName: 'hello',
serveName: 'MainGreeter'
};
let cnt = 1;
function sayHello(call:any, callback:any) {
console.log('server', call.request);
callback(null, { message: `[${cnt++}] echo: ` + call.request.name });
}
function sayMultiHello(call:any, callback:any) {
const names:string[] = [];
call.on('data', (data:Record<string, any>) => {
console.log(`get name: ${data.name}`);
names.push(data.name);
});
call.on('end', ()=>{
const res = { message: 'Hello ' + names.join(',') };
callback(null, res);
});
}
function receiveMultiHello(call:any, callback:any) {
console.log('server', call.request);
const senders: any[] = [];
for (var i = 0; i < 10; i++) {
senders[i] = (function(i) {
return (callback:any)=>{
call.write({ message: 'Hello ' + `name${i}` });
lodash.delay(callback, 1000);
};
}(i));
}
async.series(senders, () => {
call.end();
});
}
function main() {
const PROTO_PATH = path.join(__dirname, './protos/Hello.proto');
const packageDefinition = protoLoader.loadSync(PROTO_PATH, { keepCase: true, longs: String, enums: String, defaults: true, oneofs: true });
const protoDescriptor = grpc.loadPackageDefinition(packageDefinition);
const helloProto = protoDescriptor[config.packageName];
var server = new grpc.Server();
server.addService(helloProto[config.serveName].service, { sayHello, sayMultiHello, receiveMultiHello });
server.bindAsync(`${config.host}:${config.port}`, grpc.ServerCredentials.createInsecure(), () => {
server.start();
console.log(`grpc server started: ${config.host}:${config.port}`);
});
}
main();
客户端
const path = require('path');
import { Client } from '../src/index';
import { PassThrough } from 'stream';
import * as lodash from 'lodash';
import * as async from 'async';
async function main() {
const config = {
uri: '0.0.0.0:50051',
ssl: {
isOpen: false
// rootCertPath: path.join(__dirname, './certs/cacert.pem'),
// clientStorePath: path.join(__dirname, './certs/client.pfx'),
// pfxCode: 'balabal.quick.50843197',
// authority: 'quickservice'
},
config: {
PROTO_PATH: path.join(__dirname, './protos/Hello.proto'),
PACKAGE_NAME: 'hello',
SERVE_NAME: 'MainGreeter'
}
};
const client:Client = new Client(config);
const deadline = new Date().getTime() + 1000;
await client.waitForReady(deadline);
// await sayHello(client);
await sayMultiHello(client);
// await receiveMultiHello(client);
client.close();
}
async function sayHello(client:Client) {
const deadline = new Date().getTime() + 1000;
const res = await client.myProxy('sayHello', { 'name': 'Tom' }, { deadline });
console.log(res);
}
async function sayMultiHello(client:Client) {
const stream = new PassThrough({ objectMode: true });
const sayMultiHelloCall = client.myProxy('sayMultiHello', stream);
const senders: any[] = [];
for (var i = 0; i < 10; i++) {
senders[i] = (function(i) {
return (callback:any)=>{
stream.write({ name: `name${i}` });
lodash.delay(callback, 1000);
};
}(i));
}
async.series(senders, () => {
stream.end();
});
const sayMultiHello = await sayMultiHelloCall;
console.log(sayMultiHello);
}
async function receiveMultiHello(client:Client) {
const call = client.client?.receiveMultiHello({ 'name': 'Tom' });
call.on('data', function(data:any) {
console.log(data);
});
call.on('end', ()=>{
console.log('end');
});
}
main();
最后
希望能帮助到有需要的掘友、请多指教,勿喷呀!