您的位置:首页 > 数据库 > Memcache

Memcached应对高并发攻击

2016-07-11 17:09 309 查看
原文链接:http://snowolf.iteye.com/blog/1677495

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

 

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

 
更新数据时,先写Cache,然后写Database(双写),如果可以,写操作交给队列后续完成。
限制统一帐号,同一动作,同一秒钟并发次数,超过1次不做做动作,返回操作失败。
限制统一用户,每日动作次数,超限返回操作失败。

用Memcached的add方法,就可以很快速的解决问题。不需要很繁琐的开发,也不需要依赖数据库记录,完全内存操作。

以下实现一个判定冲突的方法:

 

Java代码  


/** 

 * 冲突延时 1秒 

 */  

public static final int MUTEX_EXP = 1;  

/** 

 * 冲突键 

 */  

public static final String MUTEX_KEY_PREFIX = "MUTEX_";  

  

/** 

 * 冲突判定 

 *  

 * @param key 

 */  

public boolean isMutex(String key) {  

    return isMutex(key, MUTEX_EXP);  

}  

  

/** 

 * 冲突判定 

 *  

 * @param key 

 * @param exp 

 * @return true 冲突 

 */  

public boolean isMutex(String key, int exp) {  

    boolean status = true;  

    try {  

        if (memcachedClient.add(MUTEX_KEY_PREFIX + key, exp, "true")) {  

            status = false;  

        }  

    } catch (Exception e) {  

        logger.error(e.getMessage(), e);  

    }  

    return status;  

}  

 

做个说明:

 
选项说明
add仅当存储空间中不存在键相同的数据时才保存
replace仅当存储空间中存在键相同的数据时才保存
set与add和replace不同,无论何时都保存
也就是说,如果add操作返回为true,则认为当前不冲突!


 

回归场景,恶意用户1秒钟操作6次,遇到上述这个方法,只有乖乖地1秒后再来。别小看这1秒钟,一个数据库操作不过几毫秒。1秒延迟,足以降低系统负载,增加恶意用户成本。

 

附我用到的基于XMemcached实现:

 

Java代码  


import net.rubyeye.xmemcached.MemcachedClient;  

  

import org.apache.log4j.Logger;  

import org.springframework.beans.factory.annotation.Autowired;  

import org.springframework.stereotype.Component;  

  

/** 

 *  

 * @author Snowolf 

 * @version 1.0 

 * @since 1.0 

 */  

@Component  

public class MemcachedManager {  

  

    /** 

     * 缓存时效 1天 

     */  

    public static final int CACHE_EXP_DAY = 3600 * 24;  

  

    /** 

     * 缓存时效 1周 

     */  

    public static final int CACHE_EXP_WEEK = 3600 * 24 * 7;  

  

    /** 

     * 缓存时效 1月 

     */  

    public static final int CACHE_EXP_MONTH = 3600 * 24 * 30 * 7;  

  

    /** 

     * 缓存时效 永久 

     */  

    public static final int CACHE_EXP_FOREVER = 0;  

  

    /** 

     * 冲突延时 1秒 

     */  

    public static final int MUTEX_EXP = 1;  

    /** 

     * 冲突键 

     */  

    public static final String MUTEX_KEY_PREFIX = "MUTEX_";  

    /** 

     * Logger for this class 

     */  

    private static final Logger logger = Logger  

            .getLogger(MemcachedManager.class);  

  

    /** 

     * Memcached Client 

     */  

    @Autowired  

    private MemcachedClient memcachedClient;  

  

    /** 

     * 缓存 

     *  

     * @param key 

     * @param value 

     * @param exp 

     *            失效时间 

     */  

    public void cacheObject(String key, Object value, int exp) {  

        try {  

            memcachedClient.set(key, exp, value);  

        } catch (Exception e) {  

            logger.error(e.getMessage(), e);  

        }  

        logger.info("Cache Object: [" + key + "]");  

    }  

  

    /** 

     * Shut down the Memcached Cilent. 

     */  

    public void finalize() {  

        if (memcachedClient != null) {  

            try {  

                if (!memcachedClient.isShutdown()) {  

                    memcachedClient.shutdown();  

                    logger.debug("Shutdown MemcachedManager...");  

                }  

            } catch (Exception e) {  

                logger.error(e.getMessage(), e);  

            }  

        }  

    }  

  

    /** 

     * 清理对象 

     *  

     * @param key 

     */  

    public void flushObject(String key) {  

        try {  

            memcachedClient.deleteWithNoReply(key);  

        } catch (Exception e) {  

            logger.error(e.getMessage(), e);  

        }  

        logger.info("Flush Object: [" + key + "]");  

    }  

  

    /** 

     * 冲突判定 

     *  

     * @param key 

     */  

    public boolean isMutex(String key) {  

        return isMutex(key, MUTEX_EXP);  

    }  

  

    /** 

     * 冲突判定 

     *  

     * @param key 

     * @param exp 

     * @return true 冲突 

     */  

    public boolean isMutex(String key, int exp) {  

        boolean status = true;  

        try {  

            if (memcachedClient.add(MUTEX_KEY_PREFIX + key, exp, "true")) {  

                status = false;  

            }  

        } catch (Exception e) {  

            logger.error(e.getMessage(), e);  

        }  

        return status;  

    }  

  

    /** 

     * 加载缓存对象 

     *  

     * @param key 

     * @return 

     */  

    public <T> T loadObject(String key) {  

        T object = null;  

        try {  

            object = memcachedClient.<T> get(key);  

        } catch (Exception e) {  

            logger.error(e.getMessage(), e);  

        }  

        logger.info("Load Object: [" + key + "]");  

        return object;  

    }  

  

}  

 

PS:Redis的SETNX(即SET if Not eXists,类似于memcache的add)
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: