通過NodeJS輕松使用GRPC和協(xié)議緩沖區(qū)的方法
本文將對GRPC和協(xié)議緩沖區(qū)進(jìn)行基本介紹。接下來,我將展示如何在NodeJS應(yīng)用程序中使用GRPC和協(xié)議緩沖區(qū)
GRPC是什么
GRPC是一個開源高性能RPC框架,那么RPC到底是做什么的呢?請看下面的例子
function getEmployee() { return "ABCD"; } function greetEmployee() { let employee = getEmployee(); console.log("Greet",employee) }
在這里,我們有一個getEmployee
函數(shù),它返回一個Employee Name
,另一個函數(shù)greetEmployee
,它調(diào)用getEmployee
并獲取該員工的Name
,然后打印一個Greeting
。
這里的greetEmployee
調(diào)用getEmployee
是一個常規(guī)函數(shù)調(diào)用。
現(xiàn)在,如果getEmployee
和greetEmployee
函數(shù)位于不同的地址空間中,或者它們位于由網(wǎng)絡(luò)分隔的2個不同的主機(jī)中,則該函數(shù)調(diào)用稱為“遠(yuǎn)程過程調(diào)用”(Remote Procedure Call, RPC)。在這里,具有getEmployee
函數(shù)的System充當(dāng)服務(wù)器,而具有greetEmployee
函數(shù)的System充當(dāng)客戶端。
什么是Protocol Buffer (協(xié)議緩沖區(qū))
協(xié)議緩沖區(qū)是默認(rèn)情況下在GRPC中使用的接口定義語言。
- 它有助于定義服務(wù)器提供的各種服務(wù)
- 它有助于定義系統(tǒng)中使用的有效負(fù)載的結(jié)構(gòu)
- 它有助于對消息進(jìn)行序列化(轉(zhuǎn)換為特殊的二進(jìn)制格式),并通過服務(wù)器和客戶端之間的線路進(jìn)行發(fā)送。
在本文后面的部分中,我們將在使用NodeJS應(yīng)用程序時看到如何使用protocol buffers(協(xié)議緩沖區(qū))。
支持哪些不同類型的RPC?
Unary RPC
這是可用的最簡單的RPC??蛻舳嗽诖颂幭蚍?wù)器發(fā)送請求消息。服務(wù)器處理該請求,然后將響應(yīng)消息發(fā)送回客戶端
在本文中,這是我們將重點(diǎn)介紹的grpc。
Server Streaming RPC
在此RPC中,客戶端將請求消息發(fā)送到服務(wù)器,然后服務(wù)器以流方式將一系列消息發(fā)送回客戶端。
Client Streaming RPC
在此RPC中,客戶端以流方式向服務(wù)器發(fā)送一系列消息。然后,服務(wù)器處理所有這些請求,然后將響應(yīng)消息發(fā)送回客戶端。
Bidirectional Streaming RPC
在此RPC中,客戶端以流方式向服務(wù)器發(fā)送一系列消息。然后,服務(wù)器處理請求,然后以流方式將一系列消息發(fā)送回客戶端。
如何在NodeJS中使用GRPC和協(xié)議緩沖區(qū)
使用以下命令創(chuàng)建一個名為grpc-nodejs-demo的文件夾并在其中初始化nodejs
mkdir grpc-nodejs-demo cd grpc-nodejs-demo npm init
這將創(chuàng)建一個package.json文件。
修改package.json文件
將package.json文件替換為以下內(nèi)容
{ "name": "grpc-nodejs-demo", "version": "1.0.0", "description": "", "main": "server.js", "scripts": { "test": "echo \"Error: no test specified\" && exit 1" }, "dependencies": { "@grpc/proto-loader": "^0.1.0", "grpc": "^1.11.0", "lodash": "^4.6.1" }, "author": "Name", "license": "MIT" }
在這里我們添加了3個依賴項
- @ grpc / proto_loader和grpc依賴項將幫助我們在應(yīng)用程序中使用GRPC和協(xié)議緩沖區(qū)
- lodash是通用實用程序依賴項。這將有助于簡化一些代碼邏輯
package.json文件更新后。運(yùn)行以下命令以安裝依賴項
npm install
定義協(xié)議緩沖區(qū)
在此示例中,我們將構(gòu)建一個將employee ID作為輸入,并提供employee詳細(xì)信息作為輸出的服務(wù)。
所需的服務(wù)接口和有效負(fù)載將在協(xié)議緩沖區(qū)文件中指定。協(xié)議緩沖區(qū)文件的擴(kuò)展名為.proto
現(xiàn)在,我們來創(chuàng)建.proto
文件。
在項目中創(chuàng)建一個名為proto
的文件夾。在proto
文件夾中,創(chuàng)建一個名為employee.proto
的文件,并向其中添加以下代碼
syntax = "proto3"; package employee; service Employee { rpc getDetails (EmployeeRequest) returns (EmployeeResponse) {} } message EmployeeRequest { int32 id = 1; } message EmployeeResponse{ EmployeeDetails message = 1; } message EmployeeDetails { int32 id = 1; string email = 2; string firstName = 3; string lastName = 4; }
那么,我們在這里到底做了什么?
syntax="proto3"
;表示我們要使用Protocol Buffer version 3。
package employee;
表示我們正在創(chuàng)建一個名為employee
的程序包,我們將在其中定義我們的services
service Employee { rpc getDetails (EmployeeRequest) returns (EmployeeResponse) {} }
上述腳本表明我們正在創(chuàng)建一個稱為Employee
的服務(wù)。在此服務(wù)中,我們正在創(chuàng)建一個名為getDetails
的函數(shù)(rpc),該函數(shù)接受EmployeeRequest
類型的輸入并以EmployeeResponse
格式提供響應(yīng)
接下來,我們需要定義EmployeeRequest
和EmployeeResponse
。這是在以下腳本中完成的
message EmployeeRequest { int32 id = 1; } message EmployeeResponse{ EmployeeDetails message = 1; } message EmployeeDetails { int32 id = 1; string email = 2; string firstName = 3; string lastName = 4; }
在這里,我們看到消息EmployeeRequest
具有單個類型為int32
和名稱id
的字段。此處分配的數(shù)字1
是字段號,它在消息的編碼和解碼過程中提供幫助。定義的每個字段應(yīng)具有唯一的字段號
我們還看到EmployeeResponse
有一個類型為EmployeeDetails
的自定義字段,并且名稱消息的字段編號為1
。這意味著甚至也必須定義EmployeeDetails
,如上所示。
EmployeeDetails
具有4個字段,包括類型int32
和string
。它們都有唯一的字段編號(unique field numbers)
Field numbers between 1 -15 use 1 byte of space during encoding. and field numbers from 2 - 2047 uses 2 bytes for encoding and hence will take up more space. So try to design in such a way that the field numbers are between 1 - 15 as much as possible
1 -15之間的編號在編碼過程中使用1個字節(jié)的空間。并且2-2047的字段編號使用2個字節(jié)進(jìn)行編碼,因此會占用更多空間。因此,請嘗試以盡可能多的字段編號介于1到15之間的方式進(jìn)行設(shè)計
Creating the GRPC Server
創(chuàng)建一個名為server.js的文件
首先,讓我們包括我們需要的所有庫,并定義.proto文件所在的位置
const PROTO_PATH = __dirname + '/proto/employee.proto'; const grpc = require('grpc'); const protoLoader = require('@grpc/proto-loader'); const _ = require('lodash');
接下來,我們需要加載.proto文件。這是使用protoLoader庫的loadSync方法完成的。
let packageDefinition = protoLoader.loadSync( PROTO_PATH, {keepCase: true, longs: String, enums: String, defaults: true, oneofs: true } );
接下來,從已加載的原始文件包定義中,我們需要獲取所需的包。這是使用以下腳本完成的
let employee_proto = grpc.loadPackageDefinition(packageDefinition).employee;
在這里,我們將employee包放入employee_proto變量中。
employee_proto現(xiàn)在將具有所有原型定義。
接下來,我們需要創(chuàng)建一些虛擬employees數(shù)據(jù)供服務(wù)器使用。創(chuàng)建一個名為data.js的文件,并將以下腳本添加到其中
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;
接下來,我們需要將data.js導(dǎo)入server.js。為此,在server.js中添加以下腳本
function main() { let server = new grpc.Server(); server.addService(employee_proto.Employee.service, {getDetails: getDetails}); server.bind('0.0.0.0:4500', grpc.ServerCredentials.createInsecure()); server.start(); }
let server = new grpc.Server();
是創(chuàng)建新GRPC Server的腳本
在.proto文件中,我們注意到在Employee Service內(nèi)部有一個名為getDetails的函數(shù)。
server.addService(employee_proto.Employee.service,{getDetails:getDetails});
是我們在其中添加Service實現(xiàn)的腳本。該腳本表明,我們在employee_proto.Employee Service中添加了getDetails函數(shù)。然后,我們將此服務(wù)添加到服務(wù)器。
server.bind('0.0.0.0:4500', grpc.ServerCredentials.createInsecure());
是指示服務(wù)器將在端口4500上啟動且沒有身份驗證的腳本
server.start();
是實際啟動服務(wù)器的腳本
現(xiàn)在待處理的主要事情是實現(xiàn)getDetails函數(shù)。下面的腳本顯示了實現(xiàn)
function getDetails(call, callback) { callback(null, { message: _.find(employees, { id: call.request.id }) }); }
這里的call具有請求參數(shù),而回callback 是我們需要定義實現(xiàn)的地方。
在callback 內(nèi)部,我們收到一條消息:_.find(employees,{id:call.request.id})
,其中顯示以下內(nèi)容
- 從Input獲取員工ID-call.request.id
- 搜索employees列表以查找具有該ID的員工
- Return employee詳細(xì)信息
這樣就完成了服務(wù)器的實現(xiàn)。這是server.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; let {employees} = require('./data.js'); function getDetails(call, callback) { console.log('client call :%j', call) callback(null, { message: _.find(employees, { id: call.request.id }) }); } function main() { let server = new grpc.Server(); server.addService(employee_proto.Employee.service, {getDetails: getDetails}); server.bind('0.0.0.0:4500', grpc.ServerCredentials.createInsecure()); server.start(); } main();
Creating the GRPC Client
創(chuàng)建一個名為client.js的文件 將以下腳本復(fù)制到client.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;
上面的腳本以與server.js中相同的方式將employee包加載到employee_proto變量中
接下來,我們需要一種可以調(diào)用RPC的方式。在這種情況下,我們需要能夠調(diào)用服務(wù)器中實現(xiàn)的getDetails函數(shù)。
為此,我們需要在客戶端中創(chuàng)建一個存根(stub)。這是使用以下腳本完成的。
let client = new employee_proto.Employee('localhost:4500', grpc.credentials.createInsecure());
該client Stub將幫助我們調(diào)用在服務(wù)器上運(yùn)行的Employee Service中定義的getDetails函數(shù)。服務(wù)器依次在端口4500上運(yùn)行。代碼行還指示未使用身份驗證
最后,我們可以使用以下腳本調(diào)用getDetails函數(shù)
let employeeId = 1; client.getDetails({id: employeeId}, function(err, response) { console.log('Employee Details for Employee Id:',employeeId,'\n' ,response.message); });
如前所述,客戶端存根可以像正常函數(shù)調(diào)用一樣幫助我們在服務(wù)器中調(diào)用getDetails函數(shù)。為此,我們將employeeId作為輸入。
最后,Response 進(jìn)入response 變量。然后,我們將打印響應(yīng)消息。
完整的client.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 employeeId; if (process.argv.length >= 3) { employeeId = process.argv[2]; } else { employeeId = 1; } client.getDetails({id: employeeId}, function(err, response) { console.log('Employee Details for Employee Id:',employeeId,'\n' ,response.message); }); } main();
Running The Server and Client
打開命令提示符并使用以下命令運(yùn)行服務(wù)器
node server.js
打開一個新的命令提示符,并使用以下命令運(yùn)行客戶端
node client.js
當(dāng)我們運(yùn)行客戶端時。它將打印以下輸出
Employee Details for Employee Id: 1
{ id: 1,
email: 'abcd@abcd.com',
firstName: 'First1',
lastName: 'Last1'
}
運(yùn)行截圖
原文地址
How to Easily use GRPC and Protocol Buffers with NodeJS
關(guān)于GRPC Streams in NodeJS參考作者原文
到此這篇關(guān)于如何通過NodeJS輕松使用GRPC和協(xié)議緩沖區(qū)的文章就介紹到這了,更多相關(guān)NodeJS使用GRPC和協(xié)議緩沖區(qū)內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
nodejs 使用nodejs-websocket模塊實現(xiàn)點(diǎn)對點(diǎn)實時通訊
這篇文章主要介紹了nodejs 使用nodejs-websocket模塊實現(xiàn)點(diǎn)對點(diǎn)實時通訊的實例代碼,代碼簡單易懂,非常不錯,具有一定的參考借鑒價值,需要的朋友可以參考下2018-11-11node.js中的fs.truncateSync方法使用說明
這篇文章主要介紹了node.js中的fs.truncateSync方法使用說明,本文介紹了fs.truncateSync的方法說明、語法、接收參數(shù)、使用實例和實現(xiàn)源碼,需要的朋友可以參考下2014-12-12Node.js實用代碼段之獲取Buffer對象字節(jié)長度
這篇文章主要介紹了Node.js實用代碼段之獲取Buffer對象字節(jié)長度,需要的朋友可以參考下2016-03-03Node.JS枚舉統(tǒng)計當(dāng)前文件夾和子目錄下所有代碼文件行數(shù)
這篇文章主要介紹了Node.JS枚舉統(tǒng)計當(dāng)前文件夾和子目錄下所有代碼文件行數(shù),本文給大家介紹的非常詳細(xì),具有一定的參考借鑒價值,需要的朋友可以參考下2019-08-08Nodejs?Sequelize手冊學(xué)習(xí)快速入門到應(yīng)用
這篇文章主要為大家介紹了Nodejs?Sequelize手冊學(xué)習(xí)快速入門到應(yīng)用,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪2022-10-10Node.js創(chuàng)建HTTP文件服務(wù)器的使用示例
我們的目的比較簡單,使用Node.js創(chuàng)建一個HTTP協(xié)議的文件服務(wù)器,你可以使用瀏覽器或其它下載工具到文件服務(wù)器上下載文件,具有一定的參考價值,感興趣的小伙伴們可以參考一下2018-05-05