什么是rpc
- RPC(Remote Procedure Call)远程过程调用,简单的理解是一个节点请求另一个节点提供的服务
- 本地过程调用:如果需要将本地student对象的age+1,可以实现一个addAge()方法,将student对象传入,对年龄进行更新之后返回即可,本地方法调用的函数体通过函数指针来指定。
- 远程过程调用:上述操作的过程中,如果addAge()这个方法在服务端,执行函数的函数体在远程机器上,如何告诉机器需要调用这个方法呢?
具体细节可以参考:
https://www.jianshu.com/p/7d6853140e13
这就是rpc的作用
RPC远程调用目的:通过像调用本地服务一样远程调用另一台服务器上的服务来完成需求。
相关参考
http://doc.oschina.net/grpc?t=60135
关于grpc
gRPC 是一个高性能、开源和通用的 RPC 框架,面向移动和 HTTP/2 设计。目前提供 C、Java 和 Go 语言版本,分别是:grpc, grpc-java, grpc-go. 其中 C 版本支持 C, C++, Node.js, Python, Ruby, Objective-C, PHP 和 C# 支持.
gRPC 基于 HTTP/2 标准设计,带来诸如双向流、流控、头部压缩、单 TCP 连接上的多复用请求等特。这些特性使得其在移动设备上表现更好,更省电和节省空间占用
说了这么多,其实就是很好用并且各个语言都支持
关于protobuf
gRPC 有一套自己的编写格式; 就是 protocol buffers
具体的格式定义可以看官网 (需要FQ)
https://developers.google.com/protocol-buffers/docs/overview
我们需要按照规定地格式来, 定义 service
和 message
node 使用grpc
项目地址:
https://github.com/grpc/grpc-node
项目初始化
#创建grpcdemo 文件夹
mkdir grpcdemo
#进入项目文件夹
cd grpcdemo
#初始化node 项目
npm init -y
#安装grpc和@grpc/proto-loader
npm install grpc @grpc/proto-loader
创建相关文件
echo "" > server.js
echo "" > client.js
echo "" > hello.proto
- server.js 创建服务端
- client.js 调用服务端
- hello.proto grpc通过
protocol buffers
定义接口约定服务
编写 .proto 文件内定义服务
打开创建好的hello.proto
文件,写入以下内容
syntax = "proto3";
//包名
package helloworld;
// 定义服务名 hello。
service hello {
// 定义服务SayHello方法
// HelloRequest 请求格式
// HelloResponse返回格式
rpc SayHello(HelloRequest) returns(HelloResponse) {}
}
// 定义调用服务需要传递的参数 和 返回的参数。
message HelloRequest {
//这里的 1 代表第一个参数 2 代表第二个参数
string name = 1;
int32 age = 2;
}
// 定义返回参数的格式
message HelloResponse {
//定义第一个参数
string message = 1;
// 这里我没有定义 age,那么response中也不会有
}
编写服务端
打开创建好的server.js
,写入以下内容
//包引入
const grpc = require('grpc');
const protoLoader = require('@grpc/proto-loader');
//包定义信息,加载 hello.proto 文件的对应信息 这个文件服务端和客户端都会使用到
/*
参数:
filename –一个或多个要加载的文件路径。 可以是绝对路径,也可以是相对于包含路径的路径。
选项 -
配置选项:
keepCase –保留字段名称。 默认设置是将它们更改为驼峰式。
longs –应该用于表示long值的类型。 有效选项是Number和String 。 默认为库中的Long对象类型。
enums –应该用于表示enum值的类型。 唯一有效的选项是String 。 默认为数值。
bytes –应该用于表示bytes值的类型。 有效选项是Array和String 。 默认是使用Buffer 。
defaults –在输出对象上设置默认值。 默认为false 。
arrays –为空数组设置缺少的数组值,即使defaults值为false 。 默认为false 。
objects –即使defaults值为false也为缺少的对象值设置空对象。 默认为false 。
oneofs –将虚拟的oneof属性设置为当前字段的名称
includeDirs –搜索导入的.proto文件的路径。
*/
const packageDefinition = protoLoader.loadSync('hello.proto',{
keepCase:true,
longs:String,
enums:String,
defaults:true,
onefs:true,
});
//创建包 helloworld属性 要跟.proto 文件的包名一致
const hello_proto = grpc.loadPackageDefinition(packageDefinition).helloworld;
//创建server
const server = new grpc.Server();
//添加服务 hello为服务名 要跟.proto 文件一致
server.addService(hello_proto.hello.service, {
//实现 SayHello 要跟.proto 文件一致定义的方法名一致
//call 获取请求信息, callback用来向客户端返回信息
SayHello : (call,callback) => {
try{
//获取.proto 文件里定义的 name,age 也就是请求参数
let {name,age} = call.request;
//判断有没有它,有就执行callback()【这是一个回调函数】,没有,就不执行
//callback 两个参数 第一个参数,如果报错可以传入 error 第二参数按proto 文件里的约定传值
callback && callback(null,{ message:`我叫${name},年龄${age}岁`});
}catch (error) {
console.log('服务端出错', error);
callback && callback(error);
}
}
});
//绑定ip和端口
server.bind('127.0.0.1:55555',grpc.ServerCredentials.createInsecure());
//启动服务
server.start();
console.log('server start ........');
编写客户端
打开创建好的client.js
,写入下面的内容
//包引入
const grpc = require('grpc');
const protoLoader = require('@grpc/proto-loader');
//包定义信息,加载 hello.proto 文件的对应信息 这个文件服务端和客户端都会使用到
/*
参数:
filename –一个或多个要加载的文件路径。 可以是绝对路径,也可以是相对于包含路径的路径。
选项 -
配置选项:
keepCase –保留字段名称。 默认设置是将它们更改为驼峰式。
longs –应该用于表示long值的类型。 有效选项是Number和String 。 默认为库中的Long对象类型。
enums –应该用于表示enum值的类型。 唯一有效的选项是String 。 默认为数值。
bytes –应该用于表示bytes值的类型。 有效选项是Array和String 。 默认是使用Buffer 。
defaults –在输出对象上设置默认值。 默认为false 。
arrays –为空数组设置缺少的数组值,即使defaults值为false 。 默认为false 。
objects –即使defaults值为false也为缺少的对象值设置空对象。 默认为false 。
oneofs –将虚拟的oneof属性设置为当前字段的名称
includeDirs –搜索导入的.proto文件的路径。
*/
const packageDefinition = protoLoader.loadSync('hello.proto',{
keepCase:true,
longs:String,
enums:String,
defaults:true,
onefs:true,
});
//创建包 helloworld属性 要跟.proto 文件的包名一致
const hello_proto = grpc.loadPackageDefinition(packageDefinition).helloworld
//创建客户端
console.log('init client');
//hello 方法是服务名 要跟.proto 文件的服务名一致 第一个参数是ip和端口要跟服务端保持一致
const client = new hello_proto.hello('127.0.0.1:55555',grpc.credentials.createInsecure());
//调用服务的 SayHello 方法 并 按照proto 约定传参
client.SayHello({name: 'makalo', age : 18}, (err,response) => {
if(err){
console.log(err);
return ;
}
console.log(response.message);
});
测试
启动服务端
node server.js
执行客户端
node client.js