1、背景javascript
開發Web平臺時,常常會須要定時向服務器輪詢獲取數據狀態,而且一般不只只開一個輪詢,而是根據業務須要會產生數個輪詢。這種狀況下,性能低下的Ajax長輪詢已經不能知足需求,頻繁的訪問還會形成線程阻塞。最優的解決方案固然是用Websocket,採用服務器推送的方式來減小頻繁開關鏈接形成的開銷。可是Websocket對於我來講還只是個新事物,在未完成論證的狀況下不能直接開發完就上,所以只好採用過渡方案,使用隊列的方式,暫時優化多AJax長輪詢的狀況下形成的線程阻塞問題。java
我所用的Web平臺框架是國產開源的DWZ框架。該框架不使用經典的iframe模式,全部的視圖、數據訪問都是經過Ajax獲取後在前臺進行加載渲染,頁面遷移跳轉極少,所以本質上來講基於DWZ框架的網頁都是Single Page頁面。在這種狀況下,除了長輪詢外,還會根據用戶的操做產生其它Ajax連接。這就要求在優化的同時,還要保證用戶操做的優先度。畢竟長輪詢只是後臺默認執行的操做,對用戶的體驗影響不大;但用戶的操做由於長輪詢形成延遲的話,用戶體驗就十分糟糕。jquery
此外,我還發現處理這些Ajax輪詢所用的Controller是MVC默認的,然而這些Controller不支持異步處理請求操做,在多個請求訪問時,新請求必須等待舊請求完成後才能繼續下去。ajax
綜上所述,優化Ajax輪詢形成的線程阻塞問題的過渡方案中,有如下兩點要求:json
1.使用Ajax隊列的方式,不推倒現有的技術方案,在原有的基礎上快速修改。服務器
2.在Ajax隊列優化過程當中,必須保證用戶操做的優先度,保證用戶操做的及時響應。框架
3.替換原有隻支持同步Action的Controller,使用可支持異常Action的Controller。異步
2、前臺代碼解析socket
整體思路是:性能
1.重寫jquery既有的ajax方法,將全部調用該方法的ajax所有註冊到自定義的ajax程序池中。
2.自定義ajax程序池分全局和非全局兩類,長輪詢發起的ajax爲非全局,用戶發起的ajax爲全局。
3.排隊執行兩個程序池中的請求,一個請求完成後才繼續執行下一個,而非異步將全部ajax同時發起請求。
4.全局ajax的優先度高,若是當前正在執行非全局ajax且有未發起的全局ajax,則中止正在執行的非全局ajax,優先發送全局ajax。
5.非全局ajax只有在全局ajax所有完畢的狀況下才會發送請求。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
|
// 全部ajax請求都註冊到DNE.LoadingAjax的ajax程序池中,排隊發起請求,ajax結束時刪除.
DNE.LoadingAjax = {
jqAjax: $.ajax,
requests: {},
// ajax對象集合
globalAjaxPool: [],
// 全局ajax程序池
unglobalAjaxPool: [],
// 非全局ajax程序池
interval:
null
,
// ajax循環定時器
runningType:
null
,
// 正在運行的Ajax類型 1:全局 2:非全局
runningId:
null
,
// 正在運行的AjaxId
// 註冊Ajax到程序池中
PushAjaxPool:
function
(request, options) {
var
urlComplete = request.complete;
var
requests =
this
.requests;
var
id = (request.tabId) ? request.tabId : request.url;
// 請求結束時,刪除ajax對象
request.complete =
this
.deleteAjax(urlComplete, id);
// 將請求放到ajax程序池中
var
requestObj = {
id: id,
request: request,
options: options
};
// 若是是獲取json數據的請求,則放入程序池中,若是是獲取Js或圖片等資源的請求,則直接執行
if
(requestObj.request.dataType ==
"json"
) {
if
(request.global) {
// 若是是全局ajax
this
.globalAjaxPool.push(requestObj);
}
else
{
// 若是不是全局ajax
this
.unglobalAjaxPool.push(requestObj);
}
}
else
{
var
loadingAjax = DNE.LoadingAjax;
loadingAjax.runAjax(requestObj);
}
if
(!
this
.interval) {
this
.interval = window.setInterval(
function
() {
var
loadingAjax = DNE.LoadingAjax;
// 若是當前有全局Ajax未運行,則中止正在運行的非全局Ajax
if
(loadingAjax.runningType != 1 && loadingAjax.globalAjaxPool.length > 0) {
if
(loadingAjax.runningType == 2 && loadingAjax.runningId) {
loadingAjax.ajaxAbort(id);
}
// 運行最開頭的全局Ajax
var
reqObj = loadingAjax.globalAjaxPool.shift();
loadingAjax.runAjax(reqObj);
}
else
{
// 若是當前沒有正在執行的Ajax,而且非全局Ajax程序池中有對象
if
(loadingAjax.runningType ==
null
&& loadingAjax.unglobalAjaxPool.length > 0) {
// 運行最開頭的非全局Ajax
var
reqObj = loadingAjax.unglobalAjaxPool.shift();
loadingAjax.runAjax(reqObj);
}
}
}, 100);
}
},
// 刪除Ajax
deleteAjax:
function
(urlComplete, id) {
if
(urlComplete &&
typeof
(urlComplete) ==
"function"
) {
urlComplete();
}
var
loadingAjax = DNE.LoadingAjax;
if
(loadingAjax.requests[id]) {
delete
loadingAjax.requests[id];
}
// 若是程序池中已無請求,則清空ajax循環定時器
if
(loadingAjax.globalAjaxPool.length <= 0 && loadingAjax.unglobalAjaxPool.length <= 0) {
loadingAjax.interval =
null
;
}
// 若是當前請求結束,則重置正在運行的Ajax類型及AjaxId
loadingAjax.runningType =
null
;
loadingAjax.runningId =
null
;
},
// 執行Ajax
runAjax:
function
(reqObj) {
var
jqXHR =
this
.jqAjax(reqObj.request, reqObj.options);
this
.requests[reqObj.id] = jqXHR;
},
// 中止Ajax
ajaxAbort:
function
(id) {
var
jqXHR =
this
.requests[id];
if
(jqXHR) {
jqXHR.abort();
delete
this
.requests[id];
}
}
};
$(
function
() {
$.extend({
ajax:
function
(url, options) {
// 全部ajax都註冊到ajax程序池中
DNE.LoadingAjax.PushAjaxPool(url, options);
}
});
});
|