雲平臺中使用的socket伺服器是我們自己定義一套通訊協議,並通過C#實現的一個socket服務。
該服務目前是和web服務一起執行在IIS容器中,通過啟動一個永不退出的新執行緒來監聽埠。
在開發的初期,由於服務內一些訊息的異常未進行捕獲,例如客戶端發來的訊息格式不對、試圖去關閉一個已經被釋放的連線 等操作,會導致監聽執行緒意外退出。
後來隨著系統的使用這些問題被一一修復,socket服務就穩定了很多,可是持續一個多周以後,socket服務還是會偶爾掛掉,檢視系統日誌沒有發現任何系統異常。到網上查了一些關於IIS的資料,發現IIS有一套智慧的程式回收機制,目的是為了提高伺服器的效能,程式回收時記憶體中的session、cache以及正在執行的執行緒都會被清掉,所以採用IIS作為伺服器,要保證session、cache等資源長期可用的話,要把他們放到資料庫中,或者分散式的放到其他伺服器中儲存。程式回收後,IIS會啟動新的執行緒,原來部署在IIS中站點的埠都會被重新監聽,但是之前使用者自己啟動的那些執行緒IIS就不會給啟動了。
網上有人給出一種解決方案,對IIS7進行配置:
回收——固定時間間隔(分鐘) 改為 0
——虛擬/專用記憶體限制(KB) 改為 0
程式模型——閒置超時(分鐘) 改為 0
這種方法會禁用IIS的程式回收,不過這樣可能會導致長時間執行後伺服器的效能下降。而且,經過多次嘗試這樣配置以後,經過很長時間的執行,IIS還是會對程式進行回收的。
想到IIS在程式回收之後會重啟自己對執行在其上的站點的埠監聽,我們自己也可以執行一個服務,來判斷socket伺服器的執行緒當前執行狀態是否正常,如果不正常的話就重啟服務。這個服務必須是執行在IIS之外的。
具體做法是:
web服務提供一個獲取程式狀態的介面
/SocketServer.ashx?action=getThreadStatus
提供一個重啟socket服務的介面
/SocketServer.ashx?action=startSocketServer
在IIS外部通過其他方法啟動一個服務,每隔10秒訪問一次獲取程式狀態的介面,如果不正常則呼叫重啟socket服務的介面。
現在的做法是啟動了一個Nodejs服務:
//此服務用來監控雲平臺的socket服務程式,若程式崩潰或重啟,則重新啟動socket服務、ws服務、任務超時檢測 var http=require('http'); var moment = require('moment') //var host="http://xxx"; //本地除錯 var host="http://xxxxxx"; //內網服務 //var host="http://xxxx";//公網服務 var statusCheck="xxx"; var startSocket= "xxx" ; var startWs= "xxx" ; var taskTimeout= "xxx"; var inteval; function start() { inteval = setInterval(checkStatus, 20000); } function end() { clearInterval(inteval); } start(); function checkStatus() { try { http.get(host + statusCheck, function (res) { res.on('data', function (data) { var socketStatus = JSON.parse(data.toString()); if (socketStatus.socketServer == '掛了' || socketStatus.socketServer == 'Stopped') { console.log(moment(new Date()).format('YYYY-MM-DD HH:mm:ss') + " socket服務不可用,正在重啟") //重啟服務 restartService(); } }) }).on('error', function (e) { console.log(moment(new Date()).format('YYYY-MM-DD HH:mm:ss') + "錯誤:" + e.message); }); } catch (e) { console.log(e.message); } } function restartService() { //end(); http.get(host + startSocket, function (res) { statusCode(res.statusCode, 'startSocket'); console.log(moment(new Date()).format('YYYY-MM-DD HH:mm:ss') + " 重啟socketserver" + res.statusCode); res.resume(); }); http.get(host + startWs, function (res) { statusCode(res.statusCode, 'startWs'); console.log(moment(new Date()).format('YYYY-MM-DD HH:mm:ss') + " 重啟wsserver" + res.statusCode); res.resume(); }); http.get(host + taskTimeout, function (res) { statusCode(res.statusCode, 'taskTimeout'); console.log(moment(new Date()).format('YYYY-MM-DD HH:mm:ss') + " 重啟任務狀態監控" + res.statusCode); res.resume(); }); var status = { startSocket: false, startWs: false, taskTimeout: false }; function statusCode(code, name) { if (code == 200) { status[name] = true; } if (status.startSocket && status.startWs && status.taskTimeout) { //start(); } } }
這種做法目前有兩個弊端:
1.每次IIS程式回收的時候,socket服務都會有幾秒鐘的時間不可用
2.socket服務執行在web伺服器內,不利於以後web伺服器或者socket伺服器的擴充套件,連線到A伺服器的裝置無法被B伺服器訪問
以後的改進方向是:
把socket伺服器獨立出來,重新設計Web伺服器與socket伺服器的通訊方法。
這樣可以使socket服務不會受到IIS伺服器配置的影響,而且可以隨意擴充套件web伺服器與socket伺服器。