微服务保护

一、初识 Sentinel

1. 雪崩问题及解决方案

微服务调用链路中的某个服务故障

引起整个链路中的所有微服务都不

可用,这就是雪崩

   常见解决方式有四种:

超时处理:设定超时时间,请求

    过一定时间没有响应就返回错误信

    息,不会无休止等待

舱壁模式限定每个业务能使用的

    线程数,避免耗尽整个 tomcat 的资

    源,因此也叫线程隔离

熔断降级:由断路器统计业务执行

    的异常比例,如果超出阈值则会熔断

    该业务,拦截访问该业务的一切请求

流量控制:限制业务访问的 QPS,

    避免服务因流量的突增而故障

2. 服务保护技术对比

3. Sentinel 介绍和安装

Sentinel 是阿里巴巴开源的一款微服务

流量控制组件

https://sentinelguard.io/zh-cn/index.html Sentinel

具有以下特征:

① 丰富的应用场景

② 完备的实时监控

③ 广泛的开源生态

④ 完善的 SPI 扩展点

(2) 安装 Sentinel 控制台:  

① 准备好 sentinel-dashboard-1.8.1.jar

② 执行 java -jar sentinel-dashboard-1.8.1.jar

③ 访问:localhost:8080 即可看到控制台页面,

    默认的账户和密码都是 sentinel

(3) 修改 Sentinel 的默认端口、账户、密码

java -jar sentinel-dashboard-1.8.1.jar -Dserver.port=8080

4. 微服务整合 Sentinel

在 order-service 中整合 Sentinel,并且连接

Sentinel 的控制台,步骤如下:

(1) 引入 sentinel 依赖:

<!--sentinel-->
<dependency>
    <groupId>com.alibaba.cloud</groupId> 
    <artifactId>spring-cloud-starter-alibaba-sentinel</artifactId>
</dependency>

(2) 配置控制台地址

修改 application.yaml 文件,添加下面内容:

server:
  port: 8088
spring:
  cloud: 
    sentinel:
      transport:
        dashboard: localhost:8080

(3) 访问微服务的任意端点,触发 sentinel 监控

访问 http://localhost:8088/order/101

二、限流规则

1. 快速入门

簇点链路:就是项目内的调用链路,链路

                 中被监控的每个接口就是一个

                 资源

默认 sentinel 会监控 SpringMVC 的每一

个端点 (Endpoint),因此 SpringMVC 的

每一个端点 (Endpoint) 就是调用链路中的

一个资源。流控、熔断等都是针对簇点链

路中的资源来设置的,因此我们可以点击

对应资源后面的按钮来设置规则

点击资源 /order/{orderId} 后面的流控

按钮,就可以弹出表单,表单中可以

添加流控规则

图中含义:限制 /order/{orderId} 这个资源

的单机 QPS 为 1,即每秒只允许1次请求

超出的请求会被拦截并报错

2. 流控模式

在添加限流规则时,点击高级选项,可以选

择三种流控模式:

直接:统计当前资源的请求,触发阈

    值时对当前资源直接限流,也是默认

    的模式

关联:统计与当前资源相关的另一个

    资源,触发阈值时,对当前资源限流

链路:统计从指定链路访问到本资源

    的请求,触发阈值时,对指定链路限

    流

(2) 关联

满足下面条件可以使用关联模式:

① 两个有竞争关系的资源

② 一个优先级较高,一个优先级较低

当 /write 资源访问量触发阈值时,就会对

/read 资源限流,避免影响 /write 资源 

(3) 链路

例如有两条请求链路:

/test1 → /common

/test2 → /common

如果只希望统计从 /test2 进入到 /common

的请求,则可以这样配置 

① Sentinel 默认只标记 Controller 中的方

    法为资源,如果要标记其他方法,需要

    利用 @SentineResource 注解

@SentineResource("goods")
public void queryGoods() {
        System.err.println("查询商品");
} 

② Sentinel 默认会将 Controller 方法做

    context 整合,导致链路模式的流控失

    效

   需要修改 application.yml,添加配置:  

spring:
  cloud:
    sentinel:
      web-context-unify:false # 关闭 context 整合 

3. 流控效果

流控效果是指请求达到流控阈值时应该

采取的措施

  包括三种:

快速失败:达到阈值后,新的请求会被

    立即拒绝并抛出 FlowException 异常

    (默认)

warm up预热模式,对超出阈值的请

    求同样是拒绝并抛出异常,但这种模式

    阈值会动态变化,从一个较小值逐渐增

    加到最大阈值

排队等待:让所有的请求按照先后次序

    排队执行,两个请求的间隔不能小于指

    定时长

4. 热点参数限流

之前的限流是统计访问某个资源的所有请求

判断是否超过 QPS 阈值,而热点参数限流

分别统计参数值相同的请求,判断是否超

过 QPS 阈值

(1) 配置示例: 

对 hot 这个资源的 0 号参数 (第一个参数)

做统计,每 1 秒相同参数值的请求数不能

超过 5

 

(2) 在高级选项中,可以对部分参数设置

     例外配置:

结合上一个配置,这里的含义是对 0 号

的 long 类型参数限流,每 1 秒相同参数

的 QPS 不能超过 5

有两个例外:

① 如果参数值是 100,则每 1 秒允许的

    QPS 为 10

② 如果参数值是 101,则每 1 秒允许的

    QPS为 15 

注意:热点参数限流对默认的 SpringMVC

          资源无效

三、隔离和降级

虽然限流可以尽量避免因高并发而引起的

服务故障,但服务还会因为其它原因而故

障,而要将这些故障控制在一定范围,避

免雪崩,就要靠线程隔离 (舱壁模式) 和熔

断降级手段了,不管是线程隔离还是熔断

降级,都是对客户端 (调用方) 的保护

1. FeignClient 整合 Sentinel

(1) 修改 OrderService 的 application.yml 文件,

     开启 Feign 的 Sentinel 功能

feign:
  sentinel:
    enabled: true # 开启Feign的Sentinel功能

(2) 给 FeignClient 编写失败后的降级逻辑

方式一:FallbackClass无法对远程调用

              的异常做处理

方式二:FallbackFactory,可以对远程调

              用的异常做处理,我们选择这种

  给 FeignClient 编写 FallbackFactory 并注册

  为 Bean 

@Slf4j
@Bean
public class UserClientFallbackFactory implements FallbackFactory<UserClient> {
    
    @Override
    public UserClient create(Throwable throwable) {
        // 创建UserClient接口实现类,实现其中的方法,编写失败降级的处理逻辑
        return new UserClient() {
            @Override
            public User findById(Long id) {
                // 记录异常信息
                log.error("查询用户失败", throwable);
                // 根据业务需求返回默认的数据,这里是空用户
                return new User();
            }
        };
    }
}

(3) 在 feing-api 项目中的 UserClient 接口中

    使用 UserClientFallbackFactory

@FeignClient(value = "userservice", fallbackFactory = UserClientFallbackFactory.class)
public interface UserClient {

    @GetMapping("/user/{id}")
    User findById(@PathVariable("id") Long id);
}

2. 线程隔离(舱壁模式)

   线程隔离有两种方式实现:

① 线程池隔离

② 信号量隔离 (Sentinel 默认采用)

(1) 信号量隔离

优点:轻量级,无额外开销

缺点:不支持主动超时

           不支持异步调用

场景:高频调用,高扇出

(2) 线程池隔离

优点:支持主动超时

           支持异步调用

缺点:线程的额外开销比较大

场景:低扇出

低扇出:就是说让一个类里少量或适中地

              使用其他的类

高扇出 (超过约7个):说明一个类使用了大

         量其他的类,因为可能变得过于复杂

 在添加限流规则时,可以选择两种阈值类型:

QPS:就是每秒的请求数

线程数:是该资源能使用用的 tomcat 线程

             数的最大值,也就是通过限制线程

             数量,实现舱壁模式 

3. 熔断降级

熔断降级是解决雪崩问题的重要手段,其

思路是由断路器统计服务调用的异常比例、

慢请求比例,如果超出阈值则会熔断该服

务。

即拦截访问该服务的一切请求;而当服务

恢复时,断路器会放行访问该服务的请求

断路器熔断策略有三种:

① 慢调用

② 异常比例

③ 异常数

(1) 慢调用

业务的响应时长 (RT) 大于指定时长的请

求,统计单位时长内慢调用的比例,超

过阈值则熔断

RT 超过 500ms 的调用是慢调用,统计最近

10000ms 内的请求,如果请求量超过 10 次,

并且慢调用比例不低于0.5,则触发熔断,熔

断时长为5秒,然后进入 half-open 状态,放

行一次请求做测试

(2) 异常比例

统计单位时长内异常调用的比例,超过

阈值则熔

 

统计最近 1000ms 内的请求,如果请求量

超过 10 次,并且异常比例不低于 0.5,则

触发熔断,熔断时长为 5 秒,然后进入

half-open 状态,放行一次请求做测试 

(3) 异常数

统计单位时长内异常调用的次数,超过

阈值则熔断

 

四、授权规则

(1) 授权规则

授权规则可以对调用方的来源做控制,

有两种方式:

白名单:来源 (origin) 在白名单内的调用者

              允许访问

黑名单:来源 (origin) 在黑名单内的调用者

              不允许访问

例如,我们限定只允许从网关来的请求访问

orde-service,那么流控应用中就填写网关

的名称

① Sentinel 是通过 RequestOriginParser

    这个接口的 parseOrigin 来获取请求的

    来源的

public interface RequestOriginParser {
	/**
	* 从请求request对象中获取origin,获取方式自定义
	*/
	String parseOrigin(HttpServletRequest request);
}

在 Sentinel 中,该接口的 parseOrigin 方法

返回的永远是 default,也就是说,无论请求

开源是来自网关还是浏览器,Sentinel 根本

无法区分这两个请求

需要实现这个接口,编写它的业务逻辑,让

从网关来的请求和浏览器来的请求返回不同

的结果

② 从 request 中获取一个名为 origin 的请求

    头,作为 origin 的值

@Component
public class HeaderOriginParser implements RequestOriginParser {
    @Override
    public String parseOrigin(HttpServletRequest httpServletRequest) {
        // 尝试获取请求头
        String origin = httpServletRequest.getHeader("origin");
        // 非空判断
        if (StringUtils.isEmpty(origin)){
            origin = "blank";
        }
        return origin;
    }
}

③ 在 gateway 服务中,利用网关的全局过

    滤器添加名为 gateway 的 origin 头

spring:
  cloud:
    gateway:
      default-filters:
        - AddRequestHeader=origin,gateway # 添加名为origin的请求头,值为gateway

④ 给 /order/{orderId} 配置授权规则:

(2) 自定义异常结果

默认情况下,发生限流、降级、授权拦截

时,都会抛出异常到调用方

如果要自定义异常时的返回结果,需要实现

BlockExceptionHandler 接口:

public interface BlockExceptionHandler {
    // 处理请求被限流、降级、授权拦截时抛出的异常
    void handle(HttpServletRequest var1, HttpServletResponse var2, BlockException var3) throws Exception;
}

BlockException 包含很多个子类,以应对不同

的场景:

异常 说明
FlowException 限流异常
ParamFlowException 热点参数限流的异常
DegradeException 降级异常
AuthorityException 授权规则异常
SystemBlockException 系统规则异常

在 order-service 中定义类,实现 BlockExceptionHandler 接口:

@Component
public class SentinelExceptionHandler implements BlockExceptionHandler {
    @Override
    public void handle(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, 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;
        }

        httpServletResponse.setContentType("application/json;charset=utf-8");
        httpServletResponse.setStatus(status);
        httpServletResponse.getWriter().write("{\"msg\": " + msg + ", \"status\":" + status + "}");
    }
}

五、规则持久化

1. 规则管理模式

Sentinel 的控制台规则管理有三种模式:

① 原始模式:控制台配置的规则直接推送到

  Sentinel 客户端,也就是我们的应用,然后

  保存在内存中,服务重启则丢失

 

② pull 模式:控制台将配置的规则推送到

  Sentinel 客户端,而客户端会将配置规则

  存在本地文件或数据库中,以后会定时去本

  地文件或数据库中查询,更新本地规则

③ push 模式:控制台将配置规则推送到远程

   配置中心,例如 Nacos 或 Zookeeper,

   Sentinel 客户端监听 Nocas,获取配置变更

   的推送消息,完成本地配置更新 (推荐)

2. 实现push模式

push 模式的实现最为复杂,因为其依赖于

nacos,并且需要改 Sentinel 控制台的源码,

整体步骤如下:

(1) 修改 order-service 服务,使其监听

     Nacos 配置中心

① 引入依赖

<dependency>
    <groupId>com.alibaba.csp</groupId>
    <artifactId>sentinel-datasource-nacos</artifactId>
</dependency>

② 配置 nacos 地址

spring:
  cloud:
    sentinel:
      datasource:
        flow:
          nacos:
            server-addr: localhost:8848 # nacos地址
            dataId: orderservice-flow-rules
            groupId: SENTINEL_GROUP
            rule-type: flow   # 还可以是degrade、authority、param-flow
        degrade:
          nacos:
              server-addr: localhost:8848 # nacos地址
              dataId: orderservice-degrade-rules
              groupId: SENTINEL_GROUP
              rule-type: degrade   # 还可以是degrade、authority、param-flow


(2) 修改 Sentinel-dashboard 源码,配置

     nacos 数据源

修改源码的 pom 文件,将 sentinel-

datasource-nacos 依赖的 scope 去掉

<dependency>
    <groupId>com.alibaba.csp</groupId>
    <artifactId>sentinel-datasource-nacos</artifactId>
    <!--<scope>test</scope>-->
</dependency>


(3) 修改 Sentinel-dashboard 源码, 修改前端

     页面

① 拷贝 test 目录下的 nacos 代码到 main下的

   com.alibaba.csp.sentinel.dashboard.rule

② 修改刚刚拷贝的 nacos 包下的 NacosConfig

    类,修改其中的 nacos 地址

@Bean
public ConfigService nacosConfigService() throws Exception {
    return ConfigFactory.createConfigService("localhost:8848"); 
}

④ 修改 com.alibaba.csp.sentinel.dashboard.

    controller.v2 包下的 FlowControllerV2 类

(4) 重新编译、打包 Sentinel-dashboard 源码

① 修改 src/main/webapp/resources/app/scripts/

   directives/sidebar/ 目录下的 sidebar.html 文件,

   将其中的这部分注释打开

② 修改其中的文本

<li ui-sref-active="active" ng-if="entry.appType==0">
    <a ui-sref="dashboard.flow({app: entry.app})">
        <i class="glyphicon glyphicon-filter"></i>&nbsp;&nbsp;流控规则-NACOS</a>
</li>

猜你喜欢

转载自blog.csdn.net/m0_72041293/article/details/131656168