发布时间:2018年7月22日-4分钟阅读
节点中的gRPC
这是我们从这个系列的第一部分学习的延续。如果你直接登陆这里,我强烈建议你先看看。
在这部分,我们将实现一个小型的Todo应用服务器和客户端,以更好地理解gRPC。在开始写代码之前,我们最好先弄一个土地的叠加,并定义我们要构建的东西。这将帮助我们建立一个所有移动部件的心理模型。
基础知识
在一个典型的gRPC(或任何一般的RPC系统)中,客户端和服务器之间的通信描述如下。
gRPC客户端和gRPC服务器之间的信息流。
客户端和服务器都实现了自己的gRPC存根。这些存根提供了gRPC运行时的接口,并允许对原消息和服务进行编程访问。RPC运行时负责来回发送消息,并负责对它们进行marshal和unmarshal。
明确了这一点之后,我们就开始实现我们的客户端-服务器系统吧。我们将按照以下顺序进行--
- 启动项目、文件夹结构和依赖关系。
- 实现Todo App的proto(消息和服务定义)。
- 实现服务器(加载proto,实现服务处理程序)。
- 实现客户端(加载proto,初始化客户端并进行rpc调用)。
奖励。
对GRPC和REST服务器进行基准测试。
说话很便宜。给我看看代码。- Linus Torvalds。
你可以在下面的代码中跟进,或者查看Github repo中的代码。
1. 初始化项目、文件夹结构和依赖关系--。
创建一个新的目录,并使用我们的老朋友npm来初始化一个node项目。使用
npm init
并根据提示初始化项目。
窍门:使用
npm init -y快速跳过所有提示,使用默认值。
现在继续创建一个src文件夹,我们所有的代码将存在其中。同时运行下面的脚本来安装我们需要的所有依赖包------。
npm i -S grpc @grpc/proto-loader
除了用于快速启动我们的客户端和服务器,我们还在我们创建的package.json的scripts对象中添加了两个npm脚本,如下图所示----------。
"start:server": "node src/server. js"
"start:client": "node src/client.js"
你的最终package.json文件应该是这样的--。
{
"name": "todo-app-grpc",
"version": "1.0.0",
"description": "A sample client and server to demonstrate use of gRPC with NodeJS.",
"main": "index.js",
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1",
"start:server": "node src/server.js",
"start:client": "node src/client.js"
},
"repository": {
"type": "git",
"url": "git+https://github.com/shobhitchittora/todo-app-grpc.git"
},
"keywords": [],
"author": "",
"license": "ISC",
"bugs": {
"url": "https://github.com/shobhitchittora/todo-app-grpc/issues"
},
"homepage": "https://github.com/shobhitchittora/todo-app-grpc#readme",
"dependencies": {
"@grpc/proto-loader": "^0.1.0",
"grpc": "^1.13.0"
}
}
2. 定义Todo App的消息和服务--。
在我们的src文件夹中创建一个名为 "todo.proto "的文件。我们首先给我们的proto一个包名,并定义一个空的Service。
syntax = "proto3";
package todo_app_package;
service TodoApp {
}
然后,我们定义出Todo消息结构-
message Todo {
int32 id = 1;
string name = 2;
bool done = 3;
}
而我们的Todos信息结构,是所有Todos的集合--。
message Todos {
repeated Todo todos = 1;
}
我们还定义了一个TodoId消息,通过它来获取rpc方法通过id获取todo。我们还定义了一个空的Todo payload来请求所有的todo。
message TodoId {
int32 id = 1;
}
message TodoEmpty {
}
最后,我们继续为我们的TodoApp服务定义我们的rpc(s)。getTodo rpc接收一个TodoId消息并返回一个Todo消息。getAll rpc以TodoEmpty消息作为请求,并返回一个Todos响应消息。
/**
* Author - Shobhit Chittora
*/
syntax = "proto3";
package todo_app_package;
service TodoApp {
rpc getTodo (TodoId) returns (Todo) {}
rpc getAll (TodoEmpty) returns (Todos) {}
}
message Todos {
repeated Todo todos = 1;
}
message Todo {
int32 id = 1;
string name = 2;
bool done = 3;
}
message TodoId {
int32 id = 1;
}
message TodoEmpty {
}
3. 使用一个json对象来模拟数据库--。
为了本教程的简洁,让我们只添加一个json文件,它将作为我们的内存数据库。在我们的src文件夹下创建一个todos_db.json文件。
{
"todos": [
{
"id": 1,
"name": "Get eggs!",
"done": false
},
{
"id": 2,
"name": "Learn something new.",
"done": true
},
{
"id": 3,
"name": "Exercise.",
"done": false
},
{
"id": 4,
"name": "Go to work.",
"done": true
}
]
}
4. 创建服务器(加载proto和启动服务器)--。
在src文件夹中创建一个server.js文件,并将下面的所有内容放入其中。
const grpc = require('grpc');
const loader = require('@grpc/proto-loader');
const todos = require('./todos_db.json');
class TodoAppHandler {
getAll(_, callback) {
return callback(null, todos);
}
getTodo(call, callback) {
const result = todos.todos.filter(({ id }) => id === call.request.id);
return callback(null, result[0]);
}
}
const PATH = '0.0.0.0:8080';
const createServer = function (bindPath, handler) {
loader.load('todo.proto', { includeDirs: ['./src'] })
.then((packageDefinition) => {
const package = grpc.loadPackageDefinition(packageDefinition);
const service = package.todo_app_package.TodoApp.service;
const server = new grpc.Server();
server.addService(service, handler);
server.bind(bindPath, grpc.ServerCredentials.createInsecure());
server.start();
console.log('Server running on 8080');
});
}
createServer(PATH, new TodoAppHandler);
上面的代码加载了依赖关系(grpc lib和加载器),以及我们的todos json。然后我们创建一个TodoAppHandler类,这个类的实例包含了我们的客户端可以调用的所有rpc方法。gRPC为我们提供了一个服务实例,可以在我们的服务器实例中进行配置以使rpc工作。最后,绑定服务,调用我们服务器上的start方法,然后就可以了。
然后,你可以使用脚本------启动服务器。
npm run start:server
我们在初始化时添加的。
5. 创建客户端(加载proto并发出请求)--。
继续在src目录下创建一个client.js文件,并将下面的代码粘贴进去。
const grpc = require('grpc');
const loader = require('@grpc/proto-loader');
const bindPath = '0.0.0.0:8080';
loader.load('todo.proto', { includeDirs: ['./src'] })
.then((packageDefinition) => {
const package = grpc.loadPackageDefinition(packageDefinition);
const Client = package.todo_app_package.TodoApp;
const client = new Client(bindPath, grpc.credentials.createInsecure());
client.getAll({}, function (err, res) {
if (err) {
return console.log(err);
}
console.log('todos: ');
return console.log(res.todos);
});
client.getTodo({ id: 1 }, function (err, res) {
if (err) {
return console.log(err);
}
return console.log(res);
});
client.getTodo({ id: 3 }, function (err, res) {
if (err) {
return console.log(err);
}
return console.log(res);
});
});
客户端的工作原理和服务器差不多。gRPC为我们提供了一个Client实例,我们可以通过它调用服务器的处理方法来获取暴露的数据。
你可以使用下面的命令运行客户端----。
npm run start:client
就这样,你已经在Node中写出了一个非常小但性能良好的gRPC客户端-服务器系统。请看下面的章节,了解做进一步修补的思路。
接下来的步骤
- 继续并实现其他方法,如putTodo,它应该写一个新的todo,updateTodo,它应该更新现有的todo和其他。
- 在这里查看getAll todos的流媒体api样本。试着理解并把别人的api也转换为流媒体api。此外,还可以考虑双向流。
- 奖励:为todo应用添加一个前端(查看Todomvc项目获得灵感)。