(十三)nodejs循序漸進-高性能游戲服務器框架pomelo之擴展聊天服務器為機器人自動聊天

聊天服務器擴展

大家在上一篇文章里相信已經學會了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調用也實現了零配置。實例如下圖所示:

深入淺出node.js游戲服務器開發——Pomelo框架的設計動機與架構介紹

上圖的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框架的開發使用,請關注我

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/news/444529.shtml
繁體地址,請注明出處:http://hk.pswp.cn/news/444529.shtml
英文地址,請注明出處:http://en.pswp.cn/news/444529.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

C++:09---類靜態成員、類常量成員

一、類靜態成員(static) 先介紹一下什么是靜態變量、靜態函數 靜態局部變量:存在域(全局數據區),作用域(塊作用域)靜態全局變量:存在域(全局數據區),作用域(整個文件)靜態函數:存在域(全局數據區),作用域(整個文件)static int a=10;//全局靜態變量 static vo…

C++:08---成員變量初始化方式

成員變量初始化有三種方式: 在構造函數體內賦值初始化在自定義的公有函數體中賦值初始化(一般用于成員變量的初始化)在構造函數的成員初始化列表初始化一、構造函數體內初始化 說明:在構造函數體內的初始化方式,本質是是為成員變量賦值,而不是真正意義上的初始化,這點要…

leetcode1290. 二進制鏈表轉整數 刷新認知,最簡單算法題

給你一個單鏈表的引用結點 head。鏈表中每個結點的值不是 0 就是 1。已知此鏈表是一個整數數字的二進制表示形式。 請你返回該鏈表所表示數字的 十進制值 。 示例 1: 輸入:head [1,0,1] 輸出:5 解釋:二進制數 (101) 轉化為十進…

Redis:02---安裝Redis(Linux+Windows+Docker)

Linux安裝:一、安裝方式1(下載源碼編譯安裝)第一步:從下面的網址中下載Redis最新穩定版本的源代碼sudo wget http://download.redis.io/redis-stable.tar.gz第二步:下載完之后解壓,建立一個軟鏈接指向于red…

C++:10---再議拷貝構造函數

一、概念 使用一個已經存在的對象,去構造(初始化)另一個對象二、格式 參數加上const&,因為拷貝構造函數在幾種情況下都會被隱式地使用,因此拷貝構造函數不應該是explict的const:防止函數內部修改值&:防止無限循環拷貝類名(類名 const& 參數名) { 函數體 }三、…

人的思維謬誤與心理學效應

啟發法 用一個容易的問題代替難以回答的真正問題。這個容易的問題的答案就是對真正問題的啟發,但啟發經常和真正的答案差得很遠,而人卻往往把啟發當成了真正問題的答案。 接下來介紹和啟發法相關的心理效應和謬誤。每一個謬誤都會注明真正的問題是什么…

C++:07---this指針

一、this指針介紹 概念:this指針是成員函數的一個隱式參數,在類中本質上就是對象的指針(常量指針)特點:在成員函數中可通過this指針區別成員變量與形參變量this可以顯式調用示例代碼:class Cperson { private: int age; float height; public: void InitPerson(int age,flo…

Redis :01---Redis簡介和安裝

一、Redis簡介 Redis官網:https://redis.io/ Redis是一種基于鍵值對(key-value)的NoSQL數據庫 與很多鍵值對數據庫不同的是,Redis中的值可以是由string(字符串)、hash(哈希)、 list&…

215. 數組中的第K個最大元素 BFPRT最牛解法

在未排序的數組中找到第 k 個最大的元素。請注意,你需要找的是數組排序后的第 k 個最大的元素,而不是第 k 個不同的元素。 示例 1: 輸入: [3,2,1,5,6,4] 和 k 2 輸出: 5 示例 2: 輸入: [3,2,3,1,2,4,5,5,6] 和 k 4 輸出: 4 說明: 你可以假設 k 總是…

C++: 06---構造函數析構函數

拷貝構造函數: 用一個已經存在的對象來生成一個相同類型的新對象。(淺拷貝)默認的拷貝構造函數: 如果自定義了拷貝構造函數,編譯器就不在生成默認的拷貝構造函數。 如果沒有自定義拷貝構造函數,但在代碼中用到了拷貝構造函數,編譯器會生成默認…

leetcode371. 兩整數之和 不用+號做加法

不使用運算符 和 - ,計算兩整數 ???????a 、b ???????之和。 示例 1: 輸入: a 1, b 2 輸出: 3 示例 2: 輸入: a -2, b 3 輸出: 1 思路:模擬加法器 二進制不考慮進位:000,010,110,是…

C++:05---class和struct

C++被稱為“C with class”,可見在C++中class是多么重要,與class類似的一個結構就是struct了,struct最早是在C語言中出現的,在C++中對struct的功能也進行了擴展。 class : public(公有):在類內外、派生類中都可被訪問protected(保護):希望與派生類共享但是不想被公共…

leetcode34. 在排序數組中查找元素的第一個和最后一個位置

給定一個按照升序排列的整數數組 nums,和一個目標值 target。找出給定目標值在數組中的開始位置和結束位置。 你的算法時間復雜度必須是 O(log n) 級別。 如果數組中不存在目標值,返回 [-1, -1]。 示例 1: 輸入: nums [5,7,7,8,8,10], target 8 輸…

C++:11---友元函數、友元類

一、友元(friend) 概念:通過友元,打破了類的封裝性,可以訪問類內的所有成員分類:友元函數、友元類二、友元函數 概念:友元函數是一個普通函數,不屬于類,但需要在類內表明友元關系 友元函數可訪問類內所有成員,但類不可以訪問友元函數…

leetcode75. 顏色分類

給定一個包含紅色、白色和藍色,一共 n 個元素的數組,原地對它們進行排序,使得相同顏色的元素相鄰,并按照紅色、白色、藍色順序排列。 此題中,我們使用整數 0、 1 和 2 分別表示紅色、白色和藍色。 注意: 不能使用代碼…

C++:12---運算符重載

一、概念 對已有的運算符重新進行定義,賦予其另一種功能,以適應不同的數據類型重載的運算符是具有特殊名字的函數,該函數也有返回值、參數列表、函數體二、運算符重載的3種實現方式 成員函數:私有、公有、保護都可以友元函數:同上全局函數:只能訪問公有的三、運算符重載的…

Redis:03---Redis的啟動與配置參數大全

一、Redis的可執行文件當我們安裝完Redis之后,src和/usr/local/bin目錄下提供了下面這些可執行程序,我們稱之為Redis Shell:redis-serverRedis服務器redis-cliRedis命令行客戶端redis-benchmarkRedis性能測試工具redis-check-aofRedis AOF持久…

leetcode80. 刪除排序數組中的重復項 II

給定一個排序數組,你需要在原地刪除重復出現的元素,使得每個元素最多出現兩次,返回移除后數組的新長度。 不要使用額外的數組空間,你必須在原地修改輸入數組并在使用 O(1) 額外空間的條件下完成。 示例 1: 給定 nums [1,1,1,2…

Redis:04---鍵的基本命令(上)

一、KEYS:全量遍歷鍵KEYS pattern功能:用來獲取此數據庫中所有的鍵名注意事項:KEYS命令需要遍歷Redis中的所有鍵,當鍵的數量較多時會影響性能,不建議在生產環境下使用支持glob風格通配符格式,見下表&#x…

leetcode67. 二進制求和

給定兩個二進制字符串,返回他們的和(用二進制表示)。 輸入為非空字符串且只包含數字 1 和 0。 示例 1: 輸入: a "11", b "1" 輸出: "100" 示例 2: 輸入: a "1010", b "1011" 輸出…