occaecatidicta
Version:
283 lines (247 loc) • 8.49 kB
text/typescript
/**
* Component for proxy.
* Generate proxies for rpc client.
*/
import * as crc from 'crc';
import * as utils from '../util/utils';
import { default as events } from '../util/events';
import { RpcClient, createClient } from 'omelox-rpc';
import * as pathUtil from '../util/pathUtil';
import * as Constants from '../util/constants';
import { getLogger } from 'omelox-logger';
import { Application } from '../application';
import { IComponent } from '../interfaces/IComponent';
import { RpcClientOpts } from 'omelox-rpc';
import { ServerInfo } from '../util/constants';
import { Session } from '../index';
import * as path from 'path';
let logger = getLogger('omelox', path.basename(__filename));
export interface ProxyComponentOptions extends RpcClientOpts {
rpcClient?: {
create: (opts: RpcClientOpts) => RpcClient;
};
bufferMsg?: boolean;
cacheMsg?: boolean;
interval?: number;
enableRpcLog?: boolean;
}
/**
* Proxy component class
*
* @param {Object} app current application context
* @param {Object} opts construct parameters
*/
export class ProxyComponent implements IComponent {
app: Application;
opts: ProxyComponentOptions;
client: RpcClient;
constructor(app: Application, opts: ProxyComponentOptions) {
opts = opts || {};
// proxy default config
// cacheMsg is deprecated, just for compatibility here.
opts.bufferMsg = opts.bufferMsg || opts.cacheMsg || false;
opts.interval = opts.interval || 30;
opts.router = genRouteFun();
opts.context = app;
opts.routeContext = app;
if (app.enabled('rpcDebugLog')) {
opts.rpcDebugLog = true;
opts.rpcLogger = getLogger('rpc-debug', path.basename(__filename));
}
this.app = app;
this.opts = opts;
this.client = genRpcClient(this.app, opts);
this.app.event.on(events.ADD_SERVERS, this.addServers.bind(this));
this.app.event.on(events.REMOVE_SERVERS, this.removeServers.bind(this));
this.app.event.on(events.REPLACE_SERVERS, this.replaceServers.bind(this));
}
name = '__proxy__';
/**
* Proxy component lifecycle function
*
* @param {Function} cb
* @return {Void}
*/
start(cb: (err?: Error) => void) {
if (this.opts.enableRpcLog) {
logger.warn('enableRpcLog is deprecated in 0.8.0, please use app.rpcFilter(omelox.rpcFilters.rpcLog())');
}
let rpcBefores = this.app.get(Constants.KEYWORDS.RPC_BEFORE_FILTER);
let rpcAfters = this.app.get(Constants.KEYWORDS.RPC_AFTER_FILTER);
let rpcErrorHandler = this.app.get(Constants.RESERVED.RPC_ERROR_HANDLER);
if (!!rpcBefores) {
this.client.before(rpcBefores);
}
if (!!rpcAfters) {
this.client.after(rpcAfters);
}
if (!!rpcErrorHandler) {
this.client.setErrorHandler(rpcErrorHandler);
}
process.nextTick(cb);
}
/**
* Component lifecycle callback
*
* @param {Function} cb
* @return {Void}
*/
afterStart(cb: (err?: Error) => void) {
let self = this;
Object.defineProperty(this.app, 'rpc', {
get: function () {
return self.client.proxies.user;
}
});
Object.defineProperty(this.app, 'sysrpc', {
get: function () {
return self.client.proxies.sys;
}
});
this.app.rpcInvoke = this.client.rpcInvoke.bind(this.client);
this.client.start(cb);
}
/**
* Add remote server to the rpc client.
*
* @param {Array} servers server info list, {id, serverType, host, port}
*/
addServers(servers: ServerInfo[]) {
if (!servers || !servers.length) {
return;
}
genProxies(this.client, this.app, servers);
this.client.addServers(servers);
}
manualReloadProxies() {
let servers = [];
for (let k in this.client._station.servers) {
servers.push(this.client._station.servers[k])
}
logger.warn('manualReloadProxies servers:', servers);
genProxies(this.client, this.app, servers);
}
/**
* Remove remote server from the rpc client.
*
* @param {Array} ids server id list
*/
removeServers(ids: string[]) {
this.client.removeServers(ids);
}
/**
* Replace remote servers from the rpc client.
*
* @param {Array} ids server id list
*/
replaceServers(servers: ServerInfo[]) {
if (!servers || !servers.length) {
return;
}
// update proxies
this.client.proxies = {};
genProxies(this.client, this.app, servers);
this.client.replaceServers(servers);
}
/**
* Proxy for rpc client rpcInvoke.
*
* @param {String} serverId remote server id
* @param {Object} msg rpc message: {serverType: serverType, service: serviceName, method: methodName, args: arguments}
* @param {Function} cb callback function
*/
rpcInvoke(serverId: string, msg: any, cb: (err: Error, ...args: any[]) => void) {
this.client.rpcInvoke(serverId, msg, cb);
}
}
export function manualReloadProxies(app: Application) {
if (!app.components.__proxy__) {
return;
}
if (app.components.__proxy__.manualReloadProxies) {
app.components.__proxy__.manualReloadProxies();
} else {
logger.warn('manualReloadProxies not method');
}
}
/**
* Generate rpc client
*
* @param {Object} app current application context
* @param {Object} opts contructor parameters for rpc client
* @return {Object} rpc client
*/
let genRpcClient = function (app: Application, opts: RpcClientOpts & { rpcClient?: { create: (opts: RpcClientOpts) => RpcClient; } }) {
opts.context = app;
opts.routeContext = app;
if (!!opts.rpcClient) {
return opts.rpcClient.create(opts);
} else {
return createClient(opts);
}
};
/**
* Generate proxy for the server infos.
*
* @param {Object} client rpc client instance
* @param {Object} app application context
* @param {Array} sinfos server info list
*/
let genProxies = function (client: RpcClient, app: Application, sinfos: ServerInfo[]) {
let item;
for (let i = 0, l = sinfos.length; i < l; i++) {
item = sinfos[i];
client.addProxies(getProxyRecords(app, item));
}
};
/**
* Check a server whether has generated proxy before
*
* @param {Object} client rpc client instance
* @param {Object} sinfo server info
* @return {Boolean} true or false
*/
let hasProxy = function (client: RpcClient, sinfo: ServerInfo) {
let proxy = client.proxies;
return !!proxy.sys && !!proxy.sys[sinfo.serverType];
};
/**
* Get proxy path for rpc client.
* Iterate all the remote service path and create remote path record.
*
* @param {Object} app current application context
* @param {Object} sinfo server info, format: {id, serverType, host, port}
* @return {Array} remote path record array
*/
let getProxyRecords = function (app: Application, sinfo: ServerInfo) {
return sinfo.remoterPaths;
};
let genRouteFun = function () {
return function (session: Session, msg: any, app: Application, cb: RouteCallback) {
let routes = app.get(Constants.KEYWORDS.ROUTE);
if (!routes) {
defaultRoute(session, msg, app, cb);
return;
}
let type = msg.serverType,
route = routes[type] || routes['default'];
if (route) {
route(session, msg, app, cb);
} else {
defaultRoute(session, msg, app, cb);
}
};
};
export type RouteCallback = (err: Error, routeToServerId ?: string) => void;
let defaultRoute = function (session: Session, msg: any, app: Application, cb: RouteCallback) {
let list = app.getServersByType(msg.serverType);
if (!list || !list.length) {
cb(new Error('can not find server info for type:' + msg.serverType));
return;
}
let uid = session ? (session.uid || '') : '';
let index = Math.abs(crc.crc32(uid.toString())) % list.length;
utils.invokeCallback(cb, null, list[index].id);
};
export type RouteFunction = (routeFrom: any, msg: any, app: Application, cb: RouteCallback) => void;
export type RouteMaps = { [key: string]: RouteFunction };