2013年8月5日 星期一

Long Polling 之 隨興實作

上次終於得知Long Polling之後
原本只覺得應該只是知道 短時間內沒機會用到...

結果今天點歌系統做個小改版發現其實頗適合用的XDD

原本點歌系統點播清單那塊是隨著換曲時重載
(因為每次換曲點歌數一定會減少啊XD)


不過這樣要是別人點了歌並不會馬上更新
除非自己手動更新或換了歌才會知道
這樣不就不符合即時系統了嗎!!!!!! (請無視此人的發瘋

所以就開始準備動手修改一下
但... 很明顯的用polling絕對不是好方法(理想大概要1s的間隔吧...)
所以就來弄弄Long Polling啦

因為Long Polling會有相當數量的持續連線
當然不能用PHP寫啦~~~ (其實用PHP寫daemon應該可行?)

這回用的是node.js 順便來玩玩這用JS來寫Server XDD
(題外話... 原本想要裝forever來跑這server 不過好像npm registry被衝爆了...)

因為需求上是點歌系統點的一首歌後送出訊息要求客戶端更新
設計上只要一個簡單的訊息交換就夠了
所以這js還頗小的www

server.js
// http
var http = require("http"),
    url   = require("url");

http.globalAgent.maxSockets = 200;

var pendingRequests = [];

// 客戶端連線時讓連線進入等待狀態
function processPendingRequest(request, response) {
    // 連線被強制關閉時移除
    response.on('close', function() {
        clearTimeout(this.timeout);
        var id = pendingRequests.indexOf(this);
        if (id != -1) pendingRequests.splice(id, 1);
    });
    
    // force reconnect
    response.timeout = setTimeout(function (){
        response.writeHead(200, { 'Content-Type': 'application/json', 'Access-Control-Allow-Origin': '*' });
        response.write(JSON.stringify({ "message": "" }));
        response.end();
        var id = pendingRequests.indexOf(response);
        if (id != -1) pendingRequests.splice(id, 1);
    }, 30000);
    
    pendingRequests.push(response);
}

// 送出訊息時針對所有等待中連線送出訊息
function processSendingRequest(request, response) {
    var u = url.parse(request.url, true);
    var msg = u.query.msg, dat = u.query.dat;
    while (pendingRequests.length > 0) {
        var element = pendingRequests.shift();
        clearTimeout(element.timeout);
        element.writeHead(200, { 'Content-Type': 'application/json', 'Access-Control-Allow-Origin': '*' });
        element.write(JSON.stringify({ "message": msg, "data": dat }));
        element.end();
    }
    response.writeHead(200, { 'Content-Type': 'application/json' });
    response.write("{\"result\":\"complete\"}");
    response.end();
}

// 建立HTTP伺服器
http.createServer(function(request, response) {
    var u = url.parse(request.url, true);
    if (/* 此處為訊息傳送端驗證 */) processSendingRequest(request, response);
    else processPendingRequest(request, response);
}).listen(13453);
沒有寫的很複雜
整體就很簡單的訊息交換而已

而點歌頁面也做了對應的調整
function polling_call() {
    $.ajax({
        cache: false,
        dataType: 'json',
        type: "GET",
        crossDomain: true,
        url: "http://live.sbsstudio.twbbs.org:13453/",
        error: function () {
            setTimeout(polling_call, 15000);
        },
        success: function (p) {
            switch (p.message) {
            case "NeedUpdateRequireList":
                requestlist_call();
                break;
            }
            polling_call();
        }
    });
}
Long Polling的客戶端總是很簡單XD
只需要反覆的ajax請求就好
要是伺服器端沒有支援的話,這可是比Polling還糟糕啊XDD

最後後台點歌的PHP檔只需要在點歌成功後外加一個CURL請求就好
就沒多放程式碼啦~~~

這永遠BETA的點歌系統又往正式版前進一步了~~~
(這句話矛盾的很嚴重啊XDD