Node.js:Hello gRPC

341 阅读5分钟

在这篇文章中,我们将一步步通过如何在 Node.js 中实现一个简单的 gRPC 服务。我们将构建一个 "Hello World" 示例,涵盖环境准备、定义服务接口、生成 gRPC 代码、实现服务、编写客户端调用服务以及运行和测试。

眼熟吧,GO:Hello gRPC

什么是 RPC 和 gRPC

RPC(Remote Procedure Call)

RPC(远程过程调用)是一种通过网络从远程计算机程序上执行过程(子程序或函数)的协议。RPC 使得程序可以像调用本地函数一样调用远程函数,隐藏了网络通信的细节。

gRPC

gRPC 是 Google 开发的一个高性能、通用的开源 RPC 框架。它基于 HTTP/2 标准,支持多种编程语言,并使用 Protocol Buffers 作为接口定义语言。gRPC 提供了负载均衡、跟踪、健康检查、认证等功能,非常适合微服务架构。

官方文档

Node SDK仓库

官方语言支持

这些是 gRPC 官方支持的编程语言、平台和操作系统版本:

语言操作系统编译器 / SDK
C/C++Linux, MacGCC 7.3.1+, Clang 6+
C/C++Windows 10+Visual Studio 2019+
C#Linux, Mac.NET Core, Mono 4+
C#Windows 10+.NET Core, .NET 4.5+
DartWindows, Linux, MacDart 2.12+
GoWindows, Linux, MacGo 1.13+
JavaWindows, Linux, MacJava 8+ (KitKat+ for Android)
KotlinWindows, Linux, MacKotlin 1.3+
Node.jsWindows, Linux, MacNode v8+
Objective-CmacOS 10.10+, iOS 9.0+Xcode 12+
PHPLinux, MacPHP 7.0+
PythonWindows, Linux, MacPython 3.7+
RubyWindows, Linux, MacRuby 2.3+
  1. 语言:列出支持的编程语言。
  2. 操作系统:列出支持的操作系统版本。
  3. 编译器 / SDK:列出相应语言需要的编译器或 SDK 版本。

新建项目目录

创建一个新目录作为项目的根目录,例如 hello-grpc,并在其中创建一个 package.json 文件来初始化项目:

mkdir hello-grpc
cd hello-grpc
npm init -y

环境准备

确保你已经安装了 Node.js 和 npm。然后安装 @grpc/grpc-js@grpc/proto-loader 包:

npm install @grpc/grpc-js @grpc/proto-loader

定义服务接口

在项目根目录中,创建一个名为 helloworld.proto 的文件,定义你的服务接口:

syntax = "proto3";
​
package helloworld;
​
// 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;
}

实现服务

在项目根目录下创建一个 server.js 文件,并实现 gRPC 服务:

const grpc = require('@grpc/grpc-js');
const protoLoader = require('@grpc/proto-loader');
const path = require('path');
​
// 加载 protobuf 文件
const PROTO_PATH = path.join(__dirname, 'helloworld.proto');
const packageDefinition = protoLoader.loadSync(PROTO_PATH, {
  keepCase: true, // 保持字段名大小写
  longs: String,
  enums: String,
  defaults: true,
  oneofs: true
});
const hello_proto = grpc.loadPackageDefinition(packageDefinition).helloworld;
​
// 实现 SayHello RPC 方法
function sayHello(call, callback) {
  // 返回带有问候消息的响应
  callback(null, {message: 'Hello ' + call.request.name});
}
​
// 启动 gRPC 服务器
function main() {
  const server = new grpc.Server();
  // 添加 Greeter 服务到服务器
  server.addService(hello_proto.Greeter.service, {sayHello: sayHello});
  // 绑定服务器到指定端口
  server.bindAsync('0.0.0.0:50051', grpc.ServerCredentials.createInsecure(), () => {
    server.start();
    console.log('Server running at http://0.0.0.0:50051');
  });
}
​
main();

代码解释

  1. 加载并解析 Proto 文件

    const PROTO_PATH = path.join(__dirname, 'helloworld.proto');
    const packageDefinition = protoLoader.loadSync(PROTO_PATH, {
      keepCase: true,
      longs: String,
      enums: String,
      defaults: true,
      oneofs: true
    });
    const hello_proto = grpc.loadPackageDefinition(packageDefinition).helloworld;
    

    这段代码加载并解析 helloworld.proto 文件,生成 gRPC 服务描述对象。

  2. 实现 gRPC 服务方法

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

    这里定义了 sayHello 方法,它接受一个请求对象 call 和一个回调函数 callback。该方法从请求中获取 name 字段,并返回一个带有问候消息的响应。

  3. 启动 gRPC 服务器

    function main() {
      const server = new grpc.Server();
      server.addService(hello_proto.Greeter.service, {sayHello: sayHello});
      server.bindAsync('0.0.0.0:50051', grpc.ServerCredentials.createInsecure(), () => {
        server.start();
        console.log('Server running at http://0.0.0.0:50051');
      });
    }
    main();
    

    这段代码创建了一个 gRPC 服务器实例,将 Greeter 服务添加到服务器,并绑定到 0.0.0.0:50051 端口。服务器启动后,会输出一条日志消息。

编写客户端调用服务

在项目根目录下创建一个 client.js 文件,并实现 gRPC 客户端:

const grpc = require('@grpc/grpc-js');
const protoLoader = require('@grpc/proto-loader');
const path = require('path');
​
// 加载 protobuf 文件
const PROTO_PATH = path.join(__dirname, 'helloworld.proto');
const packageDefinition = protoLoader.loadSync(PROTO_PATH, {
  keepCase: true, // 保持字段名大小写
  longs: String,
  enums: String,
  defaults: true,
  oneofs: true
});
const hello_proto = grpc.loadPackageDefinition(packageDefinition).helloworld;
​
// 创建 gRPC 客户端
function main() {
  const client = new hello_proto.Greeter('localhost:50051', grpc.credentials.createInsecure());
  const user = 'world'; // 默认用户名称
​
  // 调用 SayHello 方法
  client.sayHello({name: user}, (err, response) => {
    if (err) {
      console.error(err);
    } else {
      console.log('Greeting:', response.message);
    }
  });
}
​
main();

代码解释

  1. 加载并解析 Proto 文件

    const PROTO_PATH = path.join(__dirname, 'helloworld.proto');
    const packageDefinition = protoLoader.loadSync(PROTO_PATH, {
      keepCase: true,
      longs: String,
      enums: String,
      defaults: true,
      oneofs: true
    });
    const hello_proto = grpc.loadPackageDefinition(packageDefinition).helloworld;
    

    这段代码加载并解析 helloworld.proto 文件,生成 gRPC 服务描述对象。

  2. 创建客户端并调用服务

    function main() {
      const client = new hello_proto.Greeter('localhost:50051', grpc.credentials.createInsecure());
      const user = 'world';
    ​
      client.sayHello({name: user}, (err, response) => {
        if (err) {
          console.error(err);
        } else {
          console.log('Greeting:', response.message);
        }
      });
    }
    main();
    

    这段代码创建了一个连接到 localhost:50051Greeter 客户端,并调用 sayHello 方法。调用成功后,客户端会输出服务器返回的问候消息。

交互逻辑

  1. 服务端启动

    • 加载并解析 Proto 文件:服务端需要加载并解析定义 gRPC 服务的 Proto 文件。
    • 实现 gRPC 服务方法:实现 Proto 文件中定义的 gRPC 服务方法。
    • 启动 gRPC 服务器:启动 gRPC 服务器,使其开始监听客户端请求。
  2. 客户端调用

    • 加载并解析 Proto 文件:客户端也需要加载并解析同样的 Proto 文件。
    • 创建 gRPC 客户端:创建一个 gRPC 客户端实例,连接到服务端。
    • 调用 gRPC 方法:客户端调用服务端提供的 gRPC 方法。
  3. 服务端处理请求

    • 接收客户端请求:服务端接收到客户端的 gRPC 请求。
    • 执行服务方法逻辑:服务端执行对应的服务方法逻辑。
    • 返回响应给客户端:服务端将处理结果返回给客户端。
  4. 客户端接收响应

    • 接收服务端响应:客户端接收到服务端的响应。
    • 处理响应数据:客户端处理接收到的响应数据。

image.png

运行和测试

首先,运行服务器:

node server.js

然后,在另一个终端窗口,运行客户端:

node client.js

你应该会在客户端看到服务器的问候:

Greeting: Hello world

image.png

这就是在 Node.js 中使用 gRPC 实现 Hello World 示例的完整过程。希望这个示例对你有帮助!


通过这个示例,你应该已经掌握了在 Node.js 中使用 gRPC 的基本方法。你可以基于这个示例构建更复杂的 gRPC 服务,满足实际应用的需求。接下来的日子,我们将使用Fastify+gRPC封装基础项目,让我们下期再会~