zoukankan      html  css  js  c++  java
  • Memcached笔记——(四)应对高并发攻击

    近半个月过得很痛苦,主要是产品上线后,引来无数机器用户恶意攻击,不停的刷新产品各个服务入口,制造垃圾数据,消耗资源。他们的最好成绩,1秒钟可以并发6次,赶在Database入库前,Cache进行Missing Loading前,强占这其中十几毫秒的时间,进行恶意攻击。

    相关链接: 
    Memcached笔记——(一)安装&常规错误&监控 
    Memcached笔记——(二)XMemcached&Spring集成 
    Memcached笔记——(三)Memcached使用总结 

    Memcached笔记——(四)应对高并发攻击

    为了应对上述情况,做了如下调整:

    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.   
    148. }  

    PS:Redis的SETNX(即SET if Not eXists,类似于memcache的add)

    相关链接: 
    Memcached笔记——(一)安装&常规错误&监控 
    Memcached笔记——(二)XMemcached&Spring集成 
    Memcached笔记——(三)Memcached使用总结 

    Memcached笔记——(四)应对高并发攻击

  • 相关阅读:
    Redis学习笔记之一 : 配置redis
    Web 项目更改项目名
    Linux 常用命令之一
    SQL 常用语法一
    Java http请求和调用
    Spring c3p0连接池无法释放解决方案
    Windows和Linux查看和更改mysql连接池
    Java 简单实用方法二
    Linux 更改ssh 端口
    CentOS 通过yum安装web环境
  • 原文地址:https://www.cnblogs.com/georgewangii/p/4525369.html
Copyright © 2011-2022 走看看