通過監控執行緒狀態來保證socket伺服器的穩定執行

桃子夭夭發表於2015-03-18

雲平臺中使用的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伺服器。

相關文章