Redis布隆防击穿实战-如何解决布隆值在被刷新时出现的真空期

简介: 大促、抢券、抢红包系统在面临大促时,会面临笔直上升的流量访问趋势。如果流量是“慢慢爬升”,这对系统的考验其实是很一般的。系统最怕的就是笔直上升的流量直线趋势。如下面这种图,直线几乎为90度爬升,每秒超过5,000,8,000甚至几万的并发。

大促、抢券、抢红包


系统在面临大促时,会面临笔直上升的流量访问趋势。如果流量是“慢慢爬升”,这对系统的考验其实是很一般的。系统最怕的就是笔直上升的流量直线趋势。如下面这种图,直线几乎为90度爬升,每秒超过5,000,8,000甚至几万的并发。

image.png

此时市面上90%的系统都顶不住。笔者在近9年的互联网生涯中一直对于流量进行过分析。这种流量里往往真实的业务流量只会占到80%甚至不到,很多大量是一些非正常请求,你们也可以认为是“黑产”流量。


因为在大促、抢券、领红包、秒杀时会引来很多“苍蝇”。

如我在之前的一篇博客 家乐福618安全与性能保卫战(一)-安全高地保卫战 中提到过的,这种恶意流量是可以通过WAF、SPLUNK等日志工具甚至我们使用大数据实时计算手法完全是可以分析得出来的。而使用WAF去拦截,那属于硬杀伤。


此时黑产一旦被封IP,它马上把IP返回IP池子里去。我们的电信池子的IP是动态的、有限的,比如说一个小区旁边有一个基站,提供1万个IP,这1万个IP里用户随机的去获取、更新。此时一个被拦截的IP一旦被返还到IP池子里后,它可能还处于WAF的封禁期。

image.png

而此时一个真实的用户拿到了这个IP,用这个IP一用你的APP或者小程序。真实用户拿到的这个IP由于还处于你企业的WAF的封禁期内,因此这个用户就继续会被拦截。


这种攻击我也在上一篇里提到过被称为“反弹弧式攻击”。然后当你的系统的外部流量、并发数越来越大,IP池就会在瞬间被污染了越来越利害。

image.png

当IP池子被污染了越来越利害,受伤害的用户就会越来越多。

image.png


解决WAF硬杀伤的“祸”


因此我一直在历来博客中强调的是WAF不能求“杀尽”,我们要求的是杀伤面要小、杀伤时间要短(正是IP池子的这个梗)。只要起到一个基本的保护系统的作用即可。


比如说你是APP,然后发觉用户有用浏览器打开,这种行为肯定不行。


再比如说:一秒种一个IP对着领券接口访问了3,000次,这肯定也不能放过,你是机器人还是点击工具人呀,对吧?


所以使用WAF拦截时讲究的是一个“精”字、讲究的是一个“放”字,它更多的时候是一种和黑产的“博弈”。


这个“放过”不代表真的放过,而是要用“逻辑”防护去防系统。就如我在上一篇Redis布隆防击穿实战中所述,这只是诸多手法中的一种,后续我会在讲微服务相关设计时把这些手法一一展现给到各位。

我们在面临抢券、领红包、秒杀时一些页面会存在很多页面ID、组件ID、模块ID、会员ID。那么这些东西如果是标准互联网设计都会走缓存的呀、缓存前面还有Varnish层、缓存里还有双缓层机制。但是黑产你别忘了是非常聪明的“机器人”。


比如说你可以很有理由的告诉业务你是APP,用户用浏览器打开肯定我用WAF封。这种属于非常low的黑产。


真正的黑产是什么样的呢?它家里有几堵墙、每一堵墙上插满了手机,小黑产有个500-600个手机以及不同的号码对他们这群人来说太简单不过了。然后家里拿个1-2个PC端控制着这一群手机并且还用的是真实的手机号、IP这样来搞你的网站,这谁受得了。


那么我们也必须用“聪明的手法”去对待这种黑产,你要来访问?可以,来来来,这个流量我要的,但是你如果绕开、伪造一些ID、TOKEN这个对我系统造成了压力了呀?只要不搞坏我的系统,随便你来访问,这还给我引流对吧?这多香呢?


那如何不让他们对系统造成压力呢?“强迫”他们走缓存,你别绕呀,你正儿八经的来访问一路走WAF、走Varnish、走双缓存,这个没事,你来上百万我都不怕呀。这就是“博弈”学里的“共生”手法。


因此才有了redis bloom防击穿,这就是为了“强迫”、“迫使”请求走你的这几层“流量层缓存”用的。不是我的ID、不是我的会员你也就别再继续访问下去了,我在NG层或者WEB层直接把你拦回去了。


Redis布隆防击穿带来的问题


但是Redis布隆也有一个弱点,那就是它是一次生成的且不可以更改。要更新这个布隆里的值,只有先删除、重刷新。

这是因为布隆就是一个块状的东西,且是一个连续的“位”的block段,它是预生成hash的,同时如果你要把一个己有的在bloom中的元素更改成另一个值或者删除一个特定的值,会由于hash碰撞问题,布隆过滤器不能准确判断数据是否存在,这就不能随意删除。

因此我在上一篇中讲解了自己可以做一个job,定期删除。或者在业务操作、更新业务数据时异步地去删除再重新“充值“bloom里的数据。


这就带来了另一个问题,即:bloom filter在更新时的“真空/黑窗期”的问题。

image.png

由于我们的布隆拦截是:如果存在就放过请求、不存在拦截。

那么这个bloom在删除和重新充值时由于以下的语句:

// 初始化布隆过滤器,放入到spring容器里面
    @Bean
    public BloomFilterHelper<String> initBloomFilterHelper() {
        return new BloomFilterHelper<>(
            (Funnel<String>)(from, into) -> into.putString(from, Charsets.UTF_8).putString(from, Charsets.UTF_8),
            100000000, 0.001);
    }

加上把数据重新“充值”回bloom filter,它会形成10几秒到20秒不等的“真空/黑窗期”。在这段时间,平时我们发觉不了问题。而当请求一高,在这段时间内会有4位数的真实、合法请求也会被屏蔽掉。


解决这个黑窗期的方法其实很简单,设计如下:

  1. job开始删除、重新充值bloom filter里的值时使用redission自续约锁上锁;
  2. 每次判断用户请求进来和bloom filter里的值进行比对前先去拿一下redis锁;
  3. 如果此时已经锁住,放过请求;
  4. 如果拿不到锁,走正常redis bloom filter的拦截逻辑;


那阵空期这10几-20几秒放过了请求怎么办?

唉呀。。。 还记得我在家乐福保卫战里上手画的那张图吗 ?安全与性能间的“战壕”是相通的,你的系统连10几-20多秒的并发都挡不住。。。这是什么系统?因此系统整体要能在短时间里抗,因此不要和我说10几-20几秒这种梗。


系统短时间可以抗、外部可以“聪明”地拦,这就是共生学、这就是博弈。


当然,你可以缩短这个删除->重新充值的时间,关键点就在这一句话:

return new BloomFilterHelper<>(
(Funnel<String>)(from, into) -> into.putString(from, Charsets.UTF_8).putString(from, Charsets.UTF_8),
100000000, 0.001);

你如果Total的值一共不可能超过500万,那这段代码里也就不需要放1亿这个数了,500万的bloom block的创建是秒级的。


这样你是不是本来要10几-20几秒真空/黑窗期,而现在你的真空期一下缩短到了4秒,3秒,是不是对系统的硬抗时间也更“香”了呢?


下面给出完整的逻辑设计架构图来,具体代码各位可以结合我前面博客提到过的redission自续约锁+redis布隆防击穿实战自己去动一下手即可。

image.png

相关文章
|
数据采集 存储 数据可视化
分布式爬虫框架Scrapy-Redis实战指南
本文介绍如何使用Scrapy-Redis构建分布式爬虫系统,采集携程平台上热门城市的酒店价格与评价信息。通过代理IP、Cookie和User-Agent设置规避反爬策略,实现高效数据抓取。结合价格动态趋势分析,助力酒店业优化市场策略、提升服务质量。技术架构涵盖Scrapy-Redis核心调度、代理中间件及数据解析存储,提供完整的技术路线图与代码示例。
1690 0
分布式爬虫框架Scrapy-Redis实战指南
|
8月前
|
存储 NoSQL 前端开发
Redis专题-实战篇一-基于Session和Redis实现登录业务
本项目基于SpringBoot实现黑马点评系统,涵盖Session与Redis两种登录方案。通过验证码登录、用户信息存储、拦截器校验等流程,解决集群环境下Session不共享问题,采用Redis替代Session实现数据共享与自动续期,提升系统可扩展性与安全性。
495 3
Redis专题-实战篇一-基于Session和Redis实现登录业务
|
8月前
|
存储 缓存 NoSQL
Redis专题-实战篇二-商户查询缓存
本文介绍了缓存的基本概念、应用场景及实现方式,涵盖Redis缓存设计、缓存更新策略、缓存穿透问题及其解决方案。重点讲解了缓存空对象与布隆过滤器的使用,并通过代码示例演示了商铺查询的缓存优化实践。
348 1
Redis专题-实战篇二-商户查询缓存
|
11月前
|
缓存 监控 NoSQL
Redis 实操要点:Java 最新技术栈的实战解析
本文介绍了基于Spring Boot 3、Redis 7和Lettuce客户端的Redis高级应用实践。内容包括:1)现代Java项目集成Redis的配置方法;2)使用Redisson实现分布式可重入锁与公平锁;3)缓存模式解决方案,包括布隆过滤器防穿透和随机过期时间防雪崩;4)Redis数据结构的高级应用,如HyperLogLog统计UV和GeoHash处理地理位置。文章提供了详细的代码示例,涵盖Redis在分布式系统中的核心应用场景,特别适合需要处理高并发、分布式锁等问题的开发场景。
602 42
|
NoSQL 安全 测试技术
Redis游戏积分排行榜项目中通义灵码的应用实战
Redis游戏积分排行榜项目中通义灵码的应用实战
492 4
|
缓存 NoSQL Java
基于SpringBoot的Redis开发实战教程
Redis在Spring Boot中的应用非常广泛,其高性能和灵活性使其成为构建高效分布式系统的理想选择。通过深入理解本文的内容,您可以更好地利用Redis的特性,为应用程序提供高效的缓存和消息处理能力。
1381 79
|
缓存 NoSQL Java
SpringBoot整合Redis、以及缓存穿透、缓存雪崩、缓存击穿的理解分布式情况下如何添加分布式锁 【续篇】
这篇文章是关于如何在SpringBoot应用中整合Redis并处理分布式场景下的缓存问题,包括缓存穿透、缓存雪崩和缓存击穿。文章详细讨论了在分布式情况下如何添加分布式锁来解决缓存击穿问题,提供了加锁和解锁的实现过程,并展示了使用JMeter进行压力测试来验证锁机制有效性的方法。
SpringBoot整合Redis、以及缓存穿透、缓存雪崩、缓存击穿的理解分布式情况下如何添加分布式锁 【续篇】
|
11月前
|
机器学习/深度学习 存储 NoSQL
基于 Flink + Redis 的实时特征工程实战:电商场景动态分桶计数实现
本文介绍了基于 Flink 与 Redis 构建的电商场景下实时特征工程解决方案,重点实现动态分桶计数等复杂特征计算。通过流处理引擎 Flink 实时加工用户行为数据,结合 Redis 高性能存储,满足推荐系统毫秒级特征更新需求。技术架构涵盖状态管理、窗口计算、Redis 数据模型设计及特征服务集成,有效提升模型预测效果与系统吞吐能力。
1216 10
|
11月前
|
缓存 NoSQL 算法
高并发秒杀系统实战(Redis+Lua分布式锁防超卖与库存扣减优化)
秒杀系统面临瞬时高并发、资源竞争和数据一致性挑战。传统方案如数据库锁或应用层锁存在性能瓶颈或分布式问题,而基于Redis的分布式锁与Lua脚本原子操作成为高效解决方案。通过Redis的`SETNX`实现分布式锁,结合Lua脚本完成库存扣减,确保操作原子性并大幅提升性能(QPS从120提升至8,200)。此外,分段库存策略、多级限流及服务降级机制进一步优化系统稳定性。最佳实践包括分层防控、黄金扣减法则与容灾设计,强调根据业务特性灵活组合技术手段以应对高并发场景。
3122 7
|
缓存 NoSQL 关系型数据库
redis和缓存及相关问题和解决办法 什么是缓存预热、缓存穿透、缓存雪崩、缓存击穿
本文深入探讨了Redis缓存的相关知识,包括缓存的概念、使用场景、可能出现的问题(缓存预热、缓存穿透、缓存雪崩、缓存击穿)及其解决方案。
1030 0
redis和缓存及相关问题和解决办法 什么是缓存预热、缓存穿透、缓存雪崩、缓存击穿