An RPC framework implemented in Node.js with https://github.com/XadillaX/illyria2 which is in use of Huaban.com.
MIT License
& Project-Her
Install gensokyo
via npm:
$ [sudo] npm install gensokyo -g
Go into any directory, and then type
$ gensokyo new PROJECT_NAME
You will find a new project directory named PROJECT_NAME
is created.
Inside this directory, there are:
.
config
config.server.js
config.zookeeper.js
index.js
controller
echoController.js
filter
echoFilter.js
gensokyo.js
log
deleteme
node_modules
gensokyo
nomnom
package.json
router
echoRouter.js
config
includes some configuration files. for example, you want to add a config.foo.js
to system, you should add this file into config
and add a new line in index.js
to let others can access to it.
If you want use a specified project path, use rootPath
in parameter options
while Gensokyo.createServer
;
Use noZookeeper
in parameter options
while you won't use zookeeper to manage your nodes.
Use aliasRules
if you need expose API with their alias names. (refer here)
Use metric
if you want to monit the metrics. It needs reportPort
to open a web server to report metrics.
You can send metrics to Riemann intervally if you include riemann
in metric
.
metric: {
reportPort: 3333,
riemann: {
port: 5555,
host: "10.0.17.179",
transport: "tcp",
sendInterval: 60000
}
}
controller
includes logic code.
Each controller should named as *Controller.js
.
This is the code in pre-build controller file echoController
:
var util = require("util");
var Controller = require("gensokyo").Controller;
var EchoController = function(gensokyo) {
Controller.call(this, gensokyo);
};
util.inherits(EchoController, Controller);
EchoController.prototype.echo1 = function(req, resp, next) {
this.logger.info("echo1");
next();
};
EchoController.prototype.echo2 = function(req, resp, next) {
this.logger.info("echo2");
resp.message.ok = true;
//
if(Number.random(1, 100) < 50) {
resp.error("You're unlucky!");
return;
}
next();
};
module.exports = EchoController;
There are two types of filters: before-filter and after-filter.
They are all in filter
directory and named as *Filter.js
.
This is the example filter code:
var util = require("util");
var Filter = require("gensokyo").Filter;
var EchoFilter = function(gensokyo) {
Filter.call(this, gensokyo);
};
util.inherits(EchoFilter, Filter);
EchoFilter.prototype.before1 = function(req, resp, next) {
this.logger.info("before1");
next();
};
EchoFilter.prototype.before2 = function(req, resp, next) {
this.logger.info("before2");
next();
};
EchoFilter.prototype.after1 = function(req, resp, next) {
this.logger.info("after1");
next();
};
EchoFilter.prototype.after2 = function(req, resp, next) {
this.logger.info("after2");
next();
};
module.exports = EchoFilter;
Each server router is a logic chain which is like:
filter1 -> filter2 -> filter... -> logic1 -> logic2 -> logic... -> filter'1 -> filter'2 -> filter'...
When server received a message from client, a router chain will be triggered.
The filters before logic are called before-filters and the rests are after-filters.
The process can stop at any step and send the client back a message.
If the process is finished and no any message sent back, Gensokyo will send the message which is dealed in resp.message
automatically.
All the routers are defined in files named *Router.js
which are under directory router
.
You should defined a JSON object contains router name => router chain
key-value pair.
Attention: if your router filename is
fooRouter.js
, all the filter and logic function should infooController.js
andfooFilter.js
.
About the defination, refer to the code below:
/**
* each router example:
*
* + single controller
* - foo : "bar"
* - foo : [ "bar" ]
* - foo : { ctrller: "bar" }
*
* + multi controller
* - foo : [ [ "bar1", "bar2" ] ]
* - foo : { ctrller: [ "bar1", "bar2" ] }
*
* + with single/multiple `before filter`
* - foo : [ "before", "bar" ] ///< with only 2 elements in the array
* - foo : [ "before", [ "bar1", "bar2" ] ] ///< with only 2 elements in the array
* - foo : [ [ "before1", "before2" ], [ "bar" ] ] ///< with only 2 elements in the array
* - foo : { before: "before", ctrller: "bar" }
* - foo : { before: [ "before1", "before2" ], ctrller: "bar" }
*
* + with single/multiple `after filter`
* - foo : { ctrller: ..., after: [ ... ] }
* - foo : { ctrller: ..., after: "bar" }
*
* + with both `before` and `after` filters
* - foo : [ "before", "bar", "after" ] ///< must with 3 elements in the array
* - foo : [ [ ... ], [ ... ], [ ... ] ] ///< must with 3 elements in the array
* - foo : { before: "before" / [ ... ], ctrller: "bar" / [ ... ], after: "after" / [ ... ]}
*/
So here's an example of echoRouter.js
:
module.exports = {
echo1 : [ "before1", "echo2" ],
echo2 : [ "before1", [ "echo1", "echo2" ], "after1" ],
echo3 : "echo1",
echo4 : { before: [ "before1" ], ctrller: "echo1" }
};
The code above said
When server received a router named
echo -> echo2
(echoRouter.js
andecho2 chain
), the server will triggerecho2
chain, so the chain withEchoFilter::before1
,EchoController::echo1
,EchoController::echo2
andEchoFilter::after1
is triggered.
Incoming message usually appears in function(req, resp, next)
.
req
is so-called incoming message.
The Gencokyo object.
The server object.
The router JSON object.
Incoming parameters are inside this value.
The socket object.
Incoming time.
Return a string like "echo.echo1"
.
resp
is so-called outcoming message.
The Gensokyo object.
The server object.
You can store all the information you want to send to client in it.
For example:
resp.message.foo = "bar";
resp.message.ok = false;
After calling resp.send
manually or after the whole chain, this message will be send to client:
{
"foo": "bar",
"ok": false
}
Send the stored message to client.
If called once, it won't make any sense while calling again.
Send an error message to client.
If called once, it won't make any sense while calling again.
var helper = require("gensokyo").helper;
Get the model in /model/<modelName>Model.js
.