一、先白话白话现实问题
零基础全栈开发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控制台
- 去GitHub下载:github.com/alibaba/Sen…
- 下载
sentinel-dashboard-x.x.x.jar(控制台jar包) - 版本选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:访问控制台
- 浏览器打开:
http://localhost:8090(如果改端口了就用你改的) - 用户名:
sentinel - 密码:
sentinel - 看到这个页面就中了:
Sentinel Dashboard
左侧菜单:实时监控 | 簇点链路 | 流控规则 | 降级规则 | ...
四、给order-service加Sentinel保护
步骤1:加依赖
在order-service的pom.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-service的application.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
- 启动后,刷新Sentinel控制台
- 在左侧菜单应该能看到
order-service - 暂时还没规则,别急
五、先看看没保护是啥样
1. 正常情况
访问http://localhost:8082/order/1,正常返回用户信息。
2. 模拟user-service挂掉
把user-service停掉,再访问http://localhost:8082/order/1:
- 页面转圈圈,等很久
- 最后报错:
Connection refused或Read 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:测试
- 停掉user-service
- 访问
http://localhost:8082/order/1 - 立即返回:
{
"orderId": 1,
"orderName": "河南特产大礼包",
"status": "已发货",
"address": "河南省郑州市金水区",
"user": {
"id": 1,
"name": "系统繁忙,请稍后再试",
"address": "河南省",
"favoriteFood": "胡辣汤"
}
}
得劲! 不转圈了,立即返回兜底数据!
方法2:用@SentinelResource注解(更灵活)
零基础全栈开发Java微服务版本实战-后端-前端-运维-实战企业级三个实战项目
资源获取:关注公众号: 小坏说Java ,获取本文所有示例代码、配置模板及导出工具。
步骤1:在Controller里用@SentinelResource
在order-service的OrderController里:
@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控制台配置规则
- 访问一次
http://localhost:8082/order/detail/1 - 刷新Sentinel控制台
- 左侧菜单点簇点链路
- 找到
getOrderDetail(就是你@SentinelResource的value) - 点降级规则 -> 新增降级规则
配置熔断降级规则:
- 资源名:
getOrderDetail - 熔断策略:异常比例
- 比例阈值:0.5(50%的请求异常就熔断)
- 熔断时长:5(熔断5秒)
- 最小请求数:5(至少5个请求才统计)
- 统计时长:1(统计最近1秒)
意思就是:最近1秒内,如果超过5个请求,且50%的请求都异常了,就熔断5秒,这5秒内所有请求直接走降级逻辑。
步骤3:测试熔断
- 让user-service正常运行
- 快速访问几次
http://localhost:8082/order/detail/999(这个会抛异常) - 触发熔断规则
- 再访问
http://localhost:8082/order/detail/1(正常的) - 发现也走降级逻辑了(因为熔断了)
- 等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. 授权规则
- 白名单/黑名单
- 根据调用来源限制
九、今儿个总结
学会了啥?
- ✅ Sentinel是干啥的(服务卫士)
- ✅ 安装Sentinel控制台
- ✅ Feign的Fallback(服务降级)
- ✅ @SentinelResource注解(熔断降级)
- ✅ 在控制台配置规则
关键点
- Fallback:快速失败,返回兜底数据
- 熔断:失败多了就暂时不调了,等一会儿
- 降级:返回简单结果,保证核心功能
- 规则配置:可以在控制台动态调整
十、常见问题
1. Sentinel控制台不显示服务
- 检查配置的dashboard地址对不
- 检查服务启动时连接Sentinel成功没
- 访问下服务的接口,有流量才会显示
2. 降级规则不生效
- 检查@SentinelResource的value和控制台配置的资源名一致不
- 检查blockHandler/fallback方法签名对不
- 重启下服务试试
3. 规则丢了
Sentinel控制台的规则默认存在内存里,重启就没了。生产环境要持久化到Nacos或文件。
4. 和Hystrix的区别
- Hystrix是Netflix的,不更新了
- Sentinel是阿里巴巴的,功能更多,性能更好
- 建议用Sentinel
十一、明儿个学啥?
零基础全栈开发Java微服务版本实战-后端-前端-运维-实战企业级三个实战项目
资源获取:关注公众号: 小坏说Java ,获取本文所有示例代码、配置模板及导出工具。
明天咱学API网关!
- 所有请求都从一个门进
- 统一鉴权、限流、日志
- 用Spring Cloud Gateway
- 跟小区大门一样,保安检查健康码、测体温
明天咱给微服务小区装个智能大门!🚀