一、高并发下的服务雪崩问题
在微服务架构中,服务间通过网络调用,若单个服务故障且大量请求涌入,会导致任务堆积,最终引发服务雪崩(调用链路中某个服务故障,导致整条链路不可用)。
1.1 模拟高并发场景(基础工程搭建)
1.1.1 服务提供者(sentinel-provider)
application.yml:
server:
port: 9090
spring:
cloud:
nacos:
discovery:
server-addr: 192.168.209.129:8848
application:
name: sentinel-provider
核心服务类(模拟网络延时):
java
package com.hg.service;
import com.hg.pojo.User;
import org.springframework.stereotype.Service;
@Service
public class UserServiceImpl implements UserService {
@Override
public User getUserById(Integer id) {
// 模拟网络延时2秒
try {
Thread.sleep(2000);
} catch (InterruptedException e) {
e.printStackTrace();
}
return new User(id,"王粪堆-provider",18);
}
}
1.1.2 Feign 接口(sentinel_feign)
java
package com.hg.feign;
import com.hg.pojo.User;
import org.springframework.cloud.openfeign.FeignClient;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
@FeignClient("sentinel-provider")
public interface UserFeign {
@RequestMapping(value = "/provider/getUserById/{id}")
public User getUserById(@PathVariable Integer id);
}
1.1.3 服务消费者(sentinel_consumer)
pom.xml 核心依赖:
XML
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
</dependency>
<!-- Feign接口依赖 -->
<dependency>
<groupId>com.hg</groupId>
<artifactId>sentinel_feign</artifactId>
<version>1.0-SNAPSHOT</version>
</dependency>
</dependencies>
application.yml(限制 tomcat 线程数):
XML
server:
tomcat:
max-threads: 10 # 降低tomcat最大并发,模拟资源不足
feign:
client:
config:
default:
connectionTimeout: 5000
readTimeout: 5000
测试接口:
java
@RequestMapping(value = "/hello")
public String hello() {
return "Hello Sentienl!!!";
}
1.1.4 压测验证雪崩雏形
- 下载 JMeter,配置线程组(高并发),调用
http://127.0.0.1:8080/consumer/getUserById/1; - 同时访问
http://127.0.0.1:8080/consumer/hello,发现 hello 接口阻塞等待 ------ 服务雪崩雏形出现。
1.2 Sentinel 核心概念与入门
1.2.1 Sentinel 是什么?
Sentinel(分布式系统的流量防卫兵)是阿里开源的服务保护框架,以流量 为切入点,通过流量控制、熔断降级等维度保障服务稳定性。
1.2.2 核心概念
- 资源:Sentinel 要保护的对象(如接口、方法);
- 规则:保护资源的策略(流量控制、熔断降级等)。
1.2.3 Sentinel 入门实现(两种方式)
方式 1:抛异常方式(原生 API)
添加依赖:
XML
<dependency>
<groupId>com.alibaba.csp</groupId>
<artifactId>sentinel-core</artifactId>
</dependency>
接口改造 + 限流规则配置:
java
@RequestMapping(value = "/hello")
public String hello() {
Entry entry = null;
try {
// 定义资源,Sentinel检查规则
entry = SphU.entry("/consumer/hello");
return "Hello Sentienl!!!";
} catch (BlockException e) {
// 限流兜底逻辑
e.printStackTrace();
return "接口被限流了, exception: " + e;
}finally {
if (entry != null) {
entry.exit(); // 必须与entry成对出现
}
}
}
// 初始化限流规则
@PostConstruct
public void initFlowQpsRule() {
List<FlowRule> rules = new ArrayList<FlowRule>();
FlowRule rule1 = new FlowRule();
rule1.setResource("/consumer/hello"); // 资源名
rule1.setGrade(RuleConstant.FLOW_GRADE_QPS); // 限流维度:QPS
rule1.setCount(2); // QPS阈值2
rules.add(rule1);
FlowRuleManager.loadRules(rules); // 加载规则
}
方式 2:注解方式(低侵入)
添加依赖:
XML
<dependency>
<groupId>com.alibaba.csp</groupId>
<artifactId>sentinel-annotation-aspectj</artifactId>
</dependency>
开启注解支持 + 接口改造:
java
// 启动类/配置类中注册Bean
@Bean
public SentinelResourceAspect sentinelResourceAspect(){
return new SentinelResourceAspect();
}
// 接口层
@RequestMapping(value = "/hello2")
@SentinelResource(value="/consumer/hello2",blockHandler = "blockHandlerMethod")
public String hello2() {
return "Hello Sentienl2!!!";
}
// 限流兜底方法
public String blockHandlerMethod(BlockException e){
return "接口被限流了, exception: " + e;
}
// 规则配置(同方式1,仅修改资源名)
@PostConstruct
public void initFlowQpsRule() {
List<FlowRule> rules = new ArrayList<FlowRule>();
FlowRule rule1 = new FlowRule();
rule1.setResource("/consumer/hello2");
rule1.setGrade(RuleConstant.FLOW_GRADE_QPS);
rule1.setCount(2);
rules.add(rule1);
FlowRuleManager.loadRules(rules);
}
1.2.4 Sentinel 控制台接入
-
下载启动控制台 :下载地址:https://github.com/alibaba/Sentinel/releases启动命令:
java -jar sentinel-dashboard-1.8.1.jar访问地址:http://localhost:8080(默认账号 / 密码:sentinel/sentinel) -
客户端接入控制台:
1)添加依赖:
XML
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-sentinel</artifactId>
</dependency>
2)配置 yml:
XML
spring:
cloud:
sentinel:
transport:
dashboard: 127.0.0.1:8080 # 控制台地址
3)访问任意接口(触发 Sentinel 懒加载),即可在控制台看到监控数据。
二、Sentinel 流控规则全解析
流控规则核心参数说明:
| 参数 | 说明 |
|---|---|
| 资源名 | 唯一名称,默认请求路径 |
| 针对来源 | 限流调用方(微服务名),默认 default(不区分来源) |
| 阈值类型 | QPS(每秒访问次数)/ 线程数(每秒访问线程数) |
| 流控模式 | 直接(限流当前资源)/ 关联(关联资源阈值触发限流)/ 链路(指定接口触发) |
| 流控效果 | 快速失败 / Warm Up / 排队等待 |
2.1 阈值类型
2.1.1 QPS 限流
- 逻辑:当接口 QPS 达到阈值时,直接限流;
- 实战步骤 :
- 控制台新增流控规则(资源名:/consumer/getUserById/{id},阈值类型 QPS,阈值 2);
- JMeter 高并发调用
http://127.0.0.1/consumer/getUserById/1,触发限流。
2.1.2 线程数限流
- 逻辑:当访问接口的线程数达到阈值时,触发限流(适合慢接口);
- 实战步骤 :
- 控制台新增流控规则(阈值类型:线程数,阈值 5);
- JMeter 模拟多线程调用,线程数超过 5 时触发限流。
2.2 流控模式
2.2.1 直接模式
默认模式,直接对当前资源的 QPS / 线程数限流(上文案例均为直接模式)。
2.2.2 关联模式
- 逻辑:关联资源达到阈值时,限流当前资源(适合 "应用让步" 场景,如支付接口阈值触发,限流订单接口);
- 实战步骤 :
- 新增测试接口:
java
@RequestMapping(value="/test")
public String test(){
return "test";
}
-
- 控制台配置流控规则(资源名:/consumer/getUserById/{id},流控模式:关联,关联资源:/consumer/test,阈值 2);
- JMeter 高并发调用
/consumer/test,触发/consumer/getUserById/{id}限流。
2.2.3 链路模式
- 逻辑:指定上级接口触发限流(粒度比 "针对来源" 更细);
- 说明:需配置链路入口,实战中较少用(略)。
2.3 流控效果
2.3.1 快速失败
默认效果,触发限流时直接返回异常(Sentinel 默认提示)。
2.3.2 Warm Up(预热模式)
- 逻辑 :QPS 阈值从
阈值/3开始,经 "预热时长" 逐步升至设定阈值(适合流量突增场景); - 实战步骤 :
- 控制台配置(流控效果:Warm Up,预热时长 5 秒,QPS 阈值 6);
- 前 5 秒访问接口,QPS 超过 2(6/3)即限流;5 秒后阈值升至 6,可正常访问。
2.3.3 排队等待
- 逻辑:请求匀速通过,超出阈值的请求排队等待(超时丢弃),保证服务负载均匀;
- 实战步骤 :
- 控制台配置(流控效果:排队等待,QPS 阈值 1,超时时间 10ms);
- 接口添加时间打印:
java
@RequestMapping(value = "/getUserById/{id}")
public User getUserById(@PathVariable Integer id) {
System.out.println("排队等待效果:"+new Date());
return userFeign.getUserById(id);
}
高并发调用接口,控制台打印日志间隔约 1 秒(匀速处理),超时请求被丢弃。