egg-rpc 插件是为 egg 提供调用和发布 RPC 服务的能力
$ npm i egg-rpc-base --save
通过 ${app_root}/config/plugin.js
配置启动 egg-rpc 插件:
exports.rpc = {
enable: true,
package: 'egg-rpc-base',
};
默认的服务发现依赖于 zookeeper
,所以你需要配置一个 zk 的地址
// ${app_root}/config/config.${env}.js
config.rpc = {
registry: {
address: '127.0.0.1:2181', // 根据实际情况配置
},
};
后续我们还会提供更多的服务发现实现,你也可以根据自己的需求实现自己的 registry,详细可以参考:自定义服务发现实现
该插件提供调用其他系统暴露的 rpc 接口的能力
以 protobuf 为例,将 *.proto 文件放置到 ${app_root}/proto
目录下
可以在 ${app_root}/config/config.${env}.js
做一些全局性的配置
// ${app_root}config/config.${env}.js
exports.rpc = {
client: {
responseTimeout: 3000,
},
};
responseTimeout
(可选): RPC 的超时时长,默认为 3 秒
RPC 客户端还有一个重要的配置文件是:${app_root}/config/proxy.js
,你需要把你调用的服务配置到里面,然后通过 egg-rpc-generator
工具帮你生成本地调用代码。
让我们看一个最简单的配置,它的基本含义是:我需要调用 rpc-demo
应用暴露的 org.eggjs.rpc.test.ProtoService
这个服务。
'use strict';
module.exports = {
services: [{
appName: 'rpc-demo',
api: {
ProtoService: 'org.eggjs.rpc.test.ProtoService',
},
}],
};
详细的配置可以参考 RPC 代理(Proxy)配置
配置好了,运行 egg-rpc-generator
生成本地调用代理文件。运行成功后,会在 ${app_root}/app/proxy
目录下生成一个 ProtoSerivce.js
文件
$ egg-rpc-generator
[EggRpcGenerator] framework: /egg-rpc-demo/node_modules/egg, baseDir: /egg-rpc-demo
[ProtoRPCPlugin] found "org.eggjs.rpc.test.ProtoService" in proto file
[ProtoRPCPlugin] save all proto info into "/egg-rpc-demo/run/proto.json"
通过 ctx.proxy.proxyName
来访问生成的 proxy 代码,proxyName 就是上面 proxy.js 配置的 api 键值对中的 key。例如:上面配置的 ProtoService,但是需要特别注意的是 proxyName 会自动转成小驼峰形式,所以就是 ctx.proxy.protoService
。
'use strict';
const Controller = require('egg').Controller;
class HomeController extends Controller {
async index() {
const { ctx } = this;
const res = await ctx.proxy.protoService.echoObj({
name: 'gxcsoccer',
group: 'A',
});
ctx.body = res;
}
}
module.exports = HomeController;
和调用本地方法体验一模一样。
该插件还可以暴露 SOFARPC 接口给其他应用调用
同样以 protobuf 为例,将接口定义放置到 ${app_root}/proto
目录下。
# ProtoService.proto
syntax = "proto3";
package org.eggjs.rpc.test;
option java_multiple_files = true; // 可选
option java_outer_classname = "ProtoServiceModels"; // 可选
service ProtoService {
rpc echoObj (EchoRequest) returns (EchoResponse) {}
}
message EchoRequest {
string name = 1;
Group group = 2;
}
message EchoResponse {
int32 code = 1;
string message = 2;
}
enum Group {
A = 0;
B = 1;
}
在 ${app_root}/config/config.${env}.js
做一些配置
module.exports = {
rpc: {
server: {
namespace: 'org.eggjs.rpc.test',
},
},
},
namespace
(必选): 接口的命名空间,所有的暴露的接口默认都在该命名空间下selfPublish
(可选): 是否每个 worker 进程独立暴露服务。nodejs 多进程模式下,如果多个进程共享一个端口,在 RPC 这种场景可能造成负载不均,所以 selfPublish 默认为 true,代表每个进程独立监听端口和发布服务port
(可选): 服务监听的端口(注意:在 selfPublish=true 时,监听的端口是基于这个配置生成的)maxIdleTime
(可选): 客户端连接如果在该配置时长内没有任何流量,则主动断开连接responseTimeout
(可选): 服务端建议的超时时长,具体的超时还是以客户端配置为准codecType
(可选): 推荐的序列化方式,默认为 protobuf
在 ${app_root}/app/rpc
目录下放置接口的具体实现代码
// ${app_root}/app/rpc/ProtoService.js
exports.echoObj = async function(req) {
return {
code: 200,
message: 'hello ' + req.name + ', you are in ' + req.group,
};
};
调用服务的单元测试方式,使用 app.mockProxy
API 来 mock 服务
'use strict';
const mm = require('egg-mock');
const assert = require('assert');
describe('test/mock.test.js', () => {
let app;
before(async function() {
app = mm.app({
baseDir: 'apps/mock',
});
await app.ready();
});
afterEach(mm.restore);
after(async function() {
await app.close();
});
it('should app.mockProxy ok', async function() {
app.mockProxy('DemoService', 'sayHello', async function(name) {
await sleep(1000);
return 'hello ' + name + ' from mock';
});
const ctx = app.createAnonymousContext();
const res = await ctx.proxy.demoService.sayHello('gxcsoccer');
assert(res === 'hello gxcsoccer from mock');
});
});
在单元测试中,我们还可以通过 app.rpcRequest
接口来方便的测试我们自己暴露的 RPC 服务,例如:
'use strict';
const mm = require('egg-mock');
describe('test/index.test.js', () => {
let app;
before(async function() {
app = mm.app({
baseDir: 'apps/rpcserver',
});
await app.ready();
});
after(async function() {
await app.close();
});
it('should invoke HelloService', done => {
app.rpcRequest('org.eggjs.rpc.test.HelloService')
.invoke('hello')
.send([ 'gxcsoccer' ])
.expect('hello gxcsoccer', done);
});
});
详细 app.rpcRequest
的 api 可以参考:单元测试 RPC 服务的方法
请告知我们可以为你做些什么,不过在此之前,请检查一下是否有已经存在的Bug或者意见。
如果你是一个代码贡献者,请参考代码贡献规范。