pomelo源码解析之组件解析(一)

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的内部实现,这里就没有任何难度了。

猜你喜欢

转载自blog.csdn.net/qq_37543025/article/details/85727731