1. mailbox数据收发模块

一个RPC客户端可能同一时候须要调用多个远端(server)提供的服务。在pomelo里每一个server抽象为一个mailbox。先来看看mailbox的实现:

var MailBox = function(server, opts) {
EventEmitter.call(this); this.curId = 1;
this.id = server.id;
this.host = server.host;
this.port = server.port;
this.protocal = server.protocal || 'http:';
this.requests = {};
this.timeout = {};
this.queue = []; this.connected = false;
this.closed = false;
this.opts = opts; this.timeoutValue = 1000;
this.buffMsg = opts.buffMsg;
this.interval= 300;
}; util.inherits(MailBox, EventEmitter);

配置信息比較简单,相比服务端客户端多了一个超时的处理:

    var id = this.curId++;
this.requests[id] = cb;
setCbTimeout(this, id, cb); var pkg = {id: id, msg: msg};
if(this.buffMsg) {
enqueue(this, pkg);
}
else {
this.socket.emit('message', pkg);
}

curId能够理解为通信过程中的序列号,每次自增,唯一标示一个数据包。通经常使用来解决数据包的乱序问题。

假设buffMsg被设置则启用缓冲队列,和服务端一致。在发送数据之前会开启一个定时器。假设超时则回调通知上层。


2. mailstation 消息路由

mailstation主要实现了几个功能:

  1. 客户端状态控制
  2. 远程服务端信息管理
  3. 过滤器
  4. 消息路由

1. 消息路由

消息路由模块採用延迟载入的方式,加给mailstation加入远程服务端配置信息的时候没有立即载入一个mailbox与之相应。而是在真正对该服务器请求服务的时候创建相应的实例:

var lazyConnect = function(station, serverId, factory, cb) {
console.log('lazyConnect create mailbox and try to connect to remote server');
var server = station.servers[serverId];
var online = station.onlines[serverId];
if(!server) {
console.log('unkone server: ' + serverId);
return false;
}
if(!online || online !== 1) {
console.log('server is not onlone: ' + serverId);
}
var mailbox = factory.create(server, station.opts);
station.connecting[serverId] = true;
station.mailboxes[serverId] = mailbox;
station.connect(serverId, cb);
return true;
};

首次请求服务的时候先通过lazyConnect建立链接,并把请求加入待处理任务队列:

var addToPending = function(station, serverId, args) {
console.log('add pending request to pending queue');
var pending = station.pendings[serverId];
if(!pending) {
pending = station.pendings[serverId] = [];
}
if(pending.length > station.pendingSize) {
console.log('station pending too much for: ' + serverId);
return;
}
pending.push(args);
};

2. 过滤器

pemelo实现了beforeafter filter能够注入函数在请求发生之前以及之后做一些处理:

var doFilter = function(err, serverId, msg, opts, filters, index, operate, cb) {
if(index < filters.length) {
console.log('doFilter ' + operate + 'filter' + filters[index].name);
}
if(index >= filters.length || !!err) {
utils.invokeCallback(cb, err, serverId, msg, opts);
return;
}
var self = this;
var filter = filters[index];
if(typeof filter === 'function') {
filter(serverId, msg, opts, function(target, message, options) {
index++;
if(utils.getObjectClass(target) === 'Error') {
doFilter(target, serverId, msg, opts, filters, index, operate, cb);
}
else {
doFilter(null, target || serverId, message||msg, options||opts, filters, index, operate, cb);
}
});
return;
}
if(typeof filter[operate] === 'function') {
filter[operate](serverId, msg, opts, function(target, message, options) {
index++;
if(utils.getObjectClass(target) === 'Error') {
doFilter(target, serverId, msg, opts, filters, index, operate, cb);
}
else {
doFilter(null, target || serverId, message||msg, options||opts, filters, index, operate, cb);
}
});
return;
}
index++;
doFilter(err, serverId, msg, opts, filters, index, operate, cb);
};

看起来有点乱:),採用递归的方式依次调用各个过滤器。

来看个mailstation模块的大体流程图:

3. 服务端代理模块

架在mailstation模块上面的是服务端代理模块。

该模块完毕了对服务端的抽象。使得调用远程服务变的十分优雅。

Client.prototype.addProxies = function(records) {
if(!records || !records.length) {
return;
}
for(var i = 0, l = records.length; i < l; i++) {
this.addProxy(records[i]);
}
};

上层通过addProxies接口加入远程服务器配置信息,客户端模块会自己主动为该服务生成代理:

var generateProxy = function(client, record, context) {
if(!record) {
return;
}
var res, name;
var modules = Loader.load(record.path, context);
if(modules) {
res = {};
for(name in modules) {
res[name] = Proxy.create({
service: name,
origin: modules[name],
attach: record,
proxyCB: proxyCB.bind(null, client)
});
}
}
return res;
};

和服务器端配置相似,record注入一个文件路径。我们须要载入该文件提供的模块。

假设record.namespace为:user, 远程服务器类型为test, record.path相应的文件路径为: /remore/test/service.js该文件导出两个模块分别包括一个接口:func1func2。在模块载入完毕之后相应的路由信息大致例如以下:

proxies : {
user: {
test: {
module1: {
func1-Proxy: 'xxx'
},
module2: {
func2-Proxy: 'zzz'
}
}
}
}

终于会为每一个服务端的每一个接口生成一个代理:

var genObjectProxy = function(service, origin, attach, proxyCB) {
var res = {};
for(var field in origin) {
if(typeof origin[field] === 'function') {
res[field] = genFunctionProxy(service, field, origin, attach, proxyCB);
}
} return res;
}; var genFunctionProxy = function(serviceName, methodName, origin, attach, proxyCB) {
return (function() {
var proxy = function() {
var args = Array.prototype.slice.call(arguments);
proxyCB.call(null, serviceName, methodName, args, attach);
};
proxy.toServer = function() {
var args = Array.prototype.slice.call(arguments);
proxyCB.call(null, serviceName, methodName, args, attach, true);
};
return proxy;
})();
};

能够看到我们看到全部接口的代理都是通过封装一个proxyCB函数来完毕的。来看看proxyCB的实现:

var proxyCB = function(client, serviceName, methodName, args, attach, target) {
if(client.state !== STATE_STARTED) {
console.log('fail to invoke rpc proxy client not running');
return;
}
if(args.length < 2) {
return;
}
var cb = args.pop();
var routrParam = args.shift();
var serverType = attach.serverType;
var msg = {namespace: attach.namespace, serverType: serverType,
service: serviceName, method: methodName, args: args};
if(target) {
target(client, msg, serverType, routrParam, cb);
}
else {
getRouteTarget(client, serverType, msg, routrParam, function(err, serverId) {
if(!!err) {
utils.invokeCallback(cb, err);
}
else {
client.rpcInvoke(serverId, msg, cb);
}
});
}
};

serviceName表示模块名称,method相应模块下的接口名称, args是调用接口传入的參数数组。attach表示原始的record路径信息。

这里有个getRouteTarget接口,我们知道当远程有多个提供相似服务的服务器为了均衡负载,须要把请求尽量平均的分配到各个服务器。


这样RPC模块基本了解完了,想要了解很多其它到这里下载代码

pomelo研究笔记-RPCclient的更多相关文章

  1. pomelo研究笔记-RPC服务端

    POMELO 採用多进程的架构能够非常好的实现游戏server(进程)的扩展性,达到支撑较多在线用户.减少server压力等要求. 进程间通信採用RPC的形式来完毕,pomelo的RPC实现的相当静止 ...

  2. 安装Pomelo 时遇到的坑

    一.Pomelo相关的代码地址 https://github.com/NetEase,这里面包含比较多的项目. 2. https://github.com/NetEase/pomelo/wiki/%E ...

  3. 用Pomelo 搭建一个简易的推送平台

    前言 实际上,个人感觉,pomelo 目前提供的两个默认sioconnector和hybridconnector 使用的协议并不适合用于做手机推送平台,在pomelo的一份公开ppt里面,有提到过, ...

  4. Pomelo:网易开源基于 Node.js 的游戏服务端框架

    Pomelo:网易开源基于 Node.js 的游戏服务端框架 https://github.com/NetEase/pomelo/wiki/Home-in-Chinese

  5. Skynet Pomelo Erlang Elixir 的认识

    1.skynet pomelo(node.js) elixir(erlang) 周末研究总结 手游这两年发展来看,感觉对实时性要求越来越高,有同事在研究Elixir开发,google得知这东西是基于e ...

  6. pomelo架构概览

    pomelo之所以简单易用.功能全面,并且具有高可扩展性.可伸缩性等特点,这与它的技术选型和方案设计是密不可分的.在研究大量游戏引擎设计思路基础上,结合以往游戏开发的经验,确定了pomelo框架的设计 ...

  7. 读pomelo的教程-2

    下面从头到尾记录chat demo的Login的过程 client:点击login按钮,取得username和rid两个值 $("#login").click(function() ...

  8. Windows安装pomelo过程

    安装总要出点状况的.操作系统是win7 64bit. 为了保证顺利,打开的是VS2012命令行提示.运行 npm install -g pomelo 经过一系列输出,最后安装提示完成了.但是输入 po ...

  9. 读pomelo的教程-1

    pomelo教程的例子是一个聊天室,包括一个webserver客户端,和一个gameserver的pomelo服务器.这个例子挺好,一个聊天系统逻辑简单,还包括了用户管理,客户端request,服务器 ...

随机推荐

  1. Oracle 创建用户并且授权

    以sysdba登陆 创建用户:CREATE USER username IDENTIFIED BY password; 授予(角色)权限:GRANT CONNECT,RESOURCE TO usern ...

  2. JQuery隔行变色

    <html xmlns="http://www.w3.org/1999/xhtml"> <head runat="server"> &l ...

  3. HTML5 事件

    下面的表格列出了可插入 HTML 5 元素中以定义事件行为的标准事件属性. Window 事件属性 - Window Event Attributes 表单事件 - Form Events 键盘事件 ...

  4. JS拖动DIV布局

    方法一: <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3 ...

  5. matlab实现协同过滤之pdist、squareform

    实现协同过滤算法的第一步是:计算用户或项目之间的相似度.接下来介绍pdist和squareform 用法: D = pdist(X) D = pdist(X,distance)   D = pdist ...

  6. 我的Python成长之路---第六天---Python基础(20)---2016年2月20日(晴)

    一.面向对象基础 面向对象名词解释: 类(Class): 用来描述具有相同的属性和方法的对象的集合.它定义了该集合中每个对象所共有的属性和方法.对象是类的实例. 类变量:类变量在整个实例化的对象中是公 ...

  7. C++惯用法:通过成员模板实现隐式转换(Coercion 强迫 by Member Template)

    Intent To increase the flexibility of a class template's interface by allowing the class template to ...

  8. android面试题之二

    六.android 中的动画有哪几种,它们的特点和区别是什么 ? 逐帧动画和补间动画. 逐帧动画:是传统的动画播放方式,指定每一帧的内容和停留时间,然后顺序地播放排列好的图片,类似电影. 补间动画:给 ...

  9. 微软阵营稳定的好消息:.NET开源、Visual Studio 自由

    今天各个IT社区,头版头条说的是微软.NET开源了.宇宙中最好的IED–Visual Studio Community 2013将免费提供给用户的消息. <宇宙中最强大的开发环境免费了! > ...

  10. B站开源播放框架ijkplayer(iOS版)使用教程

    最近在关注直播这块儿,开始时直接用ffmpeg写了一个,写得比较烂,卡顿很严重,后来听说了B站开源播放框架ijkplayer,于是就去试试看这是链接 ,一用之下果然不错,逢人便向人安利,可总是有部分同 ...