聊天服務器擴展
大家在上一篇文章里相信已經學會了pomelo框架的基本用法了,那么我們在上一篇文章的代碼基礎上繼續擴展,豐富系統,另外也熟悉下他的更多的用法,這一節我將擴展它:增加一個機器人自動聊天的功能。
目的是讓大家熟悉下定時器的用法,另外再熟悉下RPC方式。
rpc調用
pomelo中使用rpc調用進行進程間通信,在pomelo中rpc調用分為兩大類,使用namespace進行區分,namespace為sys的為系統rpc調用,它對用戶來說是透明的,目前pomelo中系統rpc調用有:
1.后端服務器向前端服務器請求session信息
2.后端服務器通過channel推送消息時對前端服務器發起的rpc調用
3.前端服務器將用戶請求路由給后端服務器時也是sys rpc調用
除了系統rpc調用外,其余的由用戶自定義的rpc調用屬于user namespace的rpc調用,需要用戶自己完成rpc服務端remote的handle代碼,并由rpc客戶端顯式地發起調用.
服務器間RPC調用的抽象介紹
架構中各服務器之間的通訊主要是通過底層RPC框架來完成的,該RPC框架主要解決了進程間消息的路由和RPC底層通訊協議的選擇兩個問題。 服務器間的RPC調用也實現了零配置。實例如下圖所示:
上圖的remote目錄里定義了一個RPC接口: chatRemote.js,它的接口定義如下:
chatRemote.kick = function(uid, player, cb) {}
其它服務器(RPC客戶端)只要通過以下接口就可以實現RPC調用:
app.rpc.chat.chatRemote.kick(session, uid, player, function(data){});
?這個調用會根據特定的路由規則轉發到特定的服務器。(如場景服務的請求會根據玩家在哪個場景直接轉發到對應的server)。
rpc的使用遠比其它rpc框架簡單好多,因為我們無需寫任何配置文件,也無需生成stub。因為我們服務器抽象的實現的方式,使得rpc客戶端可以在應用啟動時掃描服務器目錄自動生成stub對象。
完成了以上三個目標, 一個實時的分布式應用框架的輪廓就搭出來了。接下來我們在下一章節里說明下在當前demoserver的基礎上不斷地擴充,豐富它的功能。
拿起鍵盤就是干
我的思路是玩家在連接到服務器之后,在connector服務器創建一個定時器,每1秒向登錄進來的客戶端發送消息,當然發送的消息內容,你可以隨機,也可以固定,推送給客戶端消息的方式是通過調用一個time服務器的RPC方式pushmsg。
好了,拿起鍵盤開干:
由于增加了一個time服務器,所以第一步先往配置文件里server.json里配服務器:
{"development":{"connector":[{"id":"connector-server-1", "host":"127.0.0.1", "port":4050, "clientPort": 3050, "frontend": true},{"id":"connector-server-2", "host":"127.0.0.1", "port":4051, "clientPort": 3051, "frontend": true},{"id":"connector-server-3", "host":"127.0.0.1", "port":4052, "clientPort": 3052, "frontend": true}],"chat":[{"id":"chat-server-1", "host":"127.0.0.1", "port":6050},{"id":"chat-server-2", "host":"127.0.0.1", "port":6051},{"id":"chat-server-3", "host":"127.0.0.1", "port":6052}],"gate":[{"id": "gate-server-1", "host": "127.0.0.1", "clientPort": 3014, "frontend": true}],"time":[{"id": "time-server-1", "host": "127.0.0.1", "port": 7052}]},"production":{"connector":[{"id":"connector-server-1", "host":"127.0.0.1", "port":4050, "clientPort": 3050, "frontend": true},{"id":"connector-server-2", "host":"127.0.0.1", "port":4051, "clientPort": 3051, "frontend": true},{"id":"connector-server-3", "host":"127.0.0.1", "port":4052, "clientPort": 3052, "frontend": true}],"chat":[{"id":"chat-server-1", "host":"127.0.0.1", "port":6050},{"id":"chat-server-2", "host":"127.0.0.1", "port":6051},{"id":"chat-server-3", "host":"127.0.0.1", "port":6052}],"gate":[{"id": "gate-server-1", "host": "127.0.0.1", "clientPort": 3014, "frontend": true}],"time":[{"id": "time-server-1", "host": "127.0.0.1", "port": 7052}]
}
}
還有adminServer.json
[{"type": "connector","token": "agarxhqb98rpajloaxn34ga8xrunpagkjwlaw3ruxnpaagl29w4rxn"
}, {"type": "chat","token": "agarxhqb98rpajloaxn34ga8xrunpagkjwlaw3ruxnpaagl29w4rxn"
},{"type": "gate","token": "agarxhqb98rpajloaxn34ga8xrunpagkjwlaw3ruxnpaagl29w4rxn"
},{"type": "time","token": "agarxhqb98rpajloaxn34ga8xrunpagkjwlaw3ruxnpaagl29w4rxn"
}]
我在connector服務器增加一個定時器的回調tick,tick函數的功能很簡單,通過RPC方式調用定時器遠端的pushmsg方法,將自動聊天的內容發送給客戶端
Handler.prototype.tick = function(session,uid,rid) {//run all the action//put user into channelconsole.log("定時器觸發了");this.app.rpc.time.timeRemote.pushmsg(session, uid, this.app.get('serverId'), rid, true);
}
?接下來我們在enter的路由下面做處理,向time服務器注冊玩家的uid,rid信息,到時候time服務器在推送消息的時候就知道往哪里發送了。因此添加三行代碼:
/*** New client entry chat server.** @param {Object} msg request message* @param {Object} session current session object* @param {Function} next next stemp callback* @return {Void}*/
Handler.prototype.enter = function(msg, session, next) {var self = this;var rid = msg.rid;var uid = msg.username + '*' + ridvar sessionService = self.app.get('sessionService');//duplicate log inif( !! sessionService.getByUid(uid)) {next(null, {code: 500,error: true});return;}session.bind(uid);session.set('rid', rid);session.push('rid', function(err) {if(err) {console.error('set rid for session service failed! error is : %j', err.stack);}});session.on('closed', onUserLeave.bind(null, self.app));//put user into channelself.app.rpc.chat.chatRemote.add(session, uid, self.app.get('serverId'), rid, true, function(users){next(null, {users:users});});this.app.rpc.time.timeRemote.add(session, uid, this.app.get('serverId'), rid, true);console.log("當前的connectorid:" + self.app.get('serverId')); setInterval(this.tick.bind(this), 1000,session,uid,rid);
};
到了我們的time服務器編寫的時間了:創建time/remote目錄,增加timeRemote.js的處理
?
module.exports = function(app) {return new TimeRemote(app);
};var TimeRemote = function(app) {this.app = app;this.channelService = app.get('channelService');
};
TimeRemote.prototype.add = function(uid, sid, name, flag) {var channel = this.channelService.getChannel(name, flag);var username = uid.split('*')[0]; if( !! channel) {channel.add(uid, sid);}
};
TimeRemote.prototype.pushmsg = function(uid, sid, name, flag) {var channel = this.channelService.getChannel(name, flag);var username = uid.split('*')[0];console.log("定時器收到通知============"+uid+"---------"+sid+"username:"+username);var param = {route: 'onChat',msg: "hello this is robot",from: "robot",target: username};channel.pushMessage(param);
};
?
?
到了我們的測試階段:
瀏覽器打開http://localhost:3001,登錄后看到機器人自動給客戶端發送了消息
更多pomelo框架的開發使用,請關注我