- 策略模式
- 定义:定义一组算法,将算法封装成类,并实现同一个接口,从而使他们可以相互替换。策略模式可以让算法和算法使用的代码独立开来,降低了代码耦合度。
- 组成:
- 策略接口
- 具体的策略实现类
- 策略上下文:持有一个真正的策略实现对象,并负责与具体的实现类进行交互
- 主代码
- 优点:
- 分离算法,减少了代码耦合度,很好的遵循了开闭原则
- 开闭原则:对扩展开放,对修改封闭;在需求改动的情况下,不改动代码,通过扩展的方式去实现
- 结构清晰明了,使用简单直观
- 耦合度低,扩展方便
- 分离算法,减少了代码耦合度,很好的遵循了开闭原则
- 缺点
- 随着策略越来越多,类会很多
- 使用者必须知道所有的类,并且自行决定使用哪些策略
- 项目使用
- 将要使用的策略类名按既定顺序设置在yml配置文件中
- 项目启动后,读取yml文件中的配置,获取策略集(当前还是字符串的形式)
- @value对应yml
- 使用监听事件(类实现ApplicationListener
)
- 通过反射将字符串形式的策略集转换为具体的对象形式的策略集
- 自己手动实现一下()
- 程序中获取这个策略集,通过遍历的方式,拿到一个个具体的策略实现对象,去执行其方法,以达到使用策略对流量进行分析拦截的功能
- 策略之间,任务信息如何传递
- 使用threadLocal实现数据的透传
- threadlocal的原理
- 用户校验,语法分析,短时重复请求判断等实现非法流量拦截
- 校验用户是否存在
- 检查表
- 表名为空
- 表名带*,匹配所有表
- 表名的存在性校验
- 语句 是否设置limit,以限制返回结果的条数不会过大
- 主要用到sql工具包:sqlParseUtil.getTableName,getLimit
- 短时重复请求
- 短时重复请求机制:有一个内存map用于存放所有的检索信息的MD5编码+时间戳,如果两个相同检索的相隔时间小于1秒,则直接从缓存中拿结果,而不是调库查询。
- 可以加一个阈值判断机制。如果连着10次请求结果都一致,则将该请求放入短时重复请求map中去。如果是该map中的请求,一律查缓存
- 加入Redis实现分布式的控制
- 限流,任务续接
- RateLimiter限流
- tryAcquire()判断是否超限
- 令牌桶原理
- 令牌桶算法是对漏桶算法的改进,主要在于令牌桶算法能够在限制调用平均速率的同时,还允许一定程度的突发调用
- 具体原理:
- 系统以恒定速率产生令牌,然后将令牌放入令牌桶中
- 令牌桶有一个容量,当令牌桶满了的时候,再向其中放入的令牌就会被丢弃
- 每次一个请求过来需要从令牌桶中获取一个令牌;假设有令牌,那么提供服务;否则拒绝服务
- ratelimiter源码解析
- 重要思想:
- 令牌桶有最大令牌限制
- 令牌的生成是匀速的
- 令牌是可以累计的,但累计的数量不会超过令牌桶的容量
- ratelimiter可以提前消费令牌,如acquire(permits),这个permits可以超过当前桶内剩余的令牌数量
- 令牌桶通过记录下一次请求可以发放令牌的时间点来确定是否接收下一个请求,以及下一个请求需要等待的时间
- 重要变量
- 静态内部类 SleepingStopWatch 计时器
- 提供获取当前时间
- 按照计算的睡眠时间等待
- 子类 SmoothRaterLimiter
- SmoothRaterlimiter的两个静态内部类
- SmoothWarmUp——平滑预热限流
- 以稳定的速率生成令牌
- SmoothBursty——平滑突发限流
- 随着请求量的增加,令牌生成速度会缓慢提升,直到一个稳定的速率
- 如平均流量设置为 QPS设置为5.一下子来10个请求
- 突发限流器会让前5个请求马上通过,没有等待时间;之后每个请求则每隔200ms通过一个
- 预热限流器会让第一个请求直接通过,后面的请求都会有等待时间,且等待时间不断缩短,直到稳定在200毫秒一个。以上就是一个预热的过程
- storedPermits——当前令牌数
- maxPermits——最大令牌数
- stableIntervalMicros——稳定期间令牌生成的间隔时间
- nextFreeTickectMicros——下一个请求可以获取令牌的时间点
- 静态内部类 SleepingStopWatch 计时器
- 令牌的生成,消耗,领取和等待都是对这几个变量的操作 ,这些变量控制这令牌桶的状态
- SmoothBursty为例
- rateLimiter的创建
- create时设置maxBurstSecond(可以保存几秒的令牌),又因为maxPermit = maxBurstSecond*permitsperSecond,所以令牌桶的容量即为设置的QPS的大小
- 调用setRate()计算stableIntervalMicro(令牌生成间隔时间),storedPermits等,resync()方法确定nextFreeTickectMicros
- acquire 获取令牌
- 计算本次请求需要休眠多久
- 加锁,防止令牌桶参数被其他线程篡改
- 核心还是reverseEarliestAvailable()方法,返回最早可以获取到令牌的时间
- 获取 下一个请求获取到令牌的时间
- 计算本次实际能获取到的令牌
- 预消费的情况计算要欠的令牌
- 计算总的等待时间
- 更新下一次请求获取到令牌的时间
- 更新令牌余额(当前令牌数量)
- 返回下一个请求获取到令牌的时间点
- 使用计时器休眠指定时间
- 返回休眠时长
- 参考文章 http://t.csdn.cn/FFUHt
- 计算本次请求需要休眠多久
- rateLimiter的创建
- 重要思想:
- 任务的续接
- 超限任务包装入队
- 超限任务会抛特定异常,程序在捕获到该异常,会包装检索信息,任务到达第几步等信息,并通过传到redis中
- 任务出队,任务续接
- 从Redis中pop任务
- 定时任务,使用方法@scheduled()+类的@enableScheduling来实现
- while循环,new新的线程去执行任务,然后sleep几秒
- 消费任务(异步)
- 将信息重新装入threadlocal
- 获取策略集,重新按序执行
- Update注解会判断startTag,如果startTag与当前执行方法的类名不一致,会跳过该策略(方法输入参数有isSkip,通过在AOP方法里修改jp.getArgs的值,是isskip置为TRUE,从而跳过当前方法)
- 将续接的结果放到指定的位置(这个位置与超限后返回结果里面的位置一致)。
- 从Redis中pop任务
- 超限任务包装入队
- RateLimiter限流



