四 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.   

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值