gRPC:由 Google 开发的一个高性能、开源和通用性强的 RPC 框架

gRPC:由 Google 开发的一个高性能、开源和通用性强的 RPC 框架

动机

我们 Node 项目的后端请求没有直接使用 HTTP 模块,而是采用了 gRPC on Node.js[1](@grpc/grpc-js),而之前没有接触过。

概念

学一个东西最快的方式,就是先找个 DEMO 跑起来(我已经找到了),直接感受下。不过 gPRC 属实有点复杂,我们还是要从几个简单的概念开始认识。

gRPC

是 Google RPC 的简写,是 Google 基于 RPC 开发的一个通信框架(相当于是 PRC 一个实现方), 也是一套通用的通信方案。因此,「gRPC 是语言无关的,并不限制是用哪个语言」(比如:JavaScript(Node 平台) 、Java 等),你可以在官网《Supported languages》[2]里看到 gRPC 支持的语言,并跟随里面的例子,快速开始。

RPC

全称是“Remote Procedure Call”,即“远程过程调用”,是一种远程通信模型,这个模型定义了一个计算机程序在不同的地址空间中,如何调用另外一个地址空间的子程序或函数。

「RPC 中并没有规定使用何种通信协议传输数据」,既可以使用 HTTP,也可以使用 TCP、UDP、HTTP2。像 gRPC 内部就是使用 HTTP2 这个 HTTP 协议的最新版本实现数据传输的。

Protocol Buffers

Protocol Buffers[3] 简称 ProtoBuf,跟 JSON 一样,就是一种数据格式,官方叫“序列化协议”,存储这种数据格式的文件使用的是 .proto 后缀。它是由 Google 开发的,与相对于数据格式协议(如 XML 和 JSON 等)一样,与平台、语言无关,不过更小、更快。

在 gRPC 中,Protocol Buffers 被用作 IDL(Interface Definition Language,接口定义语言)使用(目前使用最新的第 3 版居多,又称“proto 3”[4]),所以你会看到一个个服务都是在一个个 .proto文件中定义的。

最后要说的是,相对于在 Node 端直接使用 HTTP 模块,从使用感受上,gRPC 对开发者而言,更像是是一个本地调用,而不是远程调用。

快速开始

我们这里的快速开始教程,基于官方 Node 端教程[5]仓库代码(grpc/grpc-node)[6]内容整合而成,学有余力的同学可以自己看看。

下载 example

# Clone the repository to get the example code
$ git clone https://github.com/grpc/grpc-node.git
# Navigate to the node example
cd grpc-node/examples
# Install the example's dependencies
$ npm install
# Navigate to the dynamic codegen "hello, world" Node example:
cd helloworld/dynamic_codegen

运行 gPRC 应用

# 启动服务
$ node greeter_server.js
# 运行客户端
$ node greeter_client.js

Greeting: Hello world

程序运行后,我们会在控制台看到“Greeting: Hello world”的消息输出。

至此,我们就成功运行起来了一个 gPRC 应用 了。

增加一个新的 gRPC 服务方法

现在,我们修改下服务,增加一个调用方法供客户端消费。查看 greeter_server.js 文件,会看到以下代码:

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

var grpc = require('@grpc/grpc-js');
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;

这里 helloworld.proto就是定义服务接口的地方」,使用 Protocol Buffers 数据格式定义;helloworld.proto 文件接口都定义在了 helloworld 这个命名空间下了。

现在打开 grpc-node/examples/protos/helloworld.proto 文件:

// The greeting service definition.
service Greeter {
// Sends a greeting
rpc SayHello (HelloRequest) returns (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;
}

目前,我们只要知道服务器和客户端“stub”都定义了一个 SayHello 的 RPC 方法,这个方法从客户端接收一个 HelloRequest 类型的参数,并从服务器返回一个 HelloReply 类型的响应。
 
我们更新一下,给 Greeter 服务增加一个新的 SayHelloAgain 方法,请求和响应类型与之前相同:

// The greeting service definition.
service Greeter {
// Sends a greeting
rpc SayHello (HelloRequest) returns (HelloReply) {}
// Sends another greeting
rpc SayHelloAgain (HelloRequest) returns (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;
}

保存文件!

更新并运行程序

我们加了一个新的服务定义,但是没有具体实现,这块实现代码需要我们人工编写。打开 greeter_server.js 文件,在 main() 方法内部增加 sayHelloAgain 方法实现:

function sayHello(call, callback) {
callback(null, {message: 'Hello ' + call.request.name});
}

function sayHelloAgain(call, callback) {
callback(null, {message: 'Hello again, ' + call.request.name});
}

function main() {
var server = new grpc.Server();
server.addService(hello_proto.Greeter.service,
{sayHello: sayHello, sayHelloAgain: sayHelloAgain});
server.bindAsync('0.0.0.0:50051', grpc.ServerCredentials.createInsecure(), () => {
server.start();
});
}

我们照葫芦(sayHello)画瓢(sayHelloAgain),完成 sayHelloAgain 服务方法的代码实现。

更新客户端

在同一个目录中,打开 greeter_client.js 文件,像这样调用新的方法:

function main() {
var client = new hello_proto.Greeter('localhost:50051',
grpc.credentials.createInsecure());
client.sayHello({name: 'you'}, function(err, response) {
console.log('Greeting:', response.message);
});
client.sayHelloAgain({name: 'you'}, function(err, response) {
console.log('Greeting:', response.message);
});
}

在客户端,我们增加了调用 sayHelloAgain 服务的代码,然后打印响应数据。

运行!

再一次运行我们的项目代码:

# 启动服务
$ node greeter_server.js
# 运行客户端
$ node greeter_client.js

Greeting: Hello world
Greeting: Hello again,world

多了一个 “Greeting: Hello again,world”输出,我们的修改完成!

增加拦截器

gRPC 还支持客户端拦截器的添加。在客户端调用服务时,可以在携带参数之后,跟上选项参数(options),其中支持通过 interceptors 参数,传入一个由拦截器函数组成的一个数组。

client.sayHello({ name'world' }, { interceptors: [logger] }, (err, response) => {
  console.log('Greeting:', response.message)
})

logger 拦截器的定义如下:

const logger = function (options, nextCall{
  console.log("[interceptor]", { options, nextCall });
  return new grpc.InterceptingCall(nextCall(options), {
    startfunction (metadata, listener, next{
      // 1) 执行一些前置任务
      console.log("start", metadata);

      // 调用下一步
      next(metadata, {
        onReceiveMessagefunction (message, next{
          // 3) 在请求过程中拦截消息
          console.log("onReceiveMessage", message);

          // 调用下一步
          next(message);
        },
        onReceiveStatusfunction (status, next{
          // 4) 在请求处理完成后拦截响应状态
          console.log("onReceiveStatus", status);

          // 调用下一步
          next(status);
        },
      });
    },
    sendMessagefunction (message, next{
      // 2) 发送消息前的处理
      console.log("sendMessage", message);

      // 调用下一步
      next(message);
    },
    halfClosefunction (next{
      next()
    },
    cancelfunction (message, next{
      next(message)
    }
  });
};

拦截器内部提供了 startsendMessagehalfCloseca 这些涉及请求周期范围内的钩子函数。另外 start 钩子函数内部,还提供 onReceiveMessageonReceiveStatus钩子函数,针对响应数据的过程进行干预。在这些钩子函数中,通过 next() 函数将请求传递给下一个拦截器处理

进一步了解

如果想要进一步了解项目文件代码的含义,可以参考官方《Basics tutorial》[7]教程。

参考资料

[1]

gRPC on Node.js: https://github.com/grpc/grpc-node

[2]

《Supported languages》: https://grpc.io/docs/languages/

[3]

Protocol Buffers: https://protobuf.dev/overview/

[4]

第 3 版居多,又称“proto 3”: https://protobuf.dev/programming-guides/proto3

[5]

官方 Node 端教程: https://grpc.io/docs/languages/node/

[6]

仓库代码(grpc/grpc-node): https://github.com/grpc/grpc-node

[7]

《Basics tutorial》: https://grpc.io/docs/languages/node/basics/


原文始发于微信公众号(写代码的宝哥):gRPC:由 Google 开发的一个高性能、开源和通用性强的 RPC 框架

版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 举报,一经查实,本站将立刻删除。

文章由极客之音整理,本文链接:https://www.bmabk.com/index.php/post/243944.html

(0)
小半的头像小半

相关推荐

发表回复

登录后才能评论
极客之音——专业性很强的中文编程技术网站,欢迎收藏到浏览器,订阅我们!