-
channel
频道是推送消息的工具是玩家ID的集合
可以将玩家ID加入到某个channel
,同样也可以从中移除。
如果通过channel
来推送消息,那么channel
中的所有成员都将收到这条消息。
可以创建多个channel
来自定义消息推送区域,不过这些channel
相互之间是独立的。
-
channel
可以看作是玩家ID的容器,主要用于需要广播推送消息的场景。
可以把玩家加入到一个channel
中,当对这个channel
推送消息时,所有加入到这个channel
的玩家都会受到推送过来的消息。
一个玩家的ID可能会加入多个channel
中,这样玩家就会接收到其加入的channel
推送过来的消息。
-
channel
只适用于服务器进程是本地的
应用服务器A和B并不会共享channel
,也就是说在服务器A上创建的channel
只能由服务器A才能给它推送消息,服务器进程A创建的channel
和在服务器进程B创建的channel
是两个不同的channel
,相互不影响。
频道组件channel component
频道组件维护频道信息,可以被除了master之外的服务器加载。
频道组件可看作是channelService
频道服务的组件包装,注册该组件后会在app
上下文加上channelService
,可通过app.get("channelService")
获取。
const channelService = app.get('channelService');
可以认为一个channel
就是一个用户的集合,每个用户大致对应于前端服务器中的一个session
会话,用户可以通过频道组件向一个channel
里面的所有用户推送消息。
由于后端服务器并不与客户端直接相连,故后端服务器会发起一个sys rpc
系统远程调用来表示向客户端推送消息,接受这个远程调用的是pomelo已经实现的ChannelRemote
。
对频道组件进行配置
app.set("channelConfig", opts);
频道组件的配置项
配置 | 描述 |
---|---|
broadcastFilter | 广播的过滤函数 |
broadcastFilter
会在执行channel
频道的broadcast
广播的时候,在前端服务器上在消息发送给每个session
会话之前进行一个过滤,其签名形式为broadcastFilter(session, msg, filterParam)
,其中filterParam
参数由在channelService
的broadcast
调用时传入。
channelService.broadcast(type, router, {filterParam:param}, cb);
频道服务ChannelService
- 频道服务用于服务器本地创建和维护的频道
- 频道服务由默认加载的频道组件创建
- 频道服务的组件可由
app.get("channelService")
访问获取
const channelService = app.get('channelService');
内置服务通过set()
函数挂载到app
全局对象上,相当于服务变成了单例类,从而直接从app.get("channelService")
获取之前注册的服务,根据玩家uid
和绑定的sid
确定下来是哪个服务器,哪个channel
。
方法 | 描述 |
---|---|
channelService.createChannel(name) | 指定名称创建具名频道 |
channelService.getChannel(name, create) | 指定名称获取频道 |
channelService.destroyChannel(name) | 指定名称销毁频道 |
channelService.broadcast(stype, route, msg, opts, cb) | 向已连接的客户端广播消息 |
channelService.pushMessageByUids(route, msg, uids, opts, cb) | 指定用户UID推送消息 |
$ vim pomelo/lib/common/service/channelService.js
创建频道 channelService.createChannel(name)
/**
* 创建指定名称的频道
*
* @param {String} name 频道名称
* @memberOf 频道服务
*/
ChannelService.prototype.createChannel = function(name) {
if(this.channels[name]) {
return this.channels[name];
}
var c = new Channel(name, this);
addToStore(this, genKey(this), genKey(this, name));
this.channels[name] = c;
return c;
};
获取频道 channelService.getChannel(name, create)
/**
* 通过频道名称获取频道
*
* @param {String} name 频道名称
* @param {Boolean} create 是否创建频道
* @return {Channel}
* @memberOf 频道服务
*/
ChannelService.prototype.getChannel = function(name, create) {
var channel = this.channels[name];
if(!channel && !!create) {
channel = this.channels[name] = new Channel(name, this);
addToStore(this, genKey(this), genKey(this, name));
}
return channel;
};
销毁频道channelService.destroyChannel(name)
/**
* 通过频道名称销毁频道
*
* @param {String} name 频道名称
* @memberOf 频道服务
*/
ChannelService.prototype.destroyChannel = function(name) {
delete this.channels[name];
removeFromStore(this, genKey(this), genKey(this, name));
removeAllFromStore(this, genKey(this, name));
};
广播消息channelService.broadcast(stype, route, msg, opts, cb)
/**
* 为所有已连接的客户端广播消息
*
* @param {String} stype 前端服务器类型
* @param {String} route 路由,前端消息监听方法的路由
* @param {Object} msg 发送给前端的消息内容
* @param {Object} opts 用户自定义广播选项
* opts.binded: push to binded sessions or all the sessions
* opts.filterParam: parameters for broadcast filter.
* @param {Function} cb 回调函数
* @memberOf 频道服务
*/
ChannelService.prototype.broadcast = function(stype, route, msg, opts, cb) {};
推送消息channelService.pushMessageByUids(route, msg, uids, opts, cb)
/**
* 指定用户ID推送消息.
* Group the uids by group. ignore any uid if sid not specified.
*
* @param {String} route 前端消息监听方法的路由
* @param {Object} msg 发送给前端的消息内容
* @param {Array} uids 消息接收者,格式 [{uid: userId, sid: frontendServerId}]
* @param {Object} opts 用户自定义参数
* @param {Function} cb 回调方法 cb(err)
* @memberOf 频道服务
*/
ChannelService.prototype.pushMessageByUids = function(route, msg, uids, opts, cb) {};
例如:
channelService.pushMessageByUids(
"onPushMessage",
{from:1001, target:1002, message:"hello world"},
[{uid:2000, sid:""}],
function(err){
console.error(err);
}
)
频道channel
pomelo提供channel服务用于在具体服务器中保存用户信息并提供消息发送功能,但channel只能在具体服务器中进行存储,无法提供全局服务器中用户信息存储的功能。
方法 | 描述 |
---|---|
channel.add(uid, sid) | 添加用户到频道,将指定用户的UID与用户所连接的connector服务器的ID绑定。 |
channel.leave(uid, sid) | 从频道中移除用户 |
channel.getUserAmount() | 获取频道中成员数量 |
channel.getMembers() | 获取频道中所有成员 |
channel.getMember(uid) | 根据用户uid获取成员信息 |
channel.destroy() | 销毁频道 |
channel.pushMessage(route, msg, opts, cb) | 推送消息给频道中所有的成员 |
频道类型
- pomelo中拥有两种频道分别是具名频道和匿名频道,区别在于匿名频道推送消息需定制uids。
- pomelo服务器和客户端保持长连接,推送可以根据频道推送或根据用户连接的服务器推送。
具名频道
- 具名频道又称为显式频道
- 创建一个具名频道时,需要为其指定一个频道名称才会返回一个频道实例。
- 具名频道并不会自动释放,需要调用
channel.destroy()
方法来手动进行释放。 - 具名频道通常用来保持长时间关系的订阅,比如聊天服务。
具名频道需要使用channel.createChannel()
方法或chanel.getChannel()
方法首先获得一个频道,然后再使用channel.pushMessage()
方法推送消息。
- 创建频道
channelService.createChannel(name)
createChannel
用户创建一个指定名称的频道
//从应用上下文获取频道服务对象
const channelService = app.get("channelService");
//频道服务创建频道
let channel = channelService.createChannel(name);
参数 | 描述 |
---|---|
name | 频道名称 |
- 获取频道
channelService.getChannel(name, create)
//从应用上下文获取频道服务对象
const channelService = app.get("channelService");
//从频道服务中获取频道
let channel = channelService.getChannel(name, create);
参数 | 描述 |
---|---|
name | 频道名称 |
create | 是否创建 |
getChannel
方法用于获取一定指定名称的频道,若参数create
设置为true
则表示频道不存在时会自动先创建。
- 频道添加用户
channel.add(uid, sid)
//将用户添加到频道
//uid表示用户唯一标识
//sid表示connector服务器的ID
channel.add(uid, sid)
- 频道推送
channel.pushMessage(event, msg, cb)
//根据频道推送
//event表示客户端监听的push方法,比如onAdd。
//msg表示推送参数
//cb表示推送后的回调函数
channel.pushMessage(event, msg, cb);
匿名频道
匿名频道是指不用创建Channel直接使用ChannelService进行推送,匿名频道用于频道成员变动频繁或推送临时消息,比如AOI(Area of Interest)消息,当玩家在一个场景中从A点移动到B点时,服务器就需要推送一个AOI(Area of Interest)消息给周围玩家。
匿名频道支持两种推送方式分别是channelService.pushMessageByUids
指定用户uid进行推送和channelService.broadcast
广播推送。
- 指定用户uid推送
channelService.pushMessageByUids
channelService.pushMessageByUids(route, msg, uids, opts, cb)
参数 | 描述 |
---|---|
route | 前端消息监听方法的路由 |
msg | 发送给前端的消息内容 |
uids | 消息接受则,格式为{uid:uid, sid:frontendServerId} ,uid为session绑定的uid。 |
opts | 可选的自定义参数 |
cb | 推送完成后的回调函数 |
匿名频道可通过channelService.pushMessageByUids
来推送消息,没有频道名称也没有频道实例返回。
例如:使用匿名频道向指定连接的用户推送消息
/*根据客户端连接推送*/
//定义保存用户唯一编号和connector服务器的对应管理
let uids = [];
let obj = {};
obj.uid = "session uid";
obj.sid = "connector-server-1";
uids.push(obj);
//获取应用上下文中的频道服务
const channelService = app.get("channelService");
//method表示客户端监听的push方法,比如onAdd。
const method = "onMsg";
//param表示推送参数
let param = {};
param.msg = "hello world";
//使用频道服务根据uids进行推送
channelService.pushMessageByIds(method, param, uids, function(err){
if(err){
console.error(err);
return;
}
});
- 广播推送
channelService.broadcast
大部分的消息都是通过广播推送到客户端,再由客户端播放接收的消息。channel是服务器向客户端消息广播的通道。
channelService.broadcast(stype, route, msg, opts, cb)
参数 | 描述 |
---|---|
stype | 前端服务器类型 |
route | 前端消息监听方法的路由 |
msg | 发送给前端的消息内容 |
opts | 可选的自定义参数 |
cb | 推送后的回调函数 |
例如:向全频道的所有成员推送消息
//获取应用上下文中的频道服务
const channelService = app.get("channelService");
//服务器类型
const stype = "connector";
//method表示客户端监听的push方法,比如onAdd。
const method = "onMsg";
//推送的消息
let msg = {};
msg.msg = "hello world";
//定义选项参数
let opts = {};
opts.binded = true;
//广播
channelService.broadcast(stype, method, msg, opts, function(err){
if(err){
console.error(err);
return;
}
});
具名频道和匿名频道本质上都是相同的,即使看起来是不同的。首先频道需要将连接至它们的前端服务器分组,然后需要将消息联通玩家ID通过分组一起推送到各自的前端服务器,最后分发到各自客户端。
消息推送过程
pomelo的通讯类型可以分为四种分别是request、response、notify、push。客户端发起的request请求到达服务器后,服务器处理完毕后会为其返回response响应。notify是客户端发给服务器的通知无需服务器给与回复。push是服务器主动给客户端推送的消息。,其中request、response使用pomelo.request实现,notify由pomelo.notify实现,push则由频道实现。
服务器是如何将消息推送给客户端的呢?
- 频道所在的服务器进程将消息推送到玩家客户端所连接的前端服务器进程(依赖底层的RPC框架)
- 通过前端服务器进程将消息推送给客户端,推送前会根据用户所在的前端服务器的serverId进行分组。