egg-rpc plugin for egg framework
$ npm i egg-rpc-base --save
Change ${app_root}/config/plugin.js
to enable egg-rpc plugin:
exports.rpc = {
enable: true,
package: 'egg-rpc-base',
};
By default, We use zookeeper for service discovery. Therefore you need to configure the zk address:
// ${app_root}/config/config.${env}.js
config.rpc = {
registry: {
address: '127.0.0.1:2181', // configure your real zk address
},
};
We plan to provide more implementations of service discovery. And also you can implement it by yourself, you can follow this article
By using the plugin, you can call rpc services provided by other system.
We use protobuf interface definition lanaguge to describe the RPC service. So you need to get the *.proto files and put them into ${app_root}/proto
folder
Configure RPC Client information in ${app_root}/config/config.{env}.js:
// ${app_root}config/config.${env}.js
exports.rpc = {
client: {
responseTimeout: 3000,
},
};
responseTimeout
(optional): RPC timeout in milliseconds, default value is 3000
${app_root}/config/proxy.js
is a very important config file for rpc client, you should configure the services you needed, then executing the egg-rpc-generator
tool to generate the proxy files.
Let's see a simple example of proxy.js. It declare a interface named: org.eggjs.rpc.test.ProtoService
provided by rpc-demo
application
'use strict';
module.exports = {
services: [{
appName: 'rpc-demo',
api: {
ProtoService: 'org.eggjs.rpc.test.ProtoService',
},
}],
};
Refer this acticle for more details
Run egg-rpc-generator
to generate the proxy files. After running success, it will generate a file: ProtoService.js
under ${app_root}/app/proxy
$ 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"
You can call the RPC service by using ctx.proxy.proxyName
. The proxyName is key
value of api object you configure in proxy.js. In our example, it's ProtoService, and proxyName using lower camelcase, so it's 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;
As above, you can call remote service as a local method.
By using the plugin, you also can publish your own RPC service to other system.
Writing the *.proto files, and put them into ${app_root}/proto
folder
# 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;
}
Configure RPC Server information in ${app_root}/config/config.{env}.js:
module.exports = {
rpc: {
server: {
namespace: 'org.eggjs.rpc.test',
},
},
},
namespace
(required): the default namespace of all rpc serviceselfPublish
(optional): whether every node process publish service independent, default is trueport
(optional): the TCP port will be listen onmaxIdleTime
(optional): server will disconnect the socket if idle for long timeresponseTimeout
(optional): Number of milliseconds to wait for a response to begin arriving back from the remote system after sending a requestcodecType
(optional): recommended serialization method,default is protobuf
Put your implementation code under ${app_root}/app/rpc
folder
// ${app_root}/app/rpc/ProtoService.js
exports.echoObj = async function(req) {
return {
code: 200,
message: 'hello ' + req.name + ', you are in ' + req.group,
};
};
RPC Client Unittest (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');
});
});
RPC Server Unittest
'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);
});
});
For more details of app.rpcRequest
, you can refer to this acticle
- RPC in Nodejs Part One
- Cross-Language Interoperability between Eggjs & SOFA
- Custom Service Discovery in Eggjs
- RPC Proxy Configuration in Eggjs
- RPC Unittest in Eggjs
Please let us know how can we help. Do check out issues for bug reports or suggestions first.
To become a contributor, please follow our contributing guide.