[Linux翻译]用gRPC实现快速的IPC(第二部分)。

368 阅读5分钟

原文地址:medium.com/@chittorash…

原文作者:medium.com/@chittorash…

发布时间:2018年7月22日-4分钟阅读

image.png

节点中的gRPC

这是我们从这个系列的第一部分学习的延续。如果你直接登陆这里,我强烈建议你先看看。

在这部分,我们将实现一个小型的Todo应用服务器和客户端,以更好地理解gRPC。在开始写代码之前,我们最好先弄一个土地的叠加,并定义我们要构建的东西。这将帮助我们建立一个所有移动部件的心理模型。


基础知识

在一个典型的gRPC(或任何一般的RPC系统)中,客户端和服务器之间的通信描述如下。

image.png

gRPC客户端和gRPC服务器之间的信息流。

客户端和服务器都实现了自己的gRPC存根。这些存根提供了gRPC运行时的接口,并允许对原消息和服务进行编程访问。RPC运行时负责来回发送消息,并负责对它们进行marshal和unmarshal。

明确了这一点之后,我们就开始实现我们的客户端-服务器系统吧。我们将按照以下顺序进行--

  1. 启动项目、文件夹结构和依赖关系。
  2. 实现Todo App的proto(消息和服务定义)。
  3. 实现服务器(加载proto,实现服务处理程序)。
  4. 实现客户端(加载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项目获得灵感)。

www.deepl.com 翻译