# 《分布式IM系统》大后端平台-通用模型-第06节:滑动窗口IP校验规则的设计和实现
作者:冰河
星球:http://m6z.cn/6aeFbs (opens new window)
博客:https://binghe.gitcode.host (opens new window)
文章汇总:https://binghe.gitcode.host/md/all/all.html (opens new window)
源码获取地址:https://t.zsxq.com/0dhvFs5oR (opens new window)
课程视频:https://t.zsxq.com/15xid8Zxy (opens new window)
沉淀,成长,突破,帮助他人,成就自我。
- 本节难度:★★☆☆☆
- 本节重点:基于通用拦截器规则链设计和实现滑动窗口IP的校验规则,掌握滑动窗口IP校验规则的设计和实现,掌握在通用拦截器规则链上新增具体校验规则的方法,并能够将其灵活应用到自身实际项目中。
- 课程视频:https://t.zsxq.com/15xid8Zxy (opens new window)
大家好,我是冰河~~
大后端平台作为用户访问分布式IM即时通讯系统的入口,在某种程度上,其访问流量是不容忽视的,为了避免高并发大流量的场景压垮大后端平台,一种简单粗暴的方式就是对访问的流量进行限流。一想到限流,很多小伙伴可能会想到在一段时间内限制访问频次,这种方式是可行的,但稍不注意,就可能会造成流量呈现忽高忽低的现象。所以,此时我们可以使用滑动窗口的原理来实现限流校验规则。
注意:有关各种限流的架构设计和落地方案,在《Seckill秒杀系统》 (opens new window) 专栏中有详细的介绍,包括:单机限流、分布式流控、业务网关限流、流量网关限流等。大家系统性可以学习《Seckill秒杀系统》 (opens new window) 中的限流架构设计和落地方案,这里不再赘述。
# 一、前言
在前面的文章中,我们设计和实现了通用的拦截器规则链,并基于通用的拦截器规则链实现了XSS漏洞校验规则,尽最大程度避免系统出现XSS漏洞。考虑到大后端平台作为整个分布式IM即时通讯系统的访问入口,可能会产生高并发访问的现象,为此,我们可以基于滑动窗口原理结合用户终端的IP地址来实现限流。
# 二、本节诉求
了解滑动窗口的基本原理。基于通用拦截器规则链设计和实现滑动窗口IP的校验规则,掌握滑动窗口IP校验规则的设计和实现,结合自身实际业务场景进行思考,并能够将其灵活应用到自身实际项目中。
# 三、滑动窗口简单介绍
滑动窗口(Sliding Window)是一种常用的算法思想,常用于解决字符串和数组相关的问题。
滑动窗口的基本思想是维护一个窗口,通过移动窗口的起始位置和结束位置来解决问题。在解决问题的过程中,我们可以根据实际情况来调整窗口的大小和位置。
具体来说,滑动窗口的流程如下:
- 初始化窗口的起始位置和结束位置。
- 将窗口内的元素进行处理,得到当前的结果。
- 根据结果调整窗口的大小和位置,使得窗口继续覆盖需要处理的区域。
- 重复 2 和 3,直到窗口覆盖完整个需要处理的区域。
滑动窗口可以应用于很多问题,例如:
- 找到字符串中的最长不含重复字符的子串;
- 在数组中找到满足某个条件的子数组;
- 给定两个字符串 s 和 t,在 s 中找到包含 t 所有字符的最小子串。
我们也可以使用Java来实现一个简单的滑动窗口的示例,如下所示。
public class SlidingWindow {
public static void main(String[] args) {
int[] nums = {1, 3, -1, -3, 5, 3, 6, 7};
int k = 3;
int[] result = maxSlidingWindow(nums, k);
System.out.println(Arrays.toString(result));
}
public static int[] maxSlidingWindow(int[] nums, int k) {
if (nums == null || nums.length == 0) {
return new int[0];
}
int n = nums.length;
int[] result = new int[n - k + 1];
int ri = 0; // 记录结果数组的索引
Deque<Integer> deque = new LinkedList<>(); // 使用双端队列保存窗口内元素的索引
for (int i = 0; i < n; i++) {
// 在窗口前面删除超出窗口范围的元素
while (!deque.isEmpty() && deque.peekFirst() < i - k + 1) {
deque.pollFirst();
}
// 在窗口后面删除小于当前元素的元素,保证队列中存储的是递减的元素
while (!deque.isEmpty() && nums[deque.peekLast()] < nums[i]) {
deque.pollLast();
}
// 将当前元素的索引添加到队列中
deque.offerLast(i);
// 当窗口达到大小 k 时,记录当前窗口的最大值
if (i >= k - 1) {
result[ri++] = nums[deque.peekFirst()];
}
}
return result;
}
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
在该示例中,我们使用双端队列来保存窗口内元素的索引。通过维护一个递减的窗口,可以保证队列的头部元素始终是当前窗口的最大值。在遍历数组时,我们根据窗口的大小进行相应的操作,包括删除超出窗口范围的元素、删除小于当前元素的元素以及添加当前元素的索引。当窗口达到大小 k 时,我们记录当前窗口的最大值,并将其存入结果数组中。
# 四、滑动窗口IP校验规则
# 查看完整文章
加入冰河技术 (opens new window)知识星球,解锁完整技术文章与完整代码