局部过滤器:
对某一个或者某一些请求过滤处理
案列 : 防止提前下单的操作
1.建一个类防止抢卷时提前下单的局部过滤器
/**
* 防止抢卷时提前下单的局部过滤器
* 过滤器链肯定要预先级
* SpringCloud Gateway X Servlet技术(命令式编程) webFlux(响应式编程技术)
*/
public class QiangJuanFilter implements GatewayFilter, Ordered {
/**
* 过滤的核心处理方法
* @param exchange
* @param chain
* @return
*/
@Override
public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
//放行
return chain.filter(exchange);
}
/**
* 当前过滤器的顺序值越小,优先级越大,可以是负数
* @return
*/
@Override
public int getOrder() {
return 0;
}
}
2.启动类的处理
/**
* gateway 不能添加spring-boot-starter-web这个依赖。是因为scanBasePackages = "com.qf"会扫描到全局异常里面的类,类里面又有注解用到了spring-boot-starter-web依赖所以会报错
*/
@SpringBootApplication
@ComponentScan(basePackages = "com.qf",excludeFilters = @ComponentScan.Filter(type = FilterType.ASSIGNABLE_TYPE,classes = {GlobalException.class, SystemException.class}))
@EnableEurekaClient
3.建一个局部过滤器的工厂类
/**
* 局部过滤器的工厂
*/
@Component
public class QiangJuanFilterFactory extends AbstractGatewayFilterFactory {
@Autowired
private QiangJuanFilter qiangJuanFilter;
/**
* 工厂返回的过滤器的对象
* @param config
* @return
*/
@Override
public GatewayFilter apply(Object config) {
return qiangJuanFilter;
}
/**
* 设置局部过滤器的名称
* @return
*/
@Override
public String name() {
return "myFilter";
}
}
4.配置中心一定要先配置抢券局部过滤器,在配置抢卷的所有服务
- id: qiangjuan
uri: lb://micro-qiangjuan
predicates:
- Path=/qiangjuan/get
filters:
- myFilter
- id: qiangjuan
uri: lb://micro-qiangjuan
predicates:
- Path=/qiangjuan/**
全局过滤器:
1.对所有请求都进行过滤处理
2.基于过滤器的请求限流
在解决高并发的有效途径是redis缓存数据尽可能的提高的响应速度,还有一个就是限流,可以接入第三方服务,很多服务会给一个限制,比如你是普通用户,每天只能调100次接口比如发短信,要是你充钱了就可以调用1000次。在高并发的情况下可以保护后端服务的作用。限流的缺陷就是,有可能会干掉有效的请求,因为限流的原因没办法处理。
请求计数限流:把限流的限制条件写到到redis里面去。首先在getway写一个过滤器,redis里写一个计数器number,计数初始为零,过期时间expire为1秒,最大计数maxnumber是100次,一个请求通过getway里面的过滤器,过滤器只做一件事对redis的number++,加完之后没有超过maxnumber,这个请求就正常的放行如果请求次数超过了maxnumber就不放行处理了,1秒之后不管之前累加了多少次就过期了,又重新计算这样就保证了每秒能保证有最大计数maxnumber。这里maxnumber取决于后台1秒你承受的压力。这种方式是有问题的,假如我们把1秒分为两个0.5秒,前0…5秒没有请求,后0.5秒有100次请求,第二秒的前0.5秒有100次请求,后0.5秒没有,那么在中间这1秒就发生了200次请求。后台就坑不住压力了。
漏桶算法限流:用的比较少
令牌桶算法限流:他的核心原理是,准备一个令牌桶,令牌桶里面放一个个的令牌token,令牌最大容量,按照一个频率固定的往令牌桶里面放令牌。假设1秒钟放100个令牌他不是1秒100的放,他是10毫秒放1个令牌,保证1秒放到100个令牌。令牌桶满了就所有的令牌丢弃掉。这个时候来了一个请求去令牌桶申请一个一个令牌拿到了令牌的请求就往后走,没拿到令牌要么等待,要么拒绝。这样就不会出现请求计数限流的缺陷。现在一般都是用的限流用的都是令牌桶。他的优势就是允许小时间范围的并发。
那该怎么实现呢?首先开一个线程,固定的往一个桶里面放,那这个桶是什么呢?这个桶我们可以用hash结构,hash结构是key filed value 这个key就是桶的名字,filed就是令牌的数量 ,和桶的令牌最大的数量。
桶多了就不太合适了,具体可以这么做:每秒钟放多少令牌,一个令牌要放多少时间这只是一个概念。假如现在桶里面有10个令牌,同时在记录一下当时的时间,现在有一个请求过来要去拿令牌了,比如现在有一个请求要拿20个令牌,明显10个令牌不够,但是请求来的时候也有一个时间,这个时间和记录时间有一个时间差,如果差了2秒,理论上会产生200个令牌,所以桶里面的令牌应该是210个,但是不能超过最大值,这个时候令牌最终是100个,所以令牌是够取的。所以实际上是没有放令牌这个动作的,只要拿令牌的时候去算一下当前令牌的数量,这样做的好处就是没有放令牌的线程,但是还有一个问题拿令牌的时候又要去算,又要去取,还要更新令牌是会有线程安全问题的,这个时候就用lua脚步原子性。
gateway基于redis实现令牌桶算法限流:这个key就是你根据什么来限流的凭证,假如我是根据接口限流的现在有一个qinango的接口,那么qinango的接口路径就是key只要访问这个接口就是同一个桶, 这样就会被桶里面的令牌给限制住。假如我是用户限流,key就是用户id了,这样相同的用户id就用同一个桶。那桶里面field value有哪些呢?有令牌数量 tokens 100个,最大的令牌数maxTokens 100,1秒钟会放多少令牌 tokensec 50。如果桶一多性能就会越来越差,所以一般来讲令牌桶会在桶里面多加一个字段,叫下一个可以取令牌的时间,nexTime 这个时间是微妙值。我们获取令牌的时候花点时间去计算一下令牌,这个微妙值就是计算的时间,这样做的好处就是没有线程放令牌减少服务的压力。令牌桶支持预支令牌的操作,在高并发的情况下,可能往往很难找到令牌桶里面满足你当前令牌的时刻,那么这个请求就没有办法执行