前言
随着微服务的流行,服务和服务之间的稳定性变得越来越重要。Sentinel 是面向分布式、多语言异构化服务架构的流量治理组件,主要以流量为切入点,从流量路由、流量控制、流量整形、熔断降级、系统自适应过载保护、热点流量防护等多个维度来帮助开发者保障微服务的稳定性。
- 源码地址:https://github.com/alibaba/Sentinel
- 官方文档:https://github.com/alibaba/Sentinel/wiki
正文
1. Sentinel特征
Sentinel以流量为切入点,从流量控制、熔断降级、系统负载保护等多个维度保护服务的稳定性。
Sentinel具有以下特征:
- 丰富的应用场景:Sentinel 承接了阿里巴巴近 10 年的双十一大促流量的核心场景,例如秒杀(即突发流量控 制在系统容量可以承受的范围)、消息削峰填谷、实时熔断下游不可用应用等;
- 完备的实时监控: Sentinel 同时提供实时的监控功能。您可以在控制台中看到接入应用的单台机器秒级数据, 甚至 500 台以下规模的集群的汇总运行情况。
- 广泛的开源生态: Sentinel 提供开箱即用的与其它开源框架/库的整合模块,例如与 Spring Cloud、Dubbo、 gRPC 的整合。您只需要引入相应的依赖并进行简单的配置即可快速地接入 Sentinel。
- 完善的 SPI 扩展点: Sentinel 提供简单易用、完善的 SPI 扩展点。您可以通过实现扩展点,快速的定制逻辑。 例如定制规则管理、适配数据源等。
阿里云提供了 企业级的 Sentinel 服务, 应用高可用服务 AHAS。
Sentinel和Hystrix对比:
Sentinel | Hystrix | |
---|---|---|
隔离策略 | 信号量隔离 | 线程池隔离/信号量隔离 |
熔断降级策略 | 基于相应时间或失败比率 | 基于失败比率 |
实时指标实现 | 滑动窗口 | 滑动窗口(基于RxJava) |
规则配置 | 支持多种数据源 | 支持多种数据源 |
扩展性 | 多个扩展点 | 插件的形式 |
基于注解支持 | 支持 | 支持 |
限流 | 基于QPS,支持基于调用关系的限流 | 有限的支持 |
流量整形 | 支持满启动,匀速器模式 | 不支持 |
系统负载保护 | 支持 | 不支持 |
控制台 | 开箱即用,可配置规则,查看秒级监控,机器发现等 | 不支持 |
常见框架的适配 | Servlet,Spring Cloud,Dubbo,gRPC等 | Servlet,Spring Cloud Netflix |
Sentinel和Hystrix都是开源,但是目前Hystrix的Netflix团队已经不在维护了,而Sentinel阿里团队一直在迭代更新,社区也在不断发展。
性能方面,Sentinel比Hystrix更加轻量级,性能也更好一点,并且经过了双十一阿里的超大流量考验。
2. Sentinel快速开始
2.1 API实现
-
引入依赖
<dependency>
<groupId>com.alibaba.csp</groupId>
<artifactId>sentinel ‐core</artifactId>
<version>1.8.0</version>
</dependency>
- 代码实现
在官方文档中,定义的Sentinel进行资源保护需要先定义资源和规则。
@Slf4j
@RestController
@RequestMapping(value = "/sentinel")
public class TestSentinelController {
private static final String RESOURCE_NAME = "hello";
/**
* 编写测试代码,对"hello"进行资源保护
*/
@RequestMapping(value = "/hello")
public String hello() {
Entry entry = null;
try {
// 资源名可使用任意有业务语义的字符串,比如方法名、接口名或其它可唯一标识的字符串。
entry = SphU.entry(RESOURCE_NAME);
// 被保护的业务逻辑
String str = "hello world";
log.info("====="+str);
return str;
} catch (BlockException e1) {
// 资源访问阻止,被限流或被降级
// 进行相应的处理操作
log.info("block!");
} catch (Exception ex) {
// 若需要配置降级规则,需要通过这种方式记录业务异常
Tracer.traceEntry(ex, entry);
} finally {
if (entry != null) {
entry.exit();
}
}
return null;
}
/**
* 定义流控规则
*/
@PostConstruct
private static void initFlowRules(){
List<FlowRule> rules = new ArrayList<>();
FlowRule rule = new FlowRule();
// 设置受保护的资源
rule.setResource(RESOURCE_NAME);
// 设置流控规则 QPS
rule.setGrade(RuleConstant.FLOW_GRADE_QPS);
// 设置受保护的资源阈值
// Set limit QPS to 20.
rule.setCount(1);
// 加载配置好的规则
rules.add(rule);
FlowRuleManager.loadRules(rules);
}
}
- 测试效果
- 缺点
- 业务侵入性很强,需要在controller中写入非业务代码;
配置不灵活 若需要添加新的受保护资源 需要手动添加 init方法来添加流控规则;
2.2 @SentinelResource注解实现
- 引入依赖
<dependency>
<groupId>com.alibaba.csp</groupId>
<artifactId>sentinel‐annotation‐aspectj</artifactId>
<version>1.8.0</version>
</dependency>
- 配置切面支持
@Configuration
public class SentinelAspectConfiguration {
@Bean
public SentinelResourceAspect sentinelResourceAspect() {
return new SentinelResourceAspect();
}
}
- 编写测试逻辑,添加@SentinelResource,配置blockHandler和fallback
@RequestMapping(value = "/findOrderByUserId/{id}")
@SentinelResource(
value = "findOrderByUserId",
fallback = "fallback",fallbackClass = ExceptionUtil.class,
blockHandler = "handleException",blockHandlerClass = ExceptionUtil.class)
public R findOrderByUserId(@PathVariable("id") Integer id) {
// ribbon实现
String url = "http://mall ‐order/order/findOrderByUserId/"+id;
R result = restTemplate.getForObject(url,R.class);
if(id==4){
throw new IllegalArgumentException("非法参数异常");
}
return result;
}
- 编写ExceptionUtil(注意:如果制定了从class,方法必须是static的)
public class ExceptionUtil {
public static R fallback(Integer id,Throwable e){
return R.error( ‐2, "===被异常降级啦===");
}
public static R handleException(Integer id, BlockException e){
return R.error( ‐2, "===被限流啦===");
} }
- 流控规则设置可以通过Sentinel dashboard配置
pom.xml:客户端需要引入 Transport 模块来与 Sentinel 控制台进行通信。
<dependency>
<groupId>com.alibaba.csp</groupId>
<artifactId>sentinel‐transport‐simple‐http</artifactId>
<version>1.8.0</version>
</dependency>
application.yml:客户端需要配置 Sentinel 控制台【IP:端口】进行通信。
‐Dcsp.sentinel.dashboard.server=consoleIp:port
3. 启动 Sentinel 控制台
下载控制台 jar 包并在本地启动:可以参见官网文档https://github.com/alibaba/Sentinel/releases
- jar包的控制台启动命令
java -Dserver.port=8080 -Dcsp.sentinel.dashboard.server=localhost:8080 -Dproject.name=sentinel-dashboard -jar sentinel-dashboard.jar
其中 -Dserver.port=8080
用于指定 Sentinel 控制台端口为 8080
。
从 Sentinel 1.6.0 起,Sentinel 控制台引入基本的登录功能,默认用户名和密码都是 sentinel
。可以参考 鉴权模块文档 配置用户名和密码。
Sentinel 会在客户端首次调用的时候进行初始化,开始向控制台发送心跳包,所以要确保客户端有访问量。
- 控制台地址:http://localhost:8080/
- 查看机器列表以及健康情况
当您在机器列表中看到您的机器,就代表着您已经成功接入控制台;如果没有看到您的机器,请检查配置,并通过 ${user.home}/logs/csp/sentinel-record.log.xxx
日志来排查原因。
- Sentinel控制台
4. Spring Cloud Alibaba整合Sentinel
- 引入依赖
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring‐cloud‐starter‐alibaba‐sentinel</artifactId>
</dependency>
- 添加yml配置,为微服务设置sentinel控制台地址
添加Sentinel后,需要暴露/actuator/sentinel端点,而Springboot默认是没有暴露该端点的,所以需要设置,测试 http://localhost:8800/actuator/sentinel
server:
port: 8800
spring:
application:
name: mall‐user‐sentinel‐demo
cloud:
nacos:
discovery:
server-addr: 127.0.0.1:8848
sentinel:
transport:
# 添加sentinel的控制台地址
dashboard: 127.0.0.1:8080
# 指定应用与Sentinel控制台交互的端口,应用本地会起一个该端口占用的HttpServer
# port: 8719
-
在sentinel控制台中设置流控规则
- 访问 http://localhost:8800/actuator/sentinel,可以查看flowRules
-
微服务和Sentinel Dashboard通信原理
Sentinel控制台与微服务端之间,实现了一套服务发现机制,集成了Sentinel的微服务都会将元数据传递给Sentinel控制 台,架构图如下所示: