-
channel頻道是推送消息的工具是玩家ID的集合
可以將玩家ID加入到某個channel,同樣也可以從中移除。
如果通過channel來推送消息,那么channel中的所有成員都將收到這條消息。
可以創(chuàng)建多個channel來自定義消息推送區(qū)域,不過這些channel相互之間是獨立的。
-
channel可以看作是玩家ID的容器,主要用于需要廣播推送消息的場景。
可以把玩家加入到一個channel中,當對這個channel推送消息時,所有加入到這個channel的玩家都會受到推送過來的消息。
一個玩家的ID可能會加入多個channel中,這樣玩家就會接收到其加入的channel推送過來的消息。
-
channel只適用于服務器進程是本地的
應用服務器A和B并不會共享channel,也就是說在服務器A上創(chuàng)建的channel只能由服務器A才能給它推送消息,服務器進程A創(chuàng)建的channel和在服務器進程B創(chuàng)建的channel是兩個不同的channel,相互不影響。
頻道組件channel component
頻道組件維護頻道信息,可以被除了master之外的服務器加載。
頻道組件可看作是channelService頻道服務的組件包裝,注冊該組件后會在app上下文加上channelService,可通過app.get("channelService")獲取。
const channelService = app.get('channelService');
可以認為一個channel就是一個用戶的集合,每個用戶大致對應于前端服務器中的一個session會話,用戶可以通過頻道組件向一個channel里面的所有用戶推送消息。
由于后端服務器并不與客戶端直接相連,故后端服務器會發(fā)起一個sys rpc系統(tǒng)遠程調用來表示向客戶端推送消息,接受這個遠程調用的是pomelo已經實現(xiàn)的ChannelRemote。
對頻道組件進行配置
app.set("channelConfig", opts);
頻道組件的配置項
| 配置 | 描述 |
|---|---|
| broadcastFilter | 廣播的過濾函數(shù) |
broadcastFilter會在執(zhí)行channel頻道的broadcast廣播的時候,在前端服務器上在消息發(fā)送給每個session會話之前進行一個過濾,其簽名形式為broadcastFilter(session, msg, filterParam),其中filterParam參數(shù)由在channelService的broadcast調用時傳入。
channelService.broadcast(type, router, {filterParam:param}, cb);
頻道服務ChannelService
- 頻道服務用于服務器本地創(chuàng)建和維護的頻道
- 頻道服務由默認加載的頻道組件創(chuàng)建
- 頻道服務的組件可由
app.get("channelService")訪問獲取
const channelService = app.get('channelService');
內置服務通過set()函數(shù)掛載到app全局對象上,相當于服務變成了單例類,從而直接從app.get("channelService")獲取之前注冊的服務,根據(jù)玩家uid和綁定的sid確定下來是哪個服務器,哪個channel。
| 方法 | 描述 |
|---|---|
| channelService.createChannel(name) | 指定名稱創(chuàng)建具名頻道 |
| 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
創(chuàng)建頻道 channelService.createChannel(name)
/**
* 創(chuàng)建指定名稱的頻道
*
* @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 是否創(chuàng)建頻道
* @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 路由,前端消息監(jiān)聽方法的路由
* @param {Object} msg 發(fā)送給前端的消息內容
* @param {Object} opts 用戶自定義廣播選項
* opts.binded: push to binded sessions or all the sessions
* opts.filterParam: parameters for broadcast filter.
* @param {Function} cb 回調函數(shù)
* @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 前端消息監(jiān)聽方法的路由
* @param {Object} msg 發(fā)送給前端的消息內容
* @param {Array} uids 消息接收者,格式 [{uid: userId, sid: frontendServerId}]
* @param {Object} opts 用戶自定義參數(shù)
* @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服務用于在具體服務器中保存用戶信息并提供消息發(fā)送功能,但channel只能在具體服務器中進行存儲,無法提供全局服務器中用戶信息存儲的功能。
| 方法 | 描述 |
|---|---|
| channel.add(uid, sid) | 添加用戶到頻道,將指定用戶的UID與用戶所連接的connector服務器的ID綁定。 |
| channel.leave(uid, sid) | 從頻道中移除用戶 |
| channel.getUserAmount() | 獲取頻道中成員數(shù)量 |
| channel.getMembers() | 獲取頻道中所有成員 |
| channel.getMember(uid) | 根據(jù)用戶uid獲取成員信息 |
| channel.destroy() | 銷毀頻道 |
| channel.pushMessage(route, msg, opts, cb) | 推送消息給頻道中所有的成員 |
頻道類型
- pomelo中擁有兩種頻道分別是具名頻道和匿名頻道,區(qū)別在于匿名頻道推送消息需定制uids。
- pomelo服務器和客戶端保持長連接,推送可以根據(jù)頻道推送或根據(jù)用戶連接的服務器推送。
具名頻道
- 具名頻道又稱為顯式頻道
- 創(chuàng)建一個具名頻道時,需要為其指定一個頻道名稱才會返回一個頻道實例。
- 具名頻道并不會自動釋放,需要調用
channel.destroy()方法來手動進行釋放。 - 具名頻道通常用來保持長時間關系的訂閱,比如聊天服務。
具名頻道需要使用channel.createChannel()方法或chanel.getChannel()方法首先獲得一個頻道,然后再使用channel.pushMessage()方法推送消息。
- 創(chuàng)建頻道
channelService.createChannel(name)
createChannel用戶創(chuàng)建一個指定名稱的頻道
//從應用上下文獲取頻道服務對象
const channelService = app.get("channelService");
//頻道服務創(chuàng)建頻道
let channel = channelService.createChannel(name);
| 參數(shù) | 描述 |
|---|---|
| name | 頻道名稱 |
- 獲取頻道
channelService.getChannel(name, create)
//從應用上下文獲取頻道服務對象
const channelService = app.get("channelService");
//從頻道服務中獲取頻道
let channel = channelService.getChannel(name, create);
| 參數(shù) | 描述 |
|---|---|
| name | 頻道名稱 |
| create | 是否創(chuàng)建 |
getChannel方法用于獲取一定指定名稱的頻道,若參數(shù)create設置為true則表示頻道不存在時會自動先創(chuàng)建。
- 頻道添加用戶
channel.add(uid, sid)
//將用戶添加到頻道
//uid表示用戶唯一標識
//sid表示connector服務器的ID
channel.add(uid, sid)
- 頻道推送
channel.pushMessage(event, msg, cb)
//根據(jù)頻道推送
//event表示客戶端監(jiān)聽的push方法,比如onAdd。
//msg表示推送參數(shù)
//cb表示推送后的回調函數(shù)
channel.pushMessage(event, msg, cb);
匿名頻道
匿名頻道是指不用創(chuàng)建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)
| 參數(shù) | 描述 |
|---|---|
| route | 前端消息監(jiān)聽方法的路由 |
| msg | 發(fā)送給前端的消息內容 |
| uids | 消息接受則,格式為{uid:uid, sid:frontendServerId},uid為session綁定的uid。 |
| opts | 可選的自定義參數(shù) |
| cb | 推送完成后的回調函數(shù) |
匿名頻道可通過channelService.pushMessageByUids來推送消息,沒有頻道名稱也沒有頻道實例返回。
例如:使用匿名頻道向指定連接的用戶推送消息
/*根據(jù)客戶端連接推送*/
//定義保存用戶唯一編號和connector服務器的對應管理
let uids = [];
let obj = {};
obj.uid = "session uid";
obj.sid = "connector-server-1";
uids.push(obj);
//獲取應用上下文中的頻道服務
const channelService = app.get("channelService");
//method表示客戶端監(jiān)聽的push方法,比如onAdd。
const method = "onMsg";
//param表示推送參數(shù)
let param = {};
param.msg = "hello world";
//使用頻道服務根據(jù)uids進行推送
channelService.pushMessageByIds(method, param, uids, function(err){
if(err){
console.error(err);
return;
}
});
- 廣播推送
channelService.broadcast
大部分的消息都是通過廣播推送到客戶端,再由客戶端播放接收的消息。channel是服務器向客戶端消息廣播的通道。
channelService.broadcast(stype, route, msg, opts, cb)
| 參數(shù) | 描述 |
|---|---|
| stype | 前端服務器類型 |
| route | 前端消息監(jiān)聽方法的路由 |
| msg | 發(fā)送給前端的消息內容 |
| opts | 可選的自定義參數(shù) |
| cb | 推送后的回調函數(shù) |
例如:向全頻道的所有成員推送消息
//獲取應用上下文中的頻道服務
const channelService = app.get("channelService");
//服務器類型
const stype = "connector";
//method表示客戶端監(jiān)聽的push方法,比如onAdd。
const method = "onMsg";
//推送的消息
let msg = {};
msg.msg = "hello world";
//定義選項參數(shù)
let opts = {};
opts.binded = true;
//廣播
channelService.broadcast(stype, method, msg, opts, function(err){
if(err){
console.error(err);
return;
}
});
具名頻道和匿名頻道本質上都是相同的,即使看起來是不同的。首先頻道需要將連接至它們的前端服務器分組,然后需要將消息聯(lián)通玩家ID通過分組一起推送到各自的前端服務器,最后分發(fā)到各自客戶端。
消息推送過程
pomelo的通訊類型可以分為四種分別是request、response、notify、push??蛻舳税l(fā)起的request請求到達服務器后,服務器處理完畢后會為其返回response響應。notify是客戶端發(fā)給服務器的通知無需服務器給與回復。push是服務器主動給客戶端推送的消息。,其中request、response使用pomelo.request實現(xiàn),notify由pomelo.notify實現(xiàn),push則由頻道實現(xiàn)。
服務器是如何將消息推送給客戶端的呢?
- 頻道所在的服務器進程將消息推送到玩家客戶端所連接的前端服務器進程(依賴底層的RPC框架)
- 通過前端服務器進程將消息推送給客戶端,推送前會根據(jù)用戶所在的前端服務器的serverId進行分組。