目前项目基本运行ok,对pomelo主要有以下修改或者扩展:
【pomelo】-->对pomelo框架库的修改
【project】-->自己项目上层的处理
1,【pomelo】socket.io配置为240秒一次心跳2,【pomelo】当业务子进程挂掉的时候,自动恢复child process3,【project】session,uid信息同步保存到mysql中,以便在多connector的时候查询到用户所处的connector,投递notify消息。放弃使用pomelo中的channel服务,调用rpcInvoke直接通过connector发送消息。4,【project】禁止非认证route,只在connector才能route到api服务器5,【project】对api服务器添加filter,只用通过认证的session才能调用接口
========old=====
这两天研究了一下pomelo,感觉是一个设计很好的框架. 而且发现基于nodejs socket.io方案可以很好的解决目前android客户端最麻烦的一个问题: 实时在线推送. (因为google的推送服务在国内网络不好用, google自己的推送基本是没指望了). socket.io和connector解决了长连接以及大量用户在线的问题. 当然附加的传输协议压缩也是一个亮点,比原始的http+json节约太多流量了,这点对移动端很重要.
但是这里有个问题还困扰着我,就是原有pomelo里面channel的概念.在push消息的时候,是基于channel的: ChannelService.prototype.pushMessageByUids()等.
但是channel又不是在整个服务器组中全局共享的,而是属于单个backend后台业务服务器. 那么就可能出现本来在同组(类似于qq群)中的用户被分配到了不同backend服务器中.那么push消息就会出问题.
解决的方案就是push是面向connector上的session的.换个角度来设计, 就是backend中完全没有channel. 类似于channel这种群组是存放在mysql中的.每个backend处理的时候,是和mysql这个数据共享者打交道,计算出要发送的对象userIdList, 最后再通过connector上存在的session把它们push出去.
提出这个问题的原因是,如果把pomelo设计为移动终端app的后台server.那么用户就不象游戏那种,单位时间上始终是在某个特定channel中的. 在这种设计中,用户是可以同时存在于多个channel(qq群)中的. 当然mysql不一定是最合适的群组管理者,但我觉得在这种应用中是需要一个全局的用户分组管理服务器的,因为在移动应用中,用户的身份圈子有太多种类了.
发这贴出来,希望能和大家讨论一下这种应用的可行性.
欢迎留言交流:)
记录一下看pomelo代码的历程:
刚刚看了一下发msg的代码,其实最终是通过SessionService发的.
SessionService.prototype.sendMessage = function(sid, msg)
但是为什么官方api文档上面只暴露这两个kick函数呢? 很奇怪. 我开始只看文档还以为发消息必须得通过channel呢.
SessionService
kick
kickBySessionId
那如果这个sendMessage仅仅需要sid和msg就可以发消息,那问题就简单. sid和用户id绑定的吧,直接for里面循环发到目标用户idlist就行了.
问题: 这个SessionService是不是在所有frontend和backend服务器全局唯一的呢?
查到这句话"Session对象由客户端所连接的frontend服务器维护", session属于单个frontend. 但是SessionService应该是全局的吧,不然何为Service呢, 我猜应该是这样的吧.
SessionService.prototype.create = function(sid, frontendId, socket) {
var session = new Session(sid, frontendId, socket, this);
this.sessions[session.id] = session;
return session;
};
恩. 应该是这样的. 生成session的时候就加到全局sessions里面了.
app.get('sessionService')
in frontend servers.这个SessionService只能在frontend调用?
也就是说backend业务处理服务器没法直接调用? 这又怎么弄呢? ............还得继续看代码.......
查看channel的代码,发现最终channel是用这种方式发调用到SessionService发的?
for(var sid in groups) {
app.rpcInvoke(sid, {namespace: namespace, service: service,
method: method, args: [route, msg, groups[sid]]}, rpcCB);
}
也就是说我还只需要找到这个方法就应该能解决我的问题了.
向全局的sessions中我指定的sessionlist发送消息
但是很奇怪的是,我一直以为channel是属于单个backend服务器的, 而查看这个函数的代码的时候
ChannelService.prototype.pushMessageByUids = function(route, msg, uids, cb)发现该函数中仅仅是会把不在channel中的uids加入channel中,然后发送.
那我是不是新写一个ChannelService.prototype.pushMessageByUidsForce,其中去掉检查uids, 直接基于uids发送就解决问题了:)
这样我就不用关心uids是属于哪个backend的哪个channel的了, 直接数据库中查出来要发送的uids, 然后借用pushMessageByUidsForce就ok了.
最终结果就是,我的mobile app服务器设计里面一个channel都没有,mysql里面的共享用户群组表代替了channel.
OK,写了这么多,希望我的这套理论是正确的:) 空了来试验一下.
channelService说直白点就是一个把用户简单分成不同组的容器, 消息的发送还是通过session由connectorServer来负责, 这样广播信息比较方便。
你说的mysql里用户群组也类似一个容器, 计算出uids后通过session来发送,其实是一样的, 没本质区别。
但是session service不是全局的, 每个frontend的session service都是独立的, 也就是说如果同一组用户分布在不同的backend server上, 广播消息需要用到rpc, 但这就牵扯到性能问题, rpc通信量过大也会造成消息堵塞, 聊天服务可以这么做, 实时类的MMO就算了
另外,感觉目前pomelo里面设计的C/S的通信是不是少了一种模式?
c->s: req, res
c->s: notify
s->c: push
s->c: req, res ???
有没有可能服务器向客户端发request呢, 在某些特定情况下,服务器希望客户端对req有响应包回来的? 这样才是真正的全双工:)
route这个是路由功能 是中软的负载均衡,目前只针对connector进行负载均衡。gate不是一种标配
,只不过是在chat的demo里体现了一下。
module.exports.dispatch = function(uid, connectors) {var index = Math.abs(crc.crc32(uid)) % connectors.length;return connectors[index];
};
还有你说的
然后rpc该connector发送消息应该就可以了.
这种应该是服务之间相互调用才会使用rpc的功能,connector是直接暴露给客户端的,好像目前不支持客户端直接rpc到相应的服务吧?
我直接把pomelo里面sioconnector.js里面设置
this.wsocket.set('heartbeat timeout', 3600);
this.wsocket.set('heartbeat interval', 3000);
貌似ok。
这样一个小时才心跳一次。不知道对于整体框架有没有影响。
原生socket的协议,pomelo是支持设置心跳时间的。https://github.com/NetEase/lordofpomelo/blob/master/game-server/app.js 里面app.set('connectorConfig',
{
connector : pomelo.connectors.hybridconnector,
heartbeat : 3,
useDict : true,
useProtobuf : true,
handshake : function(msg, cb){
cb(null, {});
}
});
联系客服