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

Posted on

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

首页 资讯 精华 论坛 问答 博客 专栏 群组 更多 ▼

招聘 搜索

您还未登录 ! 登录 注册

Snowolf的意境空间!

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

博客分类:

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

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

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

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

  1. 更新数据时,先写Cache,然后写Database,如果可以,写操作交给队列后续完成。
  2. 限制统一帐号,同一动作,同一秒钟并发次数,超过1次不做做动作,返回操作失败。
  3. 限制统一用户,每日动作次数,超限返回操作失败。

要完成上述操作,同事给我支招。用Memcached的add方法,就可以很快速的解决问题。不需要很繁琐的开发,也不需要依赖数据库记录,完全内存操作。

以下实现一个判定冲突的方法:

Java代码 复制代码 收藏代码

  1. ///
  2. /* 冲突延时 1秒
  3. /*/
  4. public static final int MUTEX_EXP = 1;
  5. ///
  6. /* 冲突键
  7. /*/
  8. public static final String MUTEXKEY_PREFIX = "MUTEX";
  9. ///
  10. /* 冲突判定
  11. /*
  12. /* @param key
  13. /*/
  14. public boolean isMutex(String key) {
  15. return isMutex(key, MUTEX_EXP);
  16. }
  17. ///
  18. /* 冲突判定
  19. /*
  20. /* @param key
  21. /* @param exp
  22. /* @return true 冲突
  23. /*/
  24. public boolean isMutex(String key, int exp) {
  25. boolean status = true;
  26. try {
  27. if (memcachedClient.add(MUTEX_KEY_PREFIX + key, exp, "true")) {
  28. status = false;
  29. }
  30. } catch (Exception e) {
  31. logger.error(e.getMessage(), e);
  32. }
  33. return status;
  34. }
    /// / 冲突延时 1秒 // public static final int MUTEXEXP = 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代码 复制代码 收藏代码

  1. import net.rubyeye.xmemcached.MemcachedClient;
  2. import org.apache.log4j.Logger;
  3. import org.springframework.beans.factory.annotation.Autowired;
  4. import org.springframework.stereotype.Component;
  5. ///
  6. /*
  7. /* @author Snowolf
  8. /* @version 1.0
  9. /* @since 1.0
  10. /*/
  11. @Component
  12. public class MemcachedManager {
  13. ///
  14. /* 缓存时效 1天
  15. /*/
  16. public static final int CACHE_EXP_DAY = 3600 /* 24;
  17. ///
  18. /* 缓存时效 1周
  19. /*/
  20. public static final int CACHE_EXP_WEEK = 3600 / 24 / 7;
  21. ///
  22. /* 缓存时效 1月
  23. /*/
  24. public static final int CACHE_EXP_MONTH = 3600 / 24 / 30;
  25. ///
  26. /* 缓存时效 永久
  27. /*/
  28. public static final int CACHE_EXP_FOREVER = 0;
  29. ///
  30. /* 冲突延时 1秒
  31. /*/
  32. public static final int MUTEX_EXP = 1;
  33. ///
  34. /* 冲突键
  35. /*/
  36. public static final String MUTEXKEY_PREFIX = "MUTEX";
  37. ///
  38. /* Logger for this class
  39. /*/
  40. private static final Logger logger = Logger
  41. .getLogger(MemcachedManager.class);
  42. ///
  43. /* Memcached Client
  44. /*/
  45. @Autowired
  46. private MemcachedClient memcachedClient;
  47. ///
  48. /* 缓存
  49. /*
  50. /* @param key
  51. /* @param value
  52. /* @param exp
  53. /* 失效时间
  54. /*/
  55. public void cacheObject(String key, Object value, int exp) {
  56. try {
  57. memcachedClient.set(key, exp, value);
  58. } catch (Exception e) {
  59. logger.error(e.getMessage(), e);
  60. }
  61. logger.info("Cache Object: [" + key + "]");
  62. }
  63. ///
  64. /* Shut down the Memcached Cilent.
  65. /*/
  66. public void finalize() {
  67. if (memcachedClient != null) {
  68. try {
  69. if (!memcachedClient.isShutdown()) {
  70. memcachedClient.shutdown();
  71. logger.debug("Shutdown MemcachedManager...");
  72. }
  73. } catch (Exception e) {
  74. logger.error(e.getMessage(), e);
  75. }
  76. }
  77. }
  78. ///
  79. /* 清理对象
  80. /*
  81. /* @param key
  82. /*/
  83. public void flushObject(String key) {
  84. try {
  85. memcachedClient.deleteWithNoReply(key);
  86. } catch (Exception e) {
  87. logger.error(e.getMessage(), e);
  88. }
  89. logger.info("Flush Object: [" + key + "]");
  90. }
  91. ///
  92. /* 冲突判定
  93. /*
  94. /* @param key
  95. /*/
  96. public boolean isMutex(String key) {
  97. return isMutex(key, MUTEX_EXP);
  98. }
  99. ///
  100. /* 冲突判定
  101. /*
  102. /* @param key
  103. /* @param exp
  104. /* @return true 冲突
  105. /*/
  106. public boolean isMutex(String key, int exp) {
  107. boolean status = true;
  108. try {
  109. if (memcachedClient.add(MUTEX_KEY_PREFIX + key, exp, "true")) {
  110. status = false;
  111. }
  112. } catch (Exception e) {
  113. logger.error(e.getMessage(), e);
  114. }
  115. return status;
  116. }
  117. ///
  118. /* 加载缓存对象
  119. /*
  120. /* @param key
  121. /* @return
  122. /*/
  123. public T loadObject(String key) {
  124. T object = null;
  125. try {
  126. object = memcachedClient. get(key);
  127. } catch (Exception e) {
  128. logger.error(e.getMessage(), e);
  129. }
  130. logger.info("Load Object: [" + key + "]");
  131. return object;
  132. }
  133. }
    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 CACHEEXP_WEEK = 3600 / 24 / 7; /// / 缓存时效 1月 // public static final int CACHE_EXP_MONTH = 3600 / 24 / 30; /// / 缓存时效 永久 // 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 loadObject(String key) { T object = null; try { object = memcachedClient. get(key); } catch (Exception e) { logger.error(e.getMessage(), e); } logger.info("Load Object: [" + key + "]"); return object; } }

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

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

2

分享到:

MySQL 运维笔记(一)—— 终止高负载SQL | 征服 Redis + Jedis + Spring (二)—— ...

评论

4 楼 snowolf 2012-12-11

zym820910 写道

snowolf 写道

CurrentJ 写道

先写Cache,然后写Database,断电或者故障会导致用户数据丢失。 各有利弊,需要根据业务需求权衡。 写得非常好!应对高并发的时候,我们通常的思维是泄洪模式,通过一道又一道的防洪大堤将洪水分流,尤其是在应对数据要求不严厉的SNS这类产品,异步的保存数据值得提倡! 不过,更好的方式是:通过旁路式架构,解决代码层面的大部分压力。现在很多商城的商品展示和搜索都采用NOSQL技术来应对处理,异步增加或更新,并不显得那么重要了,更多的是通过产品和技术架构来调整,比如通过分析用户喜好,事先静态化搜索结果。 赞同,感谢分享! 最核心的优化,还是应当在产品层面多下工夫。找到用户-产品-技术,三方都能满足的平衡点。 3 楼 zym820910 2012-12-11

snowolf 写道

CurrentJ 写道

先写Cache,然后写Database,断电或者故障会导致用户数据丢失。 各有利弊,需要根据业务需求权衡。 写得非常好!应对高并发的时候,我们通常的思维是泄洪模式,通过一道又一道的防洪大堤将洪水分流,尤其是在应对数据要求不严厉的SNS这类产品,异步的保存数据值得提倡! 不过,更好的方式是:通过旁路式架构,解决代码层面的大部分压力。现在很多商城的商品展示和搜索都采用NOSQL技术来应对处理,异步增加或更新,并不显得那么重要了,更多的是通过产品和技术架构来调整,比如通过分析用户喜好,事先静态化搜索结果。

2 楼 snowolf 2012-11-07

CurrentJ 写道

先写Cache,然后写Database,断电或者故障会导致用户数据丢失。 各有利弊,需要根据业务需求权衡。 1 楼 CurrentJ 2012-11-07

先写Cache,然后写Database,断电或者故障会导致用户数据丢失。

发表评论

您还没有登录,请您登录后再发表评论

snowolf的博客

snowolf

  • 浏览: 902532 次
  • 性别: Icon_minigender_1
  • 来自: 北京
  • 最近访客 更多访客>>

torry_he的博客

torry_he

cxzucc的博客

cxzucc greatestrabit的博客

greatestrabit

zhumingyuan的博客

zhumingyuan

文章分类

存档分类

最新评论

希望本站内容对您有点用处,有什么疑问或建议请在后面留言评论
转载请注明作者(RobinChia)和出处 It so life ,请勿用于任何商业用途