1、什么是rpc
RPC远程调用目的:通过像调用本地服务一样远程调用另一台服务器上的服务来完成需求。
可参考官网:
2、简单过程
3、使用协议缓冲区(Working with Protocol Buffers)
可参考官网
developers.google.com/protocol-bu…
4、过程
1、定义 结构
message Person {
string name = 1;
int32 id = 2;
bool has_ponycopter = 3;
}
复制代码
2、写服务端
// The greeter 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;
}
复制代码
3、写客户端
4、起服务测试
5、自己 起一个试试
1、新建一个文件夹 mkdir grpc-node-demo
2、初始化 npm init -y
3、npm grpc 服务 npm install grpc @grpc/proto-loader
4、启动 server node server.js
5、启动 client node client.js
6、注意端口 不要被别的服务占用,还有一点是每次修改 server.js 需要重启 服务
复制代码
- 新建 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('helloTest.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();
//添加服务 helloTest 为服务名 要跟.proto 文件一致
server.addService(hello_proto.helloTest.service, {
//实现 SayHello 要跟.proto 文件一致定义的方法名一致
//call 获取请求信息, callback用来向客户端返回信息
SayHiMark : (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:8888',grpc.ServerCredentials.createInsecure());
//启动服务
server.start();
console.log('服务已经启动,请启动客户端 ........');
复制代码
- 新建 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('helloTest.proto',{
keepCase:true,
longs:String,
enums:String,
defaults:true,
oneofs:true,
});
//创建包 helloworld属性 要跟.proto 文件的包名一致
const hello_proto = grpc.loadPackageDefinition(packageDefinition).helloworld
//初始化客户端
console.log('init client');
//hello 方法是服务名 要跟.proto 文件的服务名一致 第一个参数是ip和端口要跟服务端保持一致
const client = new hello_proto.helloTest('127.0.0.1:8888',grpc.credentials.createInsecure());
//调用服务的 SayHello 方法 并 按照proto 约定传参
client.SayHiMark({name: 'mark_fu', age : 18}, (err,response) => {
if(err){
console.log(err);
return ;
}
console.log(response.message);
});
复制代码
- 新建 helloTest.proto
syntax = "proto3";
package helloworld;
// 定义服务名 helloTest
service helloTest {
// 定义服务 SSayHiMark 方法
// HelloRequest 请求格式
// HelloResponse 返回格式
rpc SayHiMark(HelloRequest) returns(HelloResponse) {}
}
// 定义调用服务需要传递的参数 和 返回的参数。
message HelloRequest {
//这里的 1 代表第一个参数 2 代表第二个参数
string name = 1;
int32 age = 2;
}
// 定义返回参数的格式
message HelloResponse {
//定义第一个参数
string message = 1;
// 这里我没有定义 age,那么response中也不会有
}
复制代码
- 结果
- 非常 nice , 大功告成 !
参考 文档
6、本项目 github 地址
更多精彩,即将袭来......