不久前我在檢視警政備份回來的程式碼時,發現在cluster的架構下運作時,CountFilter.java紀錄counter的部分,會引發 Database中所謂Lost update的問題,因為程式中取值與增值後寫入是分別在兩個交易中進行的。只是不知eTax的程式是否也有相同的問題,而 SessionListener的程式也可能有相同的情況。這個問題會造成Counter會比實際值要小。
附帶說明一下, CountFilter.java可用於計數「瀏覽次數」及「訪客人次」,而「訪客人次」的計數也可以在SessionListener中進行。由於 HTTP為無狀態的應用協定,所以「訪客人次」需要利用Web container所提供的Session tracking機制才能達成,而一般Web container預設是以紀錄Session-id的Cookie來提供Session tracking機制,但Web browser不接受Cookie的話,則此方式的Session tracking將會失效,為避免此一情況發生,一般Web container還會至少提供URL-rewriting作為替代的Session tracking機制,但此方式必須要以程式對HTTP回應中所有URL進行加工處理,以便Web Container可在URL中附加Session-id,對於靜態網頁及由JavaScript所發出的請求則會失效,因為請求的URI中就不可能會附加Session-id。還有其他達成Session Tracking機制的方式,如HTTP認證機制、SSL及HTML Form的隱藏欄位等等,但Web Container一般無法提供,則需要以程式碼來達成目的。
綜合以上所述,要精確地計數「訪客人次」是不可能的,就連「瀏覽次數」可能都會有不精確的計算結果,但eTax的計數問題並不在於不夠精確,而在於以程式計數和以分析Access log 來計數的結果相差甚遠,而又無法提出具體而可說服客戶的解釋,依據我的認知來看,以程式來計數,會比以分析Access log來計數來的可信。當然前提是以程式來計數的演算法在各種情境中是正確無誤的。我會有如此想法,有幾點原因:首先Access log並未包含請求所有的資訊,例如請求內容(content)以及部份的Header等,而被請求的網頁有時會因為這些資訊,而將原始請求轉向 (forward)或重導(redirect)至其他網頁。以Java Servlet來說,forward是發生在Web container內部,所以Web server的Access log中不會有紀錄,但是以程式來計數卻是可能會被計數的,而redirect則是利用HTTP協定的功能來達成,兩者計數並不會有落差,但原始請求網頁實際上並未產生被顯示的回應,若不想加以計數的話,以程式計數是可以辦得到,而以分析Access log來計數則應判斷回應狀態碼。還有就是Web Server接受請求,寫入Access log中,才將請求轉給Web container進行後續處理,而在Web container的後續處理的某個程序中進行以程式計數,是有可能會發生Access log有紀錄,而程式計數卻沒有執行到!另外,由於GIP有啟用Web cache機制:在回應中附加last-modified標頭。這個對Access Log的分析不知是否會造成差異。應該還有許多的情況會造成兩種計數發生差異,各位夥伙若有想到其他造成差異的原因,請回信告訴我吧!希望可以找出更多造成差異的原因,以便對客戶提出合理的解釋。
在看了岳儒所提供的資料後,關於分析Access log有幾個疑問,可否請了解的人給予詳細的說明;首先,程式如何區分Session來計數「訪客人次」?若計數是以Cookie的Session- id來判別,那沒有Cookie(Session-id)的紀錄怎麼處理?若將沒有Cookie(Session-id)的紀錄都視為獨立的 Session,那同一Web Browser所發出的第一個請求與後續的請求,會被計數為兩個Session,但以程式計數來紀錄時則視為一個Session。依據Cookie的特性來看,應該將有Cookie(Session-id)的紀錄,視為某個無Cookie(Session-id)紀錄的後續請求,而不應加以Session 計數,否則會與程式計數有落差;而對於不接受Cookie的Web browser,若Web container有採取URL-Rewriting進行Session tracking的替代方式,且程式中也有對URL進行處理的話,那對於Session的計數兩種方式的差異將會十分明顯!我的建議是不要以分析Access log的方式來計數「訪客人次」,因為它可能非常的不精確!
此外,我也思考了GIP中以程式在DB中紀錄Counter所可能引發的問題,首先,是對系統效能的衝擊。若每次Counter加一就寫回DB的話,在大流量的情況時,會造成DB極為沉重的寫入負擔,再加上每個網頁中要顯示計數值都要存取DB時,也會造成DB極為沉重的查詢負擔。而此作法在Cluster的情況下會更形惡化,因為Cluster中多台主機,在同時寫入和讀取時會因為交易鎖定,而需要彼此同步等待,造成負載平衡的效益大打折扣!若是程式沒有寫好還會引發Lost update的問題!雖然將交易隔離層次(isolation level)設為最低等級,可舒緩此一症狀,但DB大量的存取依然會是系統極大的負荷。關於此一問題的解決方案,我已有初步的構想,但尚未進行實際的驗證。我將會在GIP 2.0的實作中運用,至於詳細運作細節,待我整理好再寄給大家吧!
孫德華 / Walter
沒有留言:
張貼留言