Memcached笔记——(四)应对高并发攻击
博客分类:
Java/Cache
Spring
ServerArchitecture/Distributed
memcachedaddxmemcached
近半个月过得很痛苦,主要是产品上线后,引来无数机器用户恶意攻击,不停的刷新产品各个服务入口,制造垃圾数据,消耗资源。他们的最好成绩,1秒钟可以并发6次,赶在Database入库前,Cache进行MissingLoading前,强占这其中十几毫秒的时间,进行恶意攻击。
相关链接:
Memcached笔记——(一)安装&常规错误&监控
Memcached笔记——(二)XMemcached&Spring集成
Memcached笔记——(三)Memcached使用总结
Memcached笔记——(四)应对高并发攻击
为了应对上述情况,做了如下调整:
更新数据时,先写Cache,然后写Database(双写),如果可以,写操作交给队列后续完成。
限制统一帐号,同一动作,同一秒钟并发次数,超过1次不做做动作,返回操作失败。
限制统一用户,每日动作次数,超限返回操作失败。
要完成上述操作,同事给我支招。用Memcached的add方法,就可以很快速的解决问题。不需要很繁琐的开发,也不需要依赖数据库记录,完全内存操作。
以下实现一个判定冲突的方法:
Java代码收藏代码
/**
*冲突延时1秒
*/
publicstaticfinalintMUTEX_EXP=1;
/**
*冲突键
*/
publicstaticfinalStringMUTEX_KEY_PREFIX="MUTEX_";
/**
*冲突判定
*
*@paramkey
*/
publicbooleanisMutex(Stringkey){
returnisMutex(key,MUTEX_EXP);
}
/**
*冲突判定
*
*@paramkey
*@paramexp
*@returntrue冲突
*/
publicbooleanisMutex(Stringkey,intexp){
booleanstatus=true;
try{
if(memcachedClient.add(MUTEX_KEY_PREFIX+key,exp,"true")){
status=false;
}
}catch(Exceptione){
logger.error(e.getMessage(),e);
}
returnstatus;
}
做个说明:
选项说明
add仅当存储空间中不存在键相同的数据时才保存
replace仅当存储空间中存在键相同的数据时才保存
set与add和replace不同,无论何时都保存
也就是说,如果add操作返回为true,则认为当前不冲突!
回归场景,恶意用户1秒钟操作6次,遇到上述这个方法,只有乖乖地1秒后再来。别小看这1秒钟,一个数据库操作不过几毫秒。1秒延迟,足以降低系统负载,增加恶意用户成本。
附我用到的基于XMemcached实现:
Java代码收藏代码
importnet.rubyeye.xmemcached.MemcachedClient;
importorg.apache.log4j.Logger;
importorg.springframework.beans.factory.annotation.Autowired;
importorg.springframework.stereotype.Component;
/**
*
*@authorSnowolf
*@version1.0
*@since1.0
*/
@Component
publicclassMemcachedManager{
/**
*缓存时效1天
*/
publicstaticfinalintCACHE_EXP_DAY=3600*24;
/**
*缓存时效1周
*/
publicstaticfinalintCACHE_EXP_WEEK=3600*24*7;
/**
*缓存时效1月
*/
publicstaticfinalintCACHE_EXP_MONTH=3600*24*30*7;
/**
*缓存时效永久
*/
publicstaticfinalintCACHE_EXP_FOREVER=0;
/**
*冲突延时1秒
*/
publicstaticfinalintMUTEX_EXP=1;
/**
*冲突键
*/
publicstaticfinalStringMUTEX_KEY_PREFIX="MUTEX_";
/**
*Loggerforthisclass
*/
privatestaticfinalLoggerlogger=Logger
.getLogger(MemcachedManager.class);
/**
*MemcachedClient
*/
@Autowired
privateMemcachedClientmemcachedClient;
/**
*缓存
*
*@paramkey
*@paramvalue
*@paramexp
*失效时间
*/
publicvoidcacheObject(Stringkey,Objectvalue,intexp){
try{
memcachedClient.set(key,exp,value);
}catch(Exceptione){
logger.error(e.getMessage(),e);
}
logger.info("CacheObject:["+key+"]");
}
/**
*ShutdowntheMemcachedCilent.
*/
publicvoidfinalize(){
if(memcachedClient!=null){
try{
if(!memcachedClient.isShutdown()){
memcachedClient.shutdown();
logger.debug("ShutdownMemcachedManager...");
}
}catch(Exceptione){
logger.error(e.getMessage(),e);
}
}
}
/**
*清理对象
*
*@paramkey
*/
publicvoidflushObject(Stringkey){
try{
memcachedClient.deleteWithNoReply(key);
}catch(Exceptione){
logger.error(e.getMessage(),e);
}
logger.info("FlushObject:["+key+"]");
}
/**
*冲突判定
*
*@paramkey
*/
publicbooleanisMutex(Stringkey){
returnisMutex(key,MUTEX_EXP);
}
/**
*冲突判定
*
*@paramkey
*@paramexp
*@returntrue冲突
*/
publicbooleanisMutex(Stringkey,intexp){
booleanstatus=true;
try{
if(memcachedClient.add(MUTEX_KEY_PREFIX+key,exp,"true")){
status=false;
}
}catch(Exceptione){
logger.error(e.getMessage(),e);
}
returnstatus;
}
/**
*加载缓存对象
*
*@paramkey
*@return
*/
public<T>TloadObject(Stringkey){
Tobject=null;
try{
object=memcachedClient.<T>get(key);
}catch(Exceptione){
logger.error(e.getMessage(),e);
}
logger.info("LoadObject:["+key+"]");
returnobject;
}
}