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;

}

}

相关推荐