Skip to content

第八章 微服务保护 v1.0 ​

第一部分 微服务保护入门 ​

一、雪崩问题及解决方案 ​

1.雪崩问题 ​

微服务调用链路中的某个服务故障,引起整个链路中的所有微服务都不可用,这就是雪崩。

A服务在调用另一个已宕机服务B时,A服务久不释放连接导致资源耗尽,出现相互依赖的多级服务宕机的连锁反应

2.解决方案 ​

  1. 超时处理:设定超时时间,请求超过一定时间没有响应就返回错误信息,不会无休止等待,能够一定程度上缓解雪崩问题
  2. 舱壁模式:也叫线程隔离,限定每个业务能使用的线程数,避免耗尽整个tomcat的资源,但会有一定的资源浪费
  3. 熔断降级:由断路器统计业务执行的异常比例,如果超出阈值则会熔断该业务,拦截访问该业务的一切请求
  4. 流量控制:限制业务访问的QPS,避免服务由流量的突增而故障,能够有效预防雪崩(主要预防高并发流量导致的服务故障问题)

二、服务保护技术 ​

image.png

三、Sentinel ​

1.简介 ​

Sentinel是阿里巴巴开源的一款微服务流量控制组件,官网:Sentinel

2.安装 ​

  1. 在Github下载对应jar包
  2. 拷贝到一个非中文的目录,运行:java -jar sentinal-dashboard-1.8.1.jar
  3. 访问网页,默认账号密码为sentinel

3.常用配置修改 ​

在运行时直接添加:java -jar sentinal-dashboard-1.8.1.jar -D<配置项>=参数值

4.整合Sentinel ​

xml
<!--sentinel--> 
<dependency>
  <groupId>com.alibaba.cloud</groupId>
  <artifactId>spring-Cloud-starter-alibaba-sentinel</artifactId>
</dependency>
yaml
spring:
	cloud:
	 sentinel:
  	 transport:
    	 dashboard: localhost:8080

第二部分 Sentinel ​

一、流量控制 ​

1.簇点链路 ​

项目内的调用链路,链路中被监控的每一个接口都是一个资源

流控、熔断等都是针对簇点链路中的资源来设置的,可以通过Sentinel控制台来设置规则

2.流控模式 ​

a.直接 ​

(默认)统计当前资源的请求,触发阈值时对当前资源直接限流

b.关联 ​

统计与当前资源相关的另一个资源,触发阈值时,对当前资源限流

使用场景:两个有竞争关系的资源;两个资源一个优先级较高,一个优先级较低

比如:用户支付时需要修改订单状态,同时用户要查询订单。查询和修改操作会争抢数据库锁,产生竞争。业务需求是优先支付和更新订单的业务,因此当修改订单业务触发阈值时,需要对查询订单业务限流。

c.链路 ​

统计从指定链路访问到本资源的请求,触发阈值时,对指定链路访问限流

使用场景:对某个特定的业务访问资源的请求进行限流

d.资源标记 ​

Sentinel默认只标记Controller中的方法为资源进行监控,如果要标记其它方法,需要利用@SentinelResource注解

java
@SentinelResource("资源名")
public void queryName() {
    //...
}

Sentinel默认会将Controller方法做context整合,导致链路模式的流控失效,需要关闭该功能

java
//默认条件下controller1与controller2中的query资源会被视为同个资源
@GetMapping("controller1")
public String controller1() {
    service.query();  //query被标记为资源
}
@GetMapping("controller2")
public String controller2() {
    service.query();  //query被标记为资源
}
yaml
spring:
	cloud:
  	sentinel:
    	web-context-unify: false  # 关闭context整合

3.流控效果 ​

a.快速失败 ​

(默认)达到阈值后,新的请求会被立即拒绝并抛出FlowException异常

b.Warm up ​

预热模式,一般是应对服务冷启动的一种方案

预热模式会对超出阈值的请求同样拒绝并抛出异常,但阈值会动态变化,从一个较小值逐渐增加到最大阈值

相关指标:

  1. 单机阈值:最大QPS阈值(threshold)
  2. 预热时长:从初始QPS阈值到最大QPS阈值所需要的预热时间设置
  3. 冷启动因子(coldFactor):默认为3,一般不需要更改
  4. 初始QPS阈值:threshold/coldFactor
c.排队等待 ​

让请求按照顺序依次排队执行,且每两个请求的间隔不能小于指定时长,预期等待时间大于超时时间将直接拒绝

相关指标:

  1. 单机阈值:最大QPS阈值
  2. 超时时间:等待时间超过超时时间的请求将被拒绝并抛出异常
  3. 时间间隔:1s/threshold

4.热点参数限流 ​

常规限流统计访问某资源的所有请求;而热带参数限流统计参数值相同的请求,判断是否超过QPS阈值

相关指标:

  1. 参数索引:若参数为i,代表对第i+1个参数做统计
  2. 单机阈值:QPS阈值设定
  3. 统计窗口时长:统计计算单机阈值的时长

高级选项(参数例外项):

  1. 参数类型:需要修改流控规则的指定参数值的数值类型
  2. 参数值:需要修改流控规则的指定参数值的数值
  3. 限制阈值:对应请求限流的QPS阈值

注:热点参数限流只对使用@SentinelResource注解声明的资源生效

三、隔离与降级 ​

1.FeignClient整合Sentinel ​

  1. 修改服务消费者配置
yaml
feign:
	sentinel:
  	endabled: true  # 开启Feign的Sentinel功能
  1. 配置FeignClient失败后的降级逻辑
java
public class ClientFallbackFactory implements FallbackFactory<Client> {
    @Override
    public Client create(Throwable throwable) {
        // 创建Client接口实现类,实现其中方法并编写降级逻辑
        return new Client() {
            @Override
            public Response action() {
                log.error("失败", throwable);  // 日志记录
                return new Response();  // 返回相应
            }
        }
    }
}
  1. 将ClientFallbackFactory注册为Bean
java
@Bean
public ClientFallbackFactory clientFallback() {
    return new ClientFallbackFactory();
}
  1. 在对应Client接口中使用ClientFallbackFactory
java
@FeignClient(value="service", fallbackFactory = ClientFallbackFactory.class)
public interface Client {
    //...
}

2.线程隔离 ​

线程隔离默认有两种方式实现:线程池隔离、信号量隔离(Sentinel默认),只需要改变阈值类型即可切换

线程池隔离 ​

优点:支持主动超时、支持异步调用

缺点:基于线程池模式,线程额外开销较大

使用场景:低扇出场景(相互依赖的服务较少)

信号量隔离 ​

优点:基于计数器模式,轻量级、无额外开销

缺点:不支持主动超时、不支持异步调用

使用场景:高频率调用,高扇出场景(相互依赖的服务较多)

3.熔断降级 ​

断路器统计服务调用的异常比例、慢请求比例,如果超出阈值会熔断该服务

断路器状态图 ​

image.png

熔断策略 ​
  1. 慢调用:指定时间内,若请求数量超过设定最小数量,慢调用比例大于设定阈值,则触发熔断
  2. 异常比例:指定时间内,如果调用此处超过指定请求数,且异常比例大于设定阈值,则触发熔断
  3. 异常数:指定时间内,如果调用此处超过指定请求数,且异常数大于设定阈值,则触发熔断

四、授权规则 ​

对调用方的来源进行控制,有黑名单和白名单两种方式

Sentinel通过RequestOriginParser这个接口的parseOrigin来获取请求的来源的

java
@Component
public class HeaderOriginParser implements RequestOriginParser {
    @0verride
    public String parseOrigin(HttpServletRequest request) {
        String origin = request.getHeader("origin");
        if(StringUtils.isEmpty(origin)){
            return "blank";
        }
        return origin;
    }
}
yaml
spring:
	cloud:
		gateway:
			default-filters:
				- AddRequestHeader=grigin,gateway #添加名为origin的请求头,值为gateway

五、自定义异常结果 ​

1.实现 ​

要实现自定义异常结果,需要在服务提供者端定义类,实现BlockExceptionHandle接口

image.png

2.BlockException的子类 ​

image.png

六、规则持久化 ​

Sentinel默认会将流控规则保存在内存中,当重启时流控规则则会丢失,需要进行持久化

1.原始模式 ​

Sentinel的默认模式,将规则保存在内存中,重启服务规则会丢失

2.pull模式 ​

控制台将配置的规则推送到Sentinel客户端,客户端会将配置规则保存在本地文件或数据库中,服务会定时查询本地文件或数据库,更新本地规则

image.png

3.push模式 ​

控制台将配置规则推送到远程配置中心,如Nacos。对远程配置中心进行监听,获取变更并完成本地配置更新

image.png

4.实现push模式 ​

需要改动源码,详见: