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操作返回为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)
产品上线后,引来无数机器用户恶意攻击,不停的刷新产品各个服务入口,制造垃圾数据,消耗资源。他们的最好成绩,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不同,无论何时都保存 |
回归场景,恶意用户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)
相关文章推荐
- redhat6.5搭建nginx+tomcat负载均衡,memcached高速缓存
- redhat6.5搭建nginx+tomcat负载均衡,memcached高速缓存
- windows安装memcached服务
- memcache
- centos下nginx+postgresql+php+memcached+apc
- 第一节 Memcached分布式缓存入门
- memcached 安装
- 分布式缓存的学习(memcached,JBoss Cache)
- Memcached常用命令及使用说明
- Memcached深度分析
- Memcached Java Client API详解
- memcached windows安装使用
- Linux-安装和使用memcached
- centos7系统下安装memcache数据库
- redis使用-1-与memcache对比
- nginx+tomcat+memcached部署过程记录
- Memcached源码分析之资源分享
- Memcache知识点梳理
- Redis 和 Memcached 的区别
- Linux下安装memcache PHP扩展