可以下载sentinel的jar包,用java -jar命令直接启动
默认端口就是8080,这里随便写一下演示,其他修改还是直接看Sentinel网站吧
java -jar -Dserver.port=8080 sentinel的jar包名.jar
对需要进行流量控制的服务进行依赖导入(这个依赖直接在父工程引入似乎无效,不知为何)
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-sentinel</artifactId>
</dependency>
配置application.yml
spring:
cloud:
sentinel:
transport:
dashboard: localhost:8088 #sentinel控制台地址
提示:可以用jmeter模拟请求测试。
流控模式
我们可以直接在sentinel的控制台对每个簇点链路添加限流规则,sentinel有三种流控模式可以选择:
1、直接:统计当前资源的请求,出发阈值时对当前资源直接限流,默认是该模式
2、关联:统计与当前资源相关的另一个资源,当关联资源触发阈值时,对当前资源限流
3、链路:统计从指定链路访问到本资源的请求,触发阈值时,对指定链路限流(也就是指定某个方法每秒调用本方法的次数,如果超过阈值,就限制那个方法的调用请求)
Sentinel默认只标记Controller中的方法为资源,如果想标记其他方法,需要在方法上加上@SentinelResource("取一个资源名")注解
另外,Sentinel默认会将Controller方法做context整合,这会导致所有Conrtoller方法都为同一个父链路的子链路,这样一来链路模式将会失效,所以我们需要给application.yml配置文件添加配置来取消整合
spring:
cloud:
sentinel:
web-context-unify: false
流控效果
1、快速失败:QPS(每秒请求数)超过阈值时,拒绝新的请求
2、warm up:QPS超过阈值时,拒绝新的请求;QPS阈值是逐渐提升的,可以避免冷启动时的高并发导致服务宕机
3、排队等待:所有请求都会进入队列,然后按照设定好的阈值依次执行请求。如果请求预期等待时间大于超时时间,则直接拒绝
热点参数限流
有时候,对于同一个资源,我们不能对所有请求采取同样的阈值限流。可能因为参数值的不同,我们也需要设定不同的阈值。
例如商品的查询功能,对不同商品,热门商品的阈值理论上应该比普通商品更高。所以我们需要热点参数限流。
注意:热点参数限流对默认的SpringMVC资源无效。用人话来说就是,类似于/order/{orderId}这种自动从Controller中识别的资源名无法设置热点参数限流,我们需要在Controller方法中手动添加@SentinelResource("取一个资源名")注解,使用这种手动添加的资源名才能对该方法进行热点参数限流
线程隔离和熔断降级
线程隔离:服务A需要去调用服务B和服务C,那么服务A会给调用服务B和服务C的请求专门划分固定的线程数,这样假如服务B故障了,我们可以只损耗留给服务B的线程,而不会影响服务C。
熔断降级:当服务A去访问服务B时,统计失败次数(如何算失败看具体怎么设定),如果失败比例过高,则后续会直接拒绝服务B的访问。
我们在SpringCloud中,使用的是Feigh进行服务之间的调用,而且Feigh支持Sentinel功能。所以我们只需要将Feigh和Sentinel进行整合即可。
此处我们将Feigh单独做了一个子工程,然后让其他项目调用他。
先对使用Feigh的项目的application.yml进行配置
feign:
sentinel:
enabled: true #开启feigh对sentinel的支持
接下来对Feigh项目模块做配置
实现FallbackFactory接口,UserClients为书写远程调用的接口
用匿名内部类实现UserClients接口,书写我们的降级方法,我们采用直接返回一个空的对象
@Slf4j
public class UserClientFallbackFactory implements FallbackFactory<UserClients> {
@Override
public UserClients create(Throwable throwable) {
return new UserClients() {
@Override
public User findById(Long id) {
log.error("查询用户异常",throwable);
return new User();
}
};
}
}
将实现好的接口类注册为Bean
public class DefaultFeignConfiguration {
@Bean
public UserClientFallbackFactory userClientFallbackFactory(){
return new UserClientFallbackFactory();
}
}
//已经在使用Feign模块的项目的启动类中配置了该类为默认配置类
//@EnableFeignClients(clients = UserClients.class,defaultConfiguration = DefaultFeignConfiguration.class)
在接口中使用UserClientFallbackFactory
@FeignClient(value = "userservice", fallbackFactory = UserClientFallbackFactory.class)
public interface UserClients {
@GetMapping("/user/{id}")
User findById(@PathVariable("id") Long id);
}
线程隔离
接下来就能在Sentinel控制台看见GET:http://userservice/user/{id}这种资源名
然后在Sentinel中对该资源进行流量控制,流控选择线程数就可设定为线程隔离
熔断降级
直接在Sentinel控制台中的簇点链路中点击降级操作设置即可
熔断策略有三种:
1、慢调用比例。每次请求超过指定时长则视为慢调用,统计时长内慢调用比例超过限定值,熔断
2、异常比例。统计时长内异常比例超过限定值,熔断
3、异常数。统计时长内异常调用次数超过限定值,熔断
授权规则
为了服务的安全,我们一般会设置网关服务,由网关对请求进行处理,筛选,然后再向对应服务发起请求。但网关并不能限制用户直接去访问服务的IP地址,如果服务的IP一旦泄露,网关的作用就消失了。所以,为了确保所有请求一定要经过网关,将未经过网关的请求全部拒绝访问,我们可以使用Sentinel的授权规则。
我们需要在进行服务的项目中,实现RequestOriginParser接口。这样当有请求访问该服务时,该方法会向Sentinel传入一个参数,根据该参数是否 处于白名单/不处于黑名单 来判断是否允许访问该服务
@Component
public class HeaderOriginParser implements RequestOriginParser {
@Override
public String parseOrigin(HttpServletRequest httpServletRequest) {
String origin = httpServletRequest.getHeader("origin");
if (StringUtils.isEmpty(origin)){
origin="empty";
}
return origin;
}
}
可以在网关的application.yml添加如下配置,将网关的所有路由都添加默认的请求头,可以看Gateway实现统一网关。
我们此处添加了Key为origin,Value为gateway的请求头。上述实现的RequestOriginParser接口中获取了Value,由于Value值是上述图中的白名单,所以网关发出的请求通过授权。
spring:
cloud:
gateway:
default-filters:
- AddRequestHeader=origin,gateway
自定义异常结果
当我们使用Sentinel进行流量控制的时候,如果Sentinel拒绝了访问,返回的消息都是一样的,这样对前端非常不友好,我们可以实现BlockExceptionHandler接口,来辨别异常信息,并对异常进行处理
@Component
public class SentinelExceptionHeadler implements BlockExceptionHandler {
@Override
public void handle(HttpServletRequest request, HttpServletResponse response, BlockException e) throws Exception {
String msg = "未知异常";
int status = 429;
if (e instanceof FlowException){
msg = "请求被限流了";
} else if (e instanceof ParamFlowException) {
msg = "请求被热点参数限流";
} else if (e instanceof DegradeException) {
msg = "请求被降级了";
} else if (e instanceof AuthorityException) {
msg = "没有访问权限";
status = 401;
}
response.setContentType("application/json;charset=utf-8");
response.setStatus(status);
response.getWriter().println("msg:"+msg+",status:"+status);
}
}
规则持久化
Sentinel的规则默认是存在内存中的,所以每次重启服务,都会导致规则消失。所以我们需要对规则进行持久化。最好的是将持久化规则写在nacos等微服务的远程配置中心里,不过这需要去修改Sentinel的源码,具体网上查吧。
标签:服务,请求,阈值,流量,分布式服务,限流,Sentinel,public From: https://www.cnblogs.com/cyknote/p/18098007