随着微服务的流行,服务和服务之间的稳定性变得越来越重要。Sentinel 是面向分布式服务架构的轻量级流量控制组件,主要以流量为切入点,从限流、流量整形、熔断降级、系统负载保护等多个维度来帮助您保障微服务的稳定性。
gitee:https://gitee.com/rmlb/Sentinel/
github: https://github.com/alibaba/Sentinel
1.服务保护策略有哪些?
2.服务限流框架有哪些
3.服务限流算法有哪些?
4.令牌桶、漏桶、滑动窗口
5.RateLimiter Api接口接口限流
6.Nginx实现接口限流
7.Getway实现API接口限流
8.纯手写封装自己的微服务限流框架
服务限流、降级、熔断、隔离策略
服务限流概念:限定固定请求数量 访问服务器端 保护服务接口
限流框架:1.nginx限流 2.谷歌 Guava限流 3.阿里巴巴 Sentinel限流 4.Redis+lua实现限流
本质限流框架底层 都是基于 漏桶、令牌桶、滑动窗口算法实现。
补充概念:qps为1 表达意思就是 每s最多只能够处理1个请求
令牌桶的算法:
1.创建一个令牌桶 它的容量假设可以存放2个token;
2.以恒定的速度,生成令牌 如果令牌桶满了,则直接废弃 如果令牌桶
没有满 则存入到令牌桶中;
3.客户端请求过来,从令牌桶中获取令牌,如果能够获取到令牌就可以执行业务
代码,如果获取不到令牌就拒绝该请求。
Google的Guava工具包中就提供了一个限流工具类——RateLimiter,RateLimiter是基于“令牌通算法”来实现限流的。
1.maven依赖
<dependency>
<groupId>com.google.guava</groupId>
<artifactId>guava</artifactId>
<version>23.0</version>
</dependency>
2.相关代码
/**
* 限流 qps 1 每s 只会向 令牌桶中 投递一个token
*/
private RateLimiter rateLimiter = RateLimiter.create(1);
/**
* 根据RateLimiter 限流
*
* @return
*/
@RequestMapping("/getMayikt")
public String getMayikt() {
if (!rateLimiter.tryAcquire()) {
log.info("<接口被限流了>");
return "您访问次数过多,请稍后重试!";
}
// 正常执行业务逻辑
return "getMayikt";
}
1.自定义注解
package com.mayikt.service.ext;
import java.lang.annotation.*;
/**
* @author 余胜军
* @ClassName MayiktRateLimiter
* @qq 644064779
* @addres www.mayikt.com
* 微信:yushengjun644
*/
@Target({ElementType.METHOD, ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface MayiktRateLimiter {
/**
* 默认QPS是1 每s 向 令牌桶投递 1s 令牌
*
* @return
*/
double aqs() default 1;
/**
* 接口限流的名称
*
* @return
*/
String name() default "";
/**
* 限流之后报错提示
*
* @return
*/
String msg() default "当前访问人数过多,请稍后重试!";
}
2.aop拦截自定义注解
import com.google.common.util.concurrent.RateLimiter;
import com.mayikt.service.ext.MayiktRateLimiter;
import lombok.extern.slf4j.Slf4j;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.aspectj.lang.reflect.MethodSignature;
import org.springframework.stereotype.Component;
import java.lang.reflect.Method;
import java.util.concurrent.ConcurrentHashMap;
/**
* @author 余胜军
* @ClassName AopRateLimiter
* @qq 644064779
* @addres www.mayikt.com
* 微信:yushengjun644
*/
@Aspect
@Component
@Slf4j
public class AopRateLimiter {
/**
* 定义RateLimiter 限流集合容器
*/
private static ConcurrentHashMap<String, RateLimiter> rateLimiters = new ConcurrentHashMap<>();
/**
* 环绕通知
*
* @param pjp
* @return
*/
@Around(value = "@annotation(com.mayikt.service.ext.MayiktRateLimiter)")
public Object currentLimit(ProceedingJoinPoint pjp) throws Throwable {
// 1.获取拦截到目标方法
MethodSignature methodSignature = (MethodSignature) pjp.getSignature();
// 2.获取目标方法
Method method = methodSignature.getMethod();
// 3.获取目标方法上的注解
MayiktRateLimiter mayiktRateLimiter = method.getDeclaredAnnotation(MayiktRateLimiter.class);
// 4.获取到请求方法名称
String requestMethodName = method.getName();
// 5.从容器查找该 请求方法 对应的 rateLimiter 是否有创建 如果没有创建 就创建
RateLimiter rateLimiter = rateLimiters.get(requestMethodName);
if (rateLimiter == null) {
// 防止多个线程同时创建RateLimiter 使用双重检验锁 判断
synchronized (this) {
if (rateLimiter == null) {
// 获取注解上mayiktRateLimiter.aqs参数值
rateLimiter = RateLimiter.create(mayiktRateLimiter.aqs());
rateLimiters.put(requestMethodName, rateLimiter);
}
}
}
try {
// 6.判断是否被限流 如果是被限流 则 获取mayiktRateLimiter注解中的 msg 返回
if (!rateLimiter.tryAcquire()) {
return mayiktRateLimiter.msg();
}
} catch (Exception e) {
log.error("" , e);
}
// 没有被限流 则直接放行
return pjp.proceed();
}
}
3.限流框架使用案例
@RequestMapping("/getMeite")
@MayiktRateLimiter(aqs = 2, msg = "当前接口已经被限流")
public String getMeite() {
return "getMayikt";
}
蚂蚁课堂 nginx 1个ip 过来 访问 nginx 1s 10次
根据ip来做限流。
Nginx接入层限流可以使用Nginx自带的两个模块:
1.连接数限流模块ngx_http_limit_conn_module
2.漏桶算法实现的请求限流模块ngx_http_limit_req_module
limit_req_zone b i n a r y r e m o t e a d d r z o n e = o n e : 10 m r a t e = 1 r / s ; 第 一 个 参 数 : binary_remote_addr zone=one:10m rate=1r/s; 第一个参数: binaryremoteaddrzone=one:10mrate=1r/s;第一个参数:binary_remote_addr 表示通过remote_addr这个标识来做限制,“binary_”的目的是缩写内存占用量,是限制同一客户端ip地址。
第二个参数:zone=one:10m表示生成一个大小为10M,名字为one的内存区域,用来存储访问的频次信息。
第三个参数:rate=1r/s表示允许相同标识的客户端的访问频次,这里限制的是每秒1次
配置演示:
upstream backserver {
server 127.0.0.1:22222;
}
limit_req_zone $binary_remote_addr zone=myRateLimit:10m rate=1r/s;
server {
listen 80;
server_name localhost;
location /{
limit_req zone=myRateLimit;
proxy_pass http://backserver/;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
index index.html index.htm;
}
error_page 500 502 503 504 /50x.html;
location = /50x.html {
root html;
}
}
50x.html
<html>
<head>
<meta charset="utf-8"/>
</head>
<body>
您当前访问人数频率过高,请稍后重试!
</body>
1.配置文件内容
server:
port: 81
####服务网关名称
spring:
application:
name: mayikt-gateway
cloud:
gateway:
discovery:
locator:
####开启以服务id去注册中心上获取转发地址
enabled: true
###路由策略
routes:
###路由id
- id: mayikt-member
#### 基于lb负载均衡形式转发
uri: lb://mayikt-member
filters:
- StripPrefix=1
###匹配规则
predicates:
- Path=/mayikt-member/**
nacos:
discovery:
server-addr: 127.0.0.1:8848
2.定义过滤器
import com.google.common.util.concurrent.RateLimiter;
import org.springframework.cloud.gateway.filter.GatewayFilterChain;
import org.springframework.cloud.gateway.filter.GlobalFilter;
import org.springframework.core.io.buffer.DataBuffer;
import org.springframework.http.server.reactive.ServerHttpResponse;
import org.springframework.stereotype.Component;
import org.springframework.web.server.ServerWebExchange;
import reactor.core.publisher.Mono;
/**
* @author 余胜军
* @ClassName RateLimiter
* @qq 644064779
* @addres www.mayikt.com
* 微信:yushengjun644
*/
@Component
public class RateLimiterFilter implements GlobalFilter {
/**
* 限流 qps 1 每s 只会向 令牌桶中 投递一个token
*/
private RateLimiter rateLimiter = RateLimiter.create(1);
@Override
public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
boolean result = rateLimiter.tryAcquire();
if (!result) {
ServerHttpResponse response = exchange.getResponse();
String msg = "Interface access frequency is too high, please try again later";
DataBuffer buffer = response.bufferFactory().wrap(msg.getBytes());
return response.writeWith(Mono.just(buffer));
}
// 放行请求
return chain.filter(exchange.mutate().build());
}
}
3.通过网关访问接口:
http://127.0.0.1:81/mayikt-member/getGatewayLimiter