`

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

 
阅读更多
近半个月过得很痛苦,主要是产品上线后,引来无数机器用户恶意攻击,不停的刷新产品各个服务入口,制造垃圾数据,消耗资源。他们的最好成绩,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; 
    } 
 
分享到:
评论

相关推荐

Global site tag (gtag.js) - Google Analytics