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



    博客分类:
    Java/Cache
    Spring
    Server Architecture/Distributed

memcachedaddxmemcached

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



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

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



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



    更新数据时,先写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; 
        } 
     
    } 

猜你喜欢

转载自yao-wenjun.iteye.com/blog/2028116