背景

网关希望根据 IP 限制一些恶意的 IP。

要求是 1min 内出现 5000 次的,被视为恶意 IP。

那么应该如何实现大量的 IP 信息累加呢?

Redis 累加

一种方式是基于 redis 累加,比如 IP 作为 key,然后定时过期累加。

好处是 redis 相对来说比较节省空间。

不过当时的系统架构并没引入 redis。

而是时序数据库,那么如何优化,降低存储的压力呢?

VM 时序数据库

基础落库

最基本的版本,我们每一个请求,对应到 vm 中的一条数据:

1  xxx.xxx.xxx.100 timestamp
1  xxx.xxx.xxx.100 timestamp
1  xxx.xxx.xxx.100 timestamp
1  xxx.xxx.xxx.101 timestamp

这样对于一般的指标比较适合,但是对于这种高频率的请求,并不好用。

合并

我们可以对数据,在短时间,相同的 label 内进行合并。

减少对应的次数。

3  xxx.xxx.xxx.100 timestamp
1  xxx.xxx.xxx.101 timestamp

去除次数

因为低频次的特别多,我们可以进一步优化,一定时间内超过一定次数的才进行处理。

比如一段时间超过 2 次的才记录到 VM

3  xxx.xxx.xxx.100 timestamp

数据库的设计

指标

我们在设计指标的时候

可以预留 2 个字段:

分组字段

分组总数

分组字段

除了必须得字段之外,按照什么字段分组。

然后定期内存中构建对应的 map

key=必须字段+分组字段

value=出现的总次数

分组次数

结合对应的业务场景,配置一个相对合理的次数。

低于这个值的,就忽略不入库。

注意点

数据量的预估

再做对应的配置处理之前,必须做好数据的估算工作。

比如可以通过统一的日志 ES 平台等,统计历史一个月的数据,然后按照峰值计算,找到一个合适的值。

时间的控制

时间不能太短,一个是考虑到时效性。

一个是考虑到太多的信息放在内存中,对内存的压力会比较大。

所以要选择一个合适的触发时间。

内存大小的控制

内存的大小也不能放的太大,避免内存造成压力。

可以有两个触发条件:

1)满足一定的时间,比如 30S 执行一次批量异步触发。

2)满足一定的大小,比如大小到 10W 进行一次批量异步处理。

方案的不足

阈值的设计

随着消费节点的增加,对应的阈值可能需要调整。

其实比较好的方式是知道当前的消费者一共有多少台,然后放在配置中心。

然后阈值,就是总的次数 / 总的消费者数。

这样可以保证不同的机器数,阈值也是合理的。

延迟+内存压力

这种方式,是以牺牲一定的数据实时性。

内存的压力+可能会丢失部分数据,来达到对应的降低存储成本的。

数据真的不允许数据缺失,可以考虑增加存储的服务机器配置。

总结

办法总比困难多,选择合适自己业务场景的方式。

参考资料