浅聊一下微服务的服务保护

发布于:2025-09-09 ⋅ 阅读:(23) ⋅ 点赞:(0)

在微服务架构里,服务间调用关系错综复杂,一个服务出问题很可能引发连锁反应,也就是 “雪崩”。今天就带大家从零开始学习 Sentinel,这款阿里开源的微服务保护工具,帮你解决雪崩难题,做好流量控制、隔离降级。

一、初识 Sentinel:先搞懂雪崩和解决方案

1.1 雪崩问题到底是啥?

微服务中,一个服务往往依赖多个其他服务。比如服务 A 依赖服务 B,服务 B 又依赖服务 C。要是服务 C 故障了,服务 B 调用 C 时会阻塞,服务 A 调用 B 也会跟着阻塞。

服务器的线程和并发数是有限的,一旦大量请求阻塞,服务器资源很快就会耗尽,进而导致所有服务不可用。接着,依赖这些服务的其他服务也会陆续 “挂掉”,形成级联失败 —— 这就是雪崩。

1.2 解决雪崩的 4 种核心方案

既然雪崩这么可怕,那怎么防?主要有 4 种思路:

  • 超时处理:给请求设个超时时间,超过时间没响应就返回错误,别一直等。比如调用一个服务,超过 200ms 就直接提示 “请求超时”,避免线程一直占用。
  • 仓壁模式(线程隔离):像船舱的防水隔板一样,给每个业务分配独立的线程池。比如订单业务用 10 个线程,商品业务用 10 个线程,就算订单业务线程耗尽,也不影响商品业务。
  • 断路器:统计服务调用的异常比例,要是超过阈值(比如异常率 50%),就 “熔断”—— 暂时拦截所有访问该服务的请求,等服务恢复了再放行。
  • 限流:限制服务的 QPS(每秒请求数),比如每秒最多处理 100 个请求,超过的直接拒绝,避免突发流量把服务冲垮。

简单总结下:限流是 “预防”,提前挡住过多流量;超时、仓壁、断路器是 “补救”,故障发生时控制影响范围。

1.3 服务保护工具对比:为啥选 Sentinel?

Spring Cloud 里有 Hystrix、Resilience4J、Sentinel 等工具,国内用得最多的是 Sentinel,对比 Hystrix 优势很明显:

对比项 Sentinel Hystrix
隔离策略 信号量隔离 线程池 / 信号量隔离
熔断策略 慢调用 / 异常比例 失败比率
流量整形 支持预热、排队等待 不支持
控制台 开箱即用,秒级监控 不完善
生态适配 Spring Cloud、Dubbo 等 主要适配 Spring Cloud Netflix

Sentinel 不仅功能全,还能承接阿里双十一大促的流量,稳定性有保障,这也是咱们选它的核心原因。

1.4 Sentinel 安装:3 步搞定

Sentinel 有个可视化控制台,能配置规则、看监控,安装特别简单:

  1. 下载 jar 包:去 GitHub 搜 “Sentinel”,下载最新的 dashboard jar 包(比如 sentinel-dashboard-1.8.1.jar)。
  2. 运行 jar 包:把 jar 包放到非中文目录,打开命令行执行:
    # 默认端口8080,账号密码都是sentinel
    java -jar sentinel-dashboard-1.8.1.jar
    # 想改端口的话,比如改到8090
    java -Dserver.port=8090 -jar sentinel-dashboard-1.8.1.jar
    
  3. 访问控制台:打开浏览器输http://localhost:8080,输入账号密码(都是 sentinel)就能进。刚进去是空白的,因为还没整合微服务。

1.5 微服务整合 Sentinel:以 order-service 为例

以订单服务(shop-order)为例,整合 Sentinel 就 3 步:

  1. 加依赖:在 pom.xml 里加 Sentinel 依赖:
    <dependency>
        <groupId>com.alibaba.cloud</groupId>
        <artifactId>spring-cloud-starter-alibaba-sentinel</artifactId>
    </dependency>
    
  2. 配控制台地址:在 application.yaml 里加配置,告诉服务控制台在哪:
    spring:
      cloud:
        sentinel:
          transport:
            dashboard: localhost:8080 # 控制台地址
    
  3. 触发监控:启动服务后,访问一个接口(比如http://localhost:8091/order/prod/19),再刷新控制台,就能看到服务的监控数据了。

二、流量控制:提前挡住突发流量

限流是预防雪崩的关键,Sentinel 的流量控制功能很强大,咱们从基础到进阶慢慢讲。

2.1 先认识 “簇点链路”

请求进微服务时,会走DispatcherServlet → Controller → Service → Mapper这样的调用链,这就是 “簇点链路”。链路上的每个接口(比如 Controller 的方法)都是一个 “资源”,Sentinel 默认会监控 SpringMVC 的所有接口。

在控制台的 “簇点链路” 菜单里,能看到所有资源,每个资源后面都有 “流控”“降级” 等按钮,点进去就能配置规则。

2.2 流控快速入门:给接口设 QPS 阈值

比如给/order/prod/{pid}这个接口设限流规则:QPS 不超过 5(每秒最多 5 个请求)。

  1. 加规则:在簇点链路里,找到/order/prod/{pid},点后面的 “流控”,配置:
    • 阈值类型:QPS
    • 单机阈值:5
    • 其他默认,点 “新增”。
  2. 用 Jmeter 测试:建个线程组,20 个用户、2 秒内发完(QPS=10,超过 5)。运行后看结果,每秒只会成功 5 个请求,超过的会被拒绝。

2.3 3 种流控模式:应对不同场景

流控模式是指 “什么时候触发限流”,Sentinel 支持 3 种,咱们结合场景讲:

2.3.1 直接模式:默认模式,对当前资源限流

刚才的快速入门就是直接模式 —— 只要当前资源的 QPS 超过阈值,就直接限流。适合简单的接口保护,比如普通查询接口。

2.3.2 关联模式:高优先级资源优先,限流低优先级

场景:比如订单的 “更新”(/order/update)和 “查询”(/order/query)都操作数据库,更新要优先保证,要是更新请求太多,就限流查询。

  1. 加接口:在 OrderController 里加两个接口:
    // 查询订单
    @GetMapping("/order/query")
    public String queryOrder() {
        return "查询订单成功";
    }
    // 更新订单
    @GetMapping("/order/update")
    public String updateOrder() {
        return "更新订单成功";
    }
    
  2. 配规则:给/order/query加关联流控规则:
    • 流控模式:关联
    • 关联资源:/order/update(当更新接口 QPS 超 5 时,限流查询)
    • 阈值:5
  3. 测试:用 Jmeter 压/order/update(QPS=10),再用浏览器访问/order/query,会提示 “Blocked by Sentinel (flow limiting)”—— 查询被限流了。
2.3.3 链路模式:只限流指定来源的请求

场景:查询订单(/order/query)和创建订单(/order/save)都要调用 “查询商品”(queryGoods)方法,只想限流从 “查询订单” 进来的请求。

  1. 加方法和接口
    • OrderService 加 queryGoods 方法,用@SentinelResource("goods")标记(让 Sentinel 监控):
      @SentinelResource("goods")
      public void queryGoods() {
          System.err.println("查询商品");
      }
      
    • OrderController 加/order/save,并让/order/query/order/save都调用 queryGoods:
      // 查询订单调用商品查询
      @GetMapping("/order/query")
      public String queryOrder() {
          orderService.queryGoods();
          return "查询订单成功";
      }
      // 新增订单调用商品查询
      @GetMapping("/order/save")
      public String saveOrder() {
          orderService.queryGoods();
          return "新增订单成功";
      }
      
  2. 关资源聚合:Sentinel 默认会把 SpringMVC 请求聚合到一个 root 资源,链路模式会失效,所以要在 application.yaml 里关了:

    yaml

    spring:
      cloud:
        sentinel:
          web-context-unify: false # 关闭context整合
    
  3. 配规则:给 “goods” 资源加链路流控规则:
    • 流控模式:链路
    • 入口资源:/order/query(只统计从查询订单进来的请求)
    • 阈值:2
  4. 测试:用 Jmeter 压/order/save(QPS=4),访问/order/save正常;压/order/query(QPS=4),超过 2 的请求会被限流。

2.4 3 种流控效果:超过阈值后怎么处理?

流控效果是指 “QPS 超阈值后,怎么处理新请求”,Sentinel 支持 3 种:

2.4.1 warm up(预热模式):应对服务冷启动

服务刚启动时,资源没初始化(比如连接池没建立),要是直接扛最大 QPS,可能会宕机。预热模式就是让阈值从一个小值慢慢涨到最大值,给服务 “预热” 时间。

比如给/order/prod/{pid}设规则:

  • 阈值类型:QPS
  • 单机阈值:10(最大 QPS)
  • 流控效果:warm up
  • 预热时长:5 秒

Sentinel 默认冷启动因子是 3,所以初始阈值是 10/3≈3。刚启动时,每秒只能过 3 个请求,5 秒后慢慢涨到 10 个。用 Jmeter 测试,刚开始失败率高,后来会越来越低。

2.4.2 排队等待:让请求平滑执行

快速失败和 warm up 都是直接拒绝超阈值请求,排队等待则是让请求排队,按固定间隔执行,避免流量波动。

比如给/order/prod/{pid}设规则:

  • 阈值类型:QPS
  • 单机阈值:10(每秒 10 个,即每 200ms 一个)
  • 流控效果:排队等待
  • 超时时间:5000ms(请求等待超过 5 秒就拒绝)

用 Jmeter 压 QPS=15(超阈值),结果所有请求都会通过,但响应时间会变长 —— 因为都在排队。控制台看 QPS 曲线会很平滑,一直保持在 10 左右,对服务器很友好。

2.4.3 快速失败:默认效果,直接拒绝

超过阈值后,新请求直接被拒绝,抛出FlowException,适合对响应时间敏感的场景(比如支付接口,不能让用户等)。

2.5 热点参数限流:对不同参数区别对待

之前的限流是统计整个资源的 QPS,热点参数限流则是按 “参数值” 统计。比如/order/prod/{pid}接口,pid=1 是秒杀商品(热点),pid=2 是普通商品,想给它们设不同的 QPS 阈值。

2.5.1 实战案例:给 pid 设不同阈值

需求:/order/prod/{pid}默认 QPS=2,pid=1 设 QPS=4,pid=19 设 QPS=10。

  1. 标记资源:给接口加@SentinelResource("hot")(热点限流要这个注解):
    @SentinelResource("hot")
    @GetMapping("/order/prod/{pid}")
    public String getOrderByPid(@PathVariable Integer pid) {
        // 业务逻辑
    }
    
  2. 加热点规则:控制台进 “热点规则”,点 “新增”:
    • 资源名:hot
    • 限流模式:QPS 模式
    • 参数索引:0(第一个参数,即 pid)
    • 单机阈值:2(默认阈值)
    • 高级选项→参数例外项:
      • 加 pid=1(long 类型),阈值 4
      • 加 pid=19(long 类型),阈值 10
  3. 测试:用 Jmeter 分别压 pid=1(QPS=5)、pid=19(QPS=12)、普通 pid(QPS=3):
    • pid=1:每秒成功 4 个
    • pid=19:每秒成功 10 个
    • 普通 pid:每秒成功 2 个

三、隔离和降级:故障发生时控制影响范围

限流是预防,隔离和降级是故障发生后的 “止损” 手段。Sentinel 主要通过线程隔离(仓壁模式)和熔断降级来实现。

3.1 Feign 整合 Sentinel:远程调用要保护

微服务远程调用大多用 Feign,所以要先整合 Feign 和 Sentinel,才能在远程调用时做隔离和降级。

3.1.1 开启 Feign 的 Sentinel 支持

在 order-service 的 application.yaml 里加配置:

feign:
  sentinel:
    enabled: true # 开启Feign对Sentinel的支持
3.1.2 写失败降级逻辑

远程调用失败时(比如服务宕机),不能直接报错,要返回友好提示。有两种方式:

方式 1:FallbackClass(不支持捕获异常)
  1. 写降级类,实现 Feign 接口:
    @Component
    public class ProductServiceFallback implements IProductService {
        @Override
        public Product findByPid(Integer pid) {
            // 降级逻辑:返回默认值
            Product product = new Product();
            product.setPid(-1);
            product.setPname("暂无商品");
            return product;
        }
    }
    
  2. Feign 接口指定降级类:
    @FeignClient(value = "service-product", fallback = ProductServiceFallback.class)
    public interface IProductService {
        @GetMapping("/product/{pid}")
        Product findByPid(@PathVariable Integer pid);
    }
    
方式 2:FallbackFactory(支持捕获异常)

要是想知道调用失败的原因,就用 FallbackFactory:

  1. 写降级工厂类:
    @Component
    public class ProductServiceFallbackFactory implements FallbackFactory<IProductService> {
        @Override
        public IProductService create(Throwable throwable) {
            // 打印异常信息
            System.out.println("调用失败:" + throwable.getMessage());
            // 返回降级逻辑
            return new IProductService() {
                @Override
                public Product findByPid(Integer pid) {
                    Product product = new Product();
                    product.setPid(-1);
                    product.setPname("暂无商品");
                    return product;
                }
            };
        }
    }
    
  2. Feign 接口指定降级工厂:
    @FeignClient(value = "service-product", fallbackFactory = ProductServiceFallbackFactory.class)
    public interface IProductService {
        @GetMapping("/product/{pid}")
        Product findByPid(@PathVariable Integer pid);
    }
    

3.2 线程隔离(仓壁模式):避免资源耗尽

线程隔离是给每个远程调用分配独立的线程池(或信号量),就算某个调用故障,也只消耗该线程池的资源,不影响其他调用。

3.2.1 两种隔离方式对比
隔离方式 原理 优点 缺点
信号量隔离 计数器模式,统计线程数 简单,开销小 不能隔离慢调用
线程池隔离 给每个调用分配独立线程池 隔离彻底,支持慢调用 线程切换开销大

Sentinel 默认用信号量隔离,要是需要隔离慢调用(比如调用一个慢服务),就用线程池隔离。

3.2.2 实战:给 Feign 接口设线程数阈值

需求:order-service 调用 product-service 的接口,线程数不超过 2(即同时最多 2 个线程调用)。

  1. 加规则:在簇点链路里找到 Feign 接口(比如/product/{pid}),点 “流控”:
    • 阈值类型:线程数
    • 单机阈值:2
    • 其他默认
  2. 测试:用 Jmeter 发 10 个并发请求(瞬间发完),会发现部分请求返回降级逻辑(“暂无商品”)—— 因为线程数超 2 了,超出的请求被限流。

3.3 熔断降级:故障服务暂时 “隔离”

要是一个服务调用失败率很高(比如 80% 的请求都失败),继续调用只会浪费资源,这时候就该 “熔断”—— 暂时拦截所有调用,等服务恢复了再放行。

Sentinel 的熔断策略有两种:

  • 慢调用比例:请求响应时间超过 “慢调用阈值” 的比例,超过阈值就熔断。
  • 异常比例:调用失败(抛异常)的比例超过阈值,就熔断。

配置方式和流控类似,在簇点链路里点 “降级”,选对应的策略和阈值即可。比如设 “异常比例”:异常率超过 50%,熔断时长 5 秒 ——5 秒内所有调用都会被拦截,5 秒后会放少量请求试探,要是恢复了就取消熔断。

总结

Sentinel 的核心功能就是 “流量控制” 和 “隔离降级”:

  • 流量控制:通过直接 / 关联 / 链路模式,结合快速失败 /warm up / 排队效果,提前挡住突发流量,预防雪崩。
  • 隔离降级:通过线程隔离(仓壁模式)和熔断降级,在服务故障时控制影响范围,避免级联失败。

实际项目中,要根据业务场景选择合适的规则 —— 比如秒杀接口用 warm up + 热点参数限流,慢服务调用用线程池隔离,核心接口用熔断降级。多测试、多调优,才能让微服务更稳定~


网站公告

今日签到

点亮在社区的每一天
去签到