四 memcache應對高併發

摘自:http://snowolf.iteye.com/blog/1677495, 感謝作者

近半個月過得很痛苦,主要是產品上線後,引來無數機器用戶惡意攻擊,不停的刷新產品各個服務入口,製造垃圾數據,消耗資源。他們的最好成績,1秒鐘可以併發6次,趕在Database入庫前,Cache進行Missing Loading前,強佔這其中十幾毫秒的時間,進行惡意攻擊。

爲了應對上述情況,做了如下調整:

 

  1. 更新數據時,先寫Cache,然後寫Database(雙寫),如果可以,寫操作交給隊列後續完成。
  2. 限制統一帳號,同一動作,同一秒鐘併發次數,超過1次不做做動作,返回操作失敗。
  3. 限制統一用戶,每日動作次數,超限返回操作失敗。

要完成上述操作,同事給我支招。用Memcached的add方法,就可以很快速的解決問題。不需要很繁瑣的開發,也不需要依賴數據庫記錄,完全內存操作。

以下實現一個判定衝突的方法:

 

Java代碼  收藏代碼
  1. /** 
  2.  * 衝突延時 1秒 
  3.  */  
  4. public static final int MUTEX_EXP = 1;  
  5. /** 
  6.  * 衝突鍵 
  7.  */  
  8. public static final String MUTEX_KEY_PREFIX = "MUTEX_";  
  9.   
  10. /** 
  11.  * 衝突判定 
  12.  *  
  13.  * @param key 
  14.  */  
  15. public boolean isMutex(String key) {  
  16.     return isMutex(key, MUTEX_EXP);  
  17. }  
  18.   
  19. /** 
  20.  * 衝突判定 
  21.  *  
  22.  * @param key 
  23.  * @param exp 
  24.  * @return true 衝突 
  25.  */  
  26. public boolean isMutex(String key, int exp) {  
  27.     boolean status = true;  
  28.     try {  
  29.         if (memcachedClient.add(MUTEX_KEY_PREFIX + key, exp, "true")) {  
  30.             status = false;  
  31.         }  
  32.     } catch (Exception e) {  
  33.         logger.error(e.getMessage(), e);  
  34.     }  
  35.     return status;  
  36. }  

 

做個說明:

 

選項 說明
add 僅當存儲空間中不存在鍵相同的數據時才保存
replace 僅當存儲空間中存在鍵相同的數據時才保存
set 與add和replace不同,無論何時都保存

也就是說,如果add操作返回爲true,則認爲當前不衝突!

 

迴歸場景,惡意用戶1秒鐘操作6次,遇到上述這個方法,只有乖乖地1秒後再來。別小看這1秒鐘,一個數據庫操作不過幾毫秒。1秒延遲,足以降低系統負載,增加惡意用戶成本。

 

附我用到的基於XMemcached實現:

 

Java代碼  收藏代碼
  1. import net.rubyeye.xmemcached.MemcachedClient;  
  2.   
  3. import org.apache.log4j.Logger;  
  4. import org.springframework.beans.factory.annotation.Autowired;  
  5. import org.springframework.stereotype.Component;  
  6.   
  7. /** 
  8.  *  
  9.  * @author Snowolf 
  10.  * @version 1.0 
  11.  * @since 1.0 
  12.  */  
  13. @Component  
  14. public class MemcachedManager {  
  15.   
  16.     /** 
  17.      * 緩存時效 1天 
  18.      */  
  19.     public static final int CACHE_EXP_DAY = 3600 * 24;  
  20.   
  21.     /** 
  22.      * 緩存時效 1周 
  23.      */  
  24.     public static final int CACHE_EXP_WEEK = 3600 * 24 * 7;  
  25.   
  26.     /** 
  27.      * 緩存時效 1月 
  28.      */  
  29.     public static final int CACHE_EXP_MONTH = 3600 * 24 * 30 * 7;  
  30.   
  31.     /** 
  32.      * 緩存時效 永久 
  33.      */  
  34.     public static final int CACHE_EXP_FOREVER = 0;  
  35.   
  36.     /** 
  37.      * 衝突延時 1秒 
  38.      */  
  39.     public static final int MUTEX_EXP = 1;  
  40.     /** 
  41.      * 衝突鍵 
  42.      */  
  43.     public static final String MUTEX_KEY_PREFIX = "MUTEX_";  
  44.     /** 
  45.      * Logger for this class 
  46.      */  
  47.     private static final Logger logger = Logger  
  48.             .getLogger(MemcachedManager.class);  
  49.   
  50.     /** 
  51.      * Memcached Client 
  52.      */  
  53.     @Autowired  
  54.     private MemcachedClient memcachedClient;  
  55.   
  56.     /** 
  57.      * 緩存 
  58.      *  
  59.      * @param key 
  60.      * @param value 
  61.      * @param exp 
  62.      *            失效時間 
  63.      */  
  64.     public void cacheObject(String key, Object value, int exp) {  
  65.         try {  
  66.             memcachedClient.set(key, exp, value);  
  67.         } catch (Exception e) {  
  68.             logger.error(e.getMessage(), e);  
  69.         }  
  70.         logger.info("Cache Object: [" + key + "]");  
  71.     }  
  72.   
  73.     /** 
  74.      * Shut down the Memcached Cilent. 
  75.      */  
  76.     public void finalize() {  
  77.         if (memcachedClient != null) {  
  78.             try {  
  79.                 if (!memcachedClient.isShutdown()) {  
  80.                     memcachedClient.shutdown();  
  81.                     logger.debug("Shutdown MemcachedManager...");  
  82.                 }  
  83.             } catch (Exception e) {  
  84.                 logger.error(e.getMessage(), e);  
  85.             }  
  86.         }  
  87.     }  
  88.   
  89.     /** 
  90.      * 清理對象 
  91.      *  
  92.      * @param key 
  93.      */  
  94.     public void flushObject(String key) {  
  95.         try {  
  96.             memcachedClient.deleteWithNoReply(key);  
  97.         } catch (Exception e) {  
  98.             logger.error(e.getMessage(), e);  
  99.         }  
  100.         logger.info("Flush Object: [" + key + "]");  
  101.     }  
  102.   
  103.     /** 
  104.      * 衝突判定 
  105.      *  
  106.      * @param key 
  107.      */  
  108.     public boolean isMutex(String key) {  
  109.         return isMutex(key, MUTEX_EXP);  
  110.     }  
  111.   
  112.     /** 
  113.      * 衝突判定 
  114.      *  
  115.      * @param key 
  116.      * @param exp 
  117.      * @return true 衝突 
  118.      */  
  119.     public boolean isMutex(String key, int exp) {  
  120.         boolean status = true;  
  121.         try {  
  122.             if (memcachedClient.add(MUTEX_KEY_PREFIX + key, exp, "true")) {  
  123.                 status = false;  
  124.             }  
  125.         } catch (Exception e) {  
  126.             logger.error(e.getMessage(), e);  
  127.         }  
  128.         return status;  
  129.     }  
  130.   
  131.     /** 
  132.      * 加載緩存對象 
  133.      *  
  134.      * @param key 
  135.      * @return 
  136.      */  
  137.     public <T> T loadObject(String key) {  
  138.         T object = null;  
  139.         try {  
  140.             object = memcachedClient.<T> get(key);  
  141.         } catch (Exception e) {  
  142.             logger.error(e.getMessage(), e);  
  143.         }  
  144.         logger.info("Load Object: [" + key + "]");  
  145.         return object;  
  146.     }  
  147.   

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章