文章目录
RPC组件
RPC是用于服务器进程间通讯的一种实现,能够以类似函数调用的方式来实现进程间通讯
这个组件基于pomelo-rpc实现,也就是额外封装了一层,方便使用
pomelo-rpc解析
RPC服务器组件
具体文件是:pomelo/lib/common/components/remote.js
组件加载后写入app中 app.components.__remote__
总的来说非常简单,创建时支持一些参数,这些参数要个进到pomelo-rpc中才能具体看到是干了什么,一般情况下用空参数即可,这里不做详细介绍。
pro.start = function(cb) {
this.opts.port = this.app.getCurServer().port;
this.remote = genRemote(this.app, this.opts);
this.remote.start();
process.nextTick(cb);
};
在组件start中可以看到,使用的是‘port’字段,也就是servers.json中的port字段。这个字段是用于rpc通信,而‘clientport’是用于和客户端通讯的端口
容易看到在启动生成rpc-server时,先生成了所需要的路径。
var getRemotePaths = function(app) {
var paths = [];
var role;
// master server should not come here
if(app.isFrontend()) {
role = 'frontend';
} else {
role = 'backend';
}
var sysPath = pathUtil.getSysRemotePath(role), serverType = app.getServerType();
if(fs.existsSync(sysPath)) {
paths.push(pathUtil.remotePathRecord('sys', serverType, sysPath));
}
var userPath = pathUtil.getUserRemotePath(app.getBase(), serverType);
if(fs.existsSync(userPath)) {
paths.push(pathUtil.remotePathRecord('user', serverType, userPath));
}
return paths;
};
前端服务器:与客户端直接通讯的服务器要配置成前端
对于前端服务器(serverName)来说,载入的为:
namespace | path |
---|---|
sys | pomelo/lib/common/rempte/frontend |
user | app/servers/serverName/remote |
对于后端服务器(serverName)来说,载入的为:
namespace | path |
---|---|
sys | pomelo/lib/common/rempte/backend |
user | app/servers/serverName/remote |
也就是说,对于服务器进程间通讯的所有接口都在对应servername/remote下
后端的话额外有msgRemote,前端额外有channelRemote,sessionRemote
后端特殊remote:msgRemote
frontend-server向backend-server发送的rpc消息会进入这里。backend-server收到消息后会直接转给自身进程的server进行处理,在这里会把session转为backendsession。
为什么要转成backendsession:
对于rpc通讯来说,参数session是要固定长度的,这里如果不转,改变后会导致rpc消息中session字段长度不一致
前端特殊remote:channelRemote
这里提供有接口,用于向客户端推送消息,也可以广播消息
前端特殊remote:sessionRemote
提供统一的session操作接口。session维护用户的socket连接信息
RPC客户端组件
具体文件是:pomelo/lib/common/components/proxy.js
组件加载后写入app中 app.components.__proxy__
RPC客户端相对服务器来说,稍微复杂一点。
给rpc-client添加可rpc的地址
在组件初始化中:
var Component = function(app, opts) {
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));
};
可以看到处理了3个消息。增加服务器,移除服务器,覆盖服务器。
对于rpc客户端来说,有哪些服务器可以rpc过去就是通过这里来实现的。
在增加服务器addServers中很容易看出。当你需要增加某个服务器时,把该服务器所支持的rpc跳转也就是上边rpc服务器的那些路径
传入rpc-client,rpc-client内部会扫描路径下的所有js以及其中的函数,最终写入rpc-client内部维护的一个map中。
而这个ADD_SERVERS最终是哪里调用的呢。在monitorwather组件中调用的
var subscribeRequest = function(self, agent, id, cb) {
var msg = {action: 'subscribe', id: id};
agent.request(Constants.KEYWORDS.MASTER_WATCHER, msg, function(err, servers) {
if(err) {
logger.error('subscribeRequest request to master with error: %j', err.stack);
utils.invokeCallback(cb, err);
}
var res = [];
for(var id in servers) {
res.push(servers[id]);
}
addServers(self, res);
utils.invokeCallback(cb);
});
};
rpc消息的前后过滤
在组件start中:
pro.start = function(cb) {
if(this.opts.enableRpcLog) {
logger.warn('enableRpcLog is deprecated in 0.8.0, please use app.rpcFilter(pomelo.rpcFilters.rpcLog())');
}
var rpcBefores = this.app.get(Constants.KEYWORDS.RPC_BEFORE_FILTER);
var rpcAfters = this.app.get(Constants.KEYWORDS.RPC_AFTER_FILTER);
var 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);
};
有一个前后过滤的参数,这个参数需要在进程启动时添加
这个过滤的最终使用地点在:
pomelo-rpc/lib/rpc-client/mailstation.js中doFilter函数
rpc调用的简化
在afterStart中:
pro.afterStart = function(cb) {
var self = this;
this.app.__defineGetter__('rpc', function() {
return self.client.proxies.user;
});
this.app.__defineGetter__('sysrpc', function() {
return self.client.proxies.sys;
});
this.app.set('rpcInvoke', this.client.rpcInvoke.bind(this.client), true);
this.client.start(cb);
};
也就是当你rpc调用时会比较方便
app.rpc === app.components__proxy__.client.proxies.user
app.sysrpc === app.components__proxy__.client.proxies.sys
还有
app.rpcInvoke === app.components__proxy__.client.rpcInvoke
: 立即发送rpc请求,传统的是要判断是否连接的。
总结
主要就是对pomelo-rpc的一层封装。了解了pomelo-rpc的内部实现,这里就没有任何难度了。