服务挂了咋办?Sentinel熔断降级

29 阅读8分钟

一、先白话白话现实问题

零基础全栈开发Java微服务版本实战-后端-前端-运维-实战企业级三个实战项目

资源获取:关注公众号: 小坏说Java ,获取本文所有示例代码、配置模板及导出工具。

昨天咱学会了用OpenFeign让服务之间说话,得劲是得劲,但是有个问题:

场景模拟

想象一下:

  • order-service(订单服务)调user-service(用户服务)查用户信息
  • 突然user-service挂了(服务器崩了、数据库连不上了)
  • order-service傻不拉几地一直等,等5秒、10秒、30秒...
  • 用户下单页面一直转圈圈,最后报错
  • 更严重的是,订单服务卡住,也影响pay-service(支付服务)

这就叫服务雪崩:一个服务挂了,把其他服务也拖垮,跟雪崩一样,哗啦啦全倒!

二、Sentinel是弄啥嘞?

零基础全栈开发Java微服务版本实战-后端-前端-运维-实战企业级三个实战项目

资源获取:关注公众号: 小坏说Java ,获取本文所有示例代码、配置模板及导出工具。

Sentinel是阿里巴巴出的服务卫士,干三件事:

1. 流量控制(限流)

  • 人太多了,排队!一个一个来
  • 比如:每秒最多处理100个请求,多的拒绝

2. 熔断降级(保险丝)

  • 电路过载了,跳闸保护!
  • 比如:user-service挂了,order-service直接返回默认值,不调了

3. 系统保护(过载保护)

  • 整个系统快撑不住了,保护一下
  • 比如:CPU超过80%,拒绝新请求

今天咱主要学熔断降级,就跟家里的保险丝一样,电流大了就断,保护电器。

三、先把Sentinel装起来

步骤1:下载Sentinel控制台

  1. 去GitHub下载:github.com/alibaba/Sen…
  2. 下载sentinel-dashboard-x.x.x.jar(控制台jar包)
  3. 版本选1.8.x以上

步骤2:启动控制台

# 命令行启动(默认端口8080)
java -Dserver.port=8080 -Dcsp.sentinel.dashboard.server=localhost:8080 -jar sentinel-dashboard-x.x.x.jar

# 可以指定端口(比如用8090端口)
java -Dserver.port=8090 -jar sentinel-dashboard-x.x.x.jar

步骤3:访问控制台

  1. 浏览器打开:http://localhost:8090(如果改端口了就用你改的)
  2. 用户名:sentinel
  3. 密码:sentinel
  4. 看到这个页面就中了:
Sentinel Dashboard
左侧菜单:实时监控 | 簇点链路 | 流控规则 | 降级规则 | ...

四、给order-service加Sentinel保护

步骤1:加依赖

order-servicepom.xml里:

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

<!-- Sentinel整合Feign -->
<dependency>
    <groupId>com.alibaba.cloud</groupId>
    <artifactId>spring-cloud-alibaba-sentinel</artifactId>
</dependency>

<!-- 数据源(用Nacos存规则)可选,今天先不用 -->

步骤2:配置

order-serviceapplication.yml里:

spring:
  cloud:
    sentinel:
      transport:
        dashboard: localhost:8090  # Sentinel控制台地址
        port: 8719  # 本地端口,默认就行
      # 延迟加载,等Feign初始化完再初始化Sentinel
      eager: false
      feign:
        enabled: true  # 开启对Feign的支持
        
# Feign配置Sentinel
feign:
  sentinel:
    enabled: true  # 开启Sentinel对Feign的支持

步骤3:重启order-service

  1. 启动后,刷新Sentinel控制台
  2. 在左侧菜单应该能看到order-service
  3. 暂时还没规则,别急

五、先看看没保护是啥样

1. 正常情况

访问http://localhost:8082/order/1,正常返回用户信息。

2. 模拟user-service挂掉

user-service停掉,再访问http://localhost:8082/order/1

  • 页面转圈圈,等很久
  • 最后报错:Connection refusedRead timed out
  • 用户体验极差!

六、用Sentinel熔断降级

零基础全栈开发Java微服务版本实战-后端-前端-运维-实战企业级三个实战项目

资源获取:关注公众号: 小坏说Java ,获取本文所有示例代码、配置模板及导出工具。

方法1:Feign的Fallback(昨天提过一嘴)

步骤1:创建Fallback类

@Component
public class UserClientFallback implements UserClient {
    
    @Override
    public User getUserById(Integer id) {
        // user-service挂了,返回兜底数据
        User fallbackUser = new User();
        fallbackUser.setId(id);
        fallbackUser.setName("系统繁忙,请稍后再试");
        fallbackUser.setAddress("河南省");
        fallbackUser.setFavoriteFood("胡辣汤");
        return fallbackUser;
    }
    
    @Override
    public String test() {
        return "user-service暂时不可用,请稍后再试";
    }
}

步骤2:Feign客户端指定Fallback

@FeignClient(
    name = "user-service", 
    fallback = UserClientFallback.class  // 指定Fallback类
)
public interface UserClient {
    // ... 接口方法
}

步骤3:测试

  1. 停掉user-service
  2. 访问http://localhost:8082/order/1
  3. 立即返回:
{
    "orderId": 1,
    "orderName": "河南特产大礼包",
    "status": "已发货",
    "address": "河南省郑州市金水区",
    "user": {
        "id": 1,
        "name": "系统繁忙,请稍后再试",
        "address": "河南省", 
        "favoriteFood": "胡辣汤"
    }
}

得劲! 不转圈了,立即返回兜底数据!

方法2:用@SentinelResource注解(更灵活)

零基础全栈开发Java微服务版本实战-后端-前端-运维-实战企业级三个实战项目

资源获取:关注公众号: 小坏说Java ,获取本文所有示例代码、配置模板及导出工具。

步骤1:在Controller里用@SentinelResource

order-serviceOrderController里:

@RestController
@RequestMapping("/order")
public class OrderController {
    
    @Autowired
    private UserClient userClient;
    
    /**
     * 获取订单详情(用Sentinel保护)
     */
    @GetMapping("/detail/{orderId}")
    @SentinelResource(
        value = "getOrderDetail",  // 资源名
        blockHandler = "getOrderDetailBlockHandler",  // 流控/降级处理
        fallback = "getOrderDetailFallback"  // 异常处理
    )
    public Map<String, Object> getOrderDetail(@PathVariable Integer orderId) {
        Map<String, Object> result = new HashMap<>();
        result.put("orderId", orderId);
        result.put("orderName", "河南特产大礼包");
        result.put("status", "已发货");
        
        // 调用user-service
        User user = userClient.getUserById(orderId);
        result.put("user", user);
        
        // 模拟偶尔抛异常
        if (orderId == 999) {
            throw new RuntimeException("模拟异常");
        }
        
        return result;
    }
    
    /**
     * 流控/降级处理(参数要和原方法一致,最后加个BlockException)
     */
    public Map<String, Object> getOrderDetailBlockHandler(
            Integer orderId, BlockException exception) {
        Map<String, Object> result = new HashMap<>();
        result.put("orderId", orderId);
        result.put("orderName", "河南特产大礼包");
        result.put("status", "已发货");
        result.put("user", "系统繁忙,请稍后再试(被限流/降级了)");
        result.put("reason", exception.getClass().getSimpleName());
        return result;
    }
    
    /**
     * 异常处理(参数要和原方法一致,最后加个Throwable)
     */
    public Map<String, Object> getOrderDetailFallback(
            Integer orderId, Throwable throwable) {
        Map<String, Object> result = new HashMap<>();
        result.put("orderId", orderId);
        result.put("orderName", "河南特产大礼包");
        result.put("status", "已发货");
        result.put("user", "系统异常,请稍后再试");
        result.put("error", throwable.getMessage());
        return result;
    }
}

步骤2:在Sentinel控制台配置规则

  1. 访问一次http://localhost:8082/order/detail/1
  2. 刷新Sentinel控制台
  3. 左侧菜单点簇点链路
  4. 找到getOrderDetail(就是你@SentinelResource的value)
  5. 降级规则 -> 新增降级规则

配置熔断降级规则

  • 资源名:getOrderDetail
  • 熔断策略:异常比例
  • 比例阈值:0.5(50%的请求异常就熔断)
  • 熔断时长:5(熔断5秒)
  • 最小请求数:5(至少5个请求才统计)
  • 统计时长:1(统计最近1秒)

意思就是:最近1秒内,如果超过5个请求,且50%的请求都异常了,就熔断5秒,这5秒内所有请求直接走降级逻辑。

步骤3:测试熔断

  1. 让user-service正常运行
  2. 快速访问几次http://localhost:8082/order/detail/999(这个会抛异常)
  3. 触发熔断规则
  4. 再访问http://localhost:8082/order/detail/1(正常的)
  5. 发现也走降级逻辑了(因为熔断了)
  6. 等5秒后再访问,又正常了

七、实际开发中的常用场景

场景1:数据库连接慢

@SentinelResource(
    value = "queryFromDB",
    fallback = "queryFromDBFallback",
    fallbackClass = DBFallback.class  // 降级类
)
public User queryUserFromDB(Integer id) {
    // 查询数据库,可能很慢
    return userDao.findById(id);
}

// 降级:查缓存或返回默认值
public User queryFromDBFallback(Integer id, Throwable t) {
    // 1. 先查缓存
    User user = cache.get("user:" + id);
    if (user != null) return user;
    
    // 2. 返回默认值
    return new User(id, "默认用户", "默认地址");
}

场景2:第三方接口不可用

@SentinelResource(
    value = "callThirdPartyAPI",
    blockHandler = "callThirdPartyAPIBlockHandler"
)
public Result callWeixinAPI() {
    // 调用微信支付,可能失败
    return weixinClient.pay();
}

// 降级:走备用支付渠道
public Result callThirdPartyAPIBlockHandler(BlockException e) {
    return alipayClient.pay();  // 换支付宝
}

场景3:商品详情页

@GetMapping("/product/{id}")
@SentinelResource(
    value = "getProductDetail",
    fallback = "getProductDetailFallback"
)
public ProductVO getProductDetail(@PathVariable Long id) {
    // 1. 基本信息
    Product product = productService.getById(id);
    
    // 2. 库存(可能超时)
    Integer stock = stockService.getStock(id);
    
    // 3. 评价(可能失败)
    List<Comment> comments = commentService.getComments(id);
    
    // 4. 推荐(可能慢)
    List<Product> recommends = recommendService.getRecommends(id);
    
    return assemble(product, stock, comments, recommends);
}

// 降级:只返回基本信息
public ProductVO getProductDetailFallback(Long id, Throwable t) {
    Product product = productService.getById(id);
    return simpleAssemble(product);  // 只组装基本信息
}

八、Sentinel控制台的其他功能

零基础全栈开发Java微服务版本实战-后端-前端-运维-实战企业级三个实战项目

资源获取:关注公众号: 小坏说Java ,获取本文所有示例代码、配置模板及导出工具。

1. 实时监控

  • 看QPS(每秒请求数)
  • 看响应时间
  • 看通过/拒绝的请求数

2. 流控规则(限流)

  • QPS限流:每秒最多多少请求
  • 线程数限流:同时最多多少线程
  • 关联限流:A接口慢了,限制B接口
  • 链路限流:只限制从某个入口来的请求

3. 系统规则

  • 保护整个应用
  • 根据CPU、负载、响应时间等限流

4. 授权规则

  • 白名单/黑名单
  • 根据调用来源限制

九、今儿个总结

学会了啥?

  1. ✅ Sentinel是干啥的(服务卫士)
  2. ✅ 安装Sentinel控制台
  3. ✅ Feign的Fallback(服务降级)
  4. ✅ @SentinelResource注解(熔断降级)
  5. ✅ 在控制台配置规则

关键点

  1. Fallback:快速失败,返回兜底数据
  2. 熔断:失败多了就暂时不调了,等一会儿
  3. 降级:返回简单结果,保证核心功能
  4. 规则配置:可以在控制台动态调整

十、常见问题

1. Sentinel控制台不显示服务

  • 检查配置的dashboard地址对不
  • 检查服务启动时连接Sentinel成功没
  • 访问下服务的接口,有流量才会显示

2. 降级规则不生效

  • 检查@SentinelResource的value和控制台配置的资源名一致不
  • 检查blockHandler/fallback方法签名对不
  • 重启下服务试试

3. 规则丢了

Sentinel控制台的规则默认存在内存里,重启就没了。生产环境要持久化到Nacos或文件。

4. 和Hystrix的区别

  • Hystrix是Netflix的,不更新了
  • Sentinel是阿里巴巴的,功能更多,性能更好
  • 建议用Sentinel

十一、明儿个学啥?

零基础全栈开发Java微服务版本实战-后端-前端-运维-实战企业级三个实战项目

资源获取:关注公众号: 小坏说Java ,获取本文所有示例代码、配置模板及导出工具。

明天咱学API网关

  • 所有请求都从一个门进
  • 统一鉴权、限流、日志
  • Spring Cloud Gateway
  • 跟小区大门一样,保安检查健康码、测体温

明天咱给微服务小区装个智能大门!🚀