<!--StartFragment-->
參考了網上一些資料,實現了session通過filter存儲到memcached服務器上.
(參見: http://ooft.javaeye.com/blog/537841 )
實現方式:
a) 通過MemcachedSessionFilter過濾器攔截cookie,取得的sessionId,通過sessionId構造新的HttpServletRequestWrapper對象,傳給後面的應用.
b) 繼承重構HttpServletRequestWrapper,HttpSessionWrapper類,覆蓋原來和session存取相關的方法呢,都通過SessionService類來實現.
c) SessionService連接memcached服務,以sessionId作爲key,存取的對象是一個map.map的內容即爲session的內容.
d) 讀取數據時,先通過sessionId從memcached服務器上獲取整個map對象,再以session的attributeName作爲key從map中獲取數據對象.寫數據的方式與讀數據類似,先獲取map,然後修改map中的值,然後將整個map保存到memcached服務器上.
改進內容:
(1) 使用xmemcached客戶端代替java memcached client.
XMemcached同樣是基於java nio的客戶端,java nio相比於傳統阻塞io模型來說,有效率高(特別在高併發下)和資源耗費相對較少的優點。
(2) 對修改和刪除session屬性時,使用cas方法實現,防止在併發時出現數據覆蓋的問題.
原來的代碼:
- public void saveSession(String id, Map session) {
- long s1= System.currentTimeMillis();
- MemCachedClient mc = this.getMemCachedClient();
- mc.replace(id, session);
- System.out.print("saveSession");
- System.out.println(System.currentTimeMillis() -s1);
- }
public void saveSession(String id, Map session) {
long s1= System.currentTimeMillis();
MemCachedClient mc = this.getMemCachedClient();
mc.replace(id, session);
System.out.print("saveSession");
System.out.println(System.currentTimeMillis() -s1);
}
其中保存進去的session是一個通過get方法從memcached上獲取的map對象,將map中的值修改以後,將map對象替換服務器上的對象.如果在多臺機器併發的情況下,很可能出現一個線程寫進去的數據被其它的覆蓋.
改進後的代碼:
Memcached是通過cas協議實現原子更新,所謂原子更新就是compare and set,原理類似樂觀鎖,每次請求存儲某個數據同時要附帶一個cas值,memcached比對這個cas值與當前存儲數據的cas值是否相等,如果相等就讓新的數據覆蓋老的數據,如果不相等就認爲更新失敗,這在併發環境下特別有用。
- public void saveSession(String id,String key, Object arg1) {
- long s1= System.currentTimeMillis();
- final String k=key;
- final Object o=arg1;
- try {
- mc.cas(id,0,new CASOperation<Map>(){
- //嘗試更新5次
- public int getMaxTries() {
- return 5;
- }
- public Map getNewValue(long currentCAS, Map map) {
- map.put(k, o);
- return map;
- }});
- System.out.print("saveSession");
- }
- finally{
- System.out.println(System.currentTimeMillis() -s1);
- }
- }