Sentinel 整合Apollo,生产环境搭建指南
第一次接触这个是19年团队微服务化重构的时候,最初版本我们是使用了官方全家桶的Hystrix(但是很遗憾,并没有配置规则,只是匆忙的看了下那头熊..)但是也很快将其替换成了Sentinel。关于其实现原理其实挺庞大也挺复杂的,至于更换的原因(那期Nacos干掉Eureka也说了一些,其实是领导选型的侧重,其实他们都满足我们的“需求”,但是那个时候的QPS,其实从未出现过比较离谱的情况...一直比较低)
而那次的使用其实只是从官网下载dashboard源码,按照官网指示启动了下(甚至有两台机器的高可用方案)。但是其实那个时候Sentinel没有解决我们的问题,也没有在生产中真正帮到我们。在我离职的时候甚至连当初部署他的运维大神都忘记了其的存在...因为真的很没存在感。
好了吐槽就说这么多,今天重新认识这中间件。
背景
目前我们线上的服务器超过百台且大部分机器半数接口QPS稳居20以上,每台机器均值QPS在95上下。而运营方告知接下来几月流量可以能会翻几倍,也就是95会乘以N。面对这种情况一般思路我们肯定要保护核心流程的永远畅通,追求服务的稳定性,以流量为切入点,从流量控制、熔断降级、系统负载保护等多个维度保护服务的稳定性。为啥不选Hystrix(原因和之前一样[因为我领导没换..开玩笑]),其实有考虑到Sentinel相较于Hystrix的优点:
Sentinel | Hystrix | |
---|---|---|
隔离策略 | 基于并发数 | 线程池隔离/信号量隔离 |
熔断降级策略 | 基于响应时间或失败比率 | 基于失败比率 |
实时指标实现 | 滑动窗口 | 滑动窗口(基于 RxJava) |
规则配置 | 支持多种数据源 | 支持多种数据源 |
扩展性 | 多个扩展点 | 插件的形式 |
基于注解的支持 | 支持 | 支持 |
调用链路信息 | 支持同步调用 | 不支持 |
限流 | 基于 QPS / 并发数,支持基于调用关系的限流 | 不支持 |
流量整形 | 支持慢启动、匀速器模式 | 不支持 |
系统负载保护 | 支持 | 不支持 |
实时监控 | API 各式各样 | 较为简单 |
控制台 | 开箱即用,可配置规则、查看秒级监控、机器发现等 | 不完善 |
常见框架的适配 | Spring Cloud、Dubbo、gRPC | Servlet、Spring Cloud Netflix |
说简单一点就是你有的我都有,而我做的都比你好,我也更适合。确定选型之后我们开始接入Sentinel。
sentinel有以下特点:
工程接入
Sentinel 分为两个部分:
- 核心库(Java 客户端)不依赖任何框架/库,能够运行于所有 Java 运行时环境,同时对 Dubbo /Spring Cloud 等框架也有较好的支持。
- 控制台(Dashboard)基于 Spring Boot 开发,打包后可以直接运行,不需要额外的 Tomcat 等应用容器
控制台Dashbord
我们使用开源版本的Sentinel控制台,如果有需要,可以通过切换依赖即可快速接入AHAS Sentinel(商用版本,功能比开源版少点但是持久化和admin都改的挺好看,唯一缺点就是收费,注解支持不是很好)控制台,反之亦可。
控制台包含了以下功能:
- 查看机器列表以及健康情况:收集 Sentinel 客户端发送的心跳包,用于判断机器是否在线。
- 监控 (单机和集群聚合):通过 Sentinel 客户端暴露的监控 API,定期拉取并且聚合应用监控信息,最终可以实现秒级的实时监控。
- 规则管理和推送:统一管理推送规则。
- 鉴权:生产环境中鉴权非常重要。这里每个开发者需要根据自己的实际情况进行定制。
规则中心统一推送,客户端通过注册监听器的方式时刻监听变化,比如使用 Nacos、Zookeeper 等配置中心。这种方式有更好的实时性和一致性保证。
(几百台机器一致性很好保证了,重启也不担心了)
为了持久化我Fork了人的代码,然后稍微改了改:
sentinel本身就在源码中提供了各种数据源持久化的方案,我选择的是Apollo,将
test package com.alibaba.csp.sentinel.dashboard.rule.apollo 下的代码复制到
main中,修改pom(将scope>test<去掉):
<dependency>
<groupId>com.ctrip.framework.apollo</groupId>
<artifactId>apollo-openapi</artifactId>
<version>1.2.0</version>
</dependency>
然后在上述代码填上你目标源的配置,如果你有接入过携程Apollo 的openapi的话这段建议跳过。
然后修改发布和推送的类 FlowControllerV2:
@Autowired
@Qualifier("flowRuleApolloProvider")
private DynamicRuleProvider<List<FlowRuleEntity>> ruleProvider;
@Autowired
@Qualifier("flowRuleApolloPublisher")
private DynamicRulePublisher<List<FlowRuleEntity>> rulePublisher;
将dashBoard 内v1版本的内存推送改为v2版本(由于前端是AngularJS,和java有点像,勉强改几句 ):
sentinel-dashboard\src\main\webapp\resources\app\scripts\controllers\identity.js:
FlowServiceV1 ----> FlowServiceV2
sentinel-dashboard\src\main\webapp\resources\app\scripts\directives\sidebar\sidebar.html:
add++:
<li ui-sref-active="active" ng-if="entry.appType==0">
<a ui-sref="dashboard.flow({app: entry.app})">
<i class="glyphicon glyphicon-filter"></i>流控规则(Apollo)</a>
</li>
然后在根目录:
mvn clean package -Dmaven.test.skip=true
将dashboard的包上传到服务器(可以自动化部署...)
以Springboot Main 的方式启动:
java -server -Xmx2g -Xms2g -Dserver.port=8085 -Dproject.name=sentinel-dashboard -jar sentinel-dashboard-test.jar &
关于启动参数和控制台的使用我觉得官方文档特别靠谱:
https://github.com/alibaba/Sentinel/wiki/
客户端
客户端的接入就很无脑了:
<dependency>
<groupId>com.alibaba.csp</groupId>
<artifactId>sentinel-transport-simple-http</artifactId>
<version>1.5.2</version>
</dependency>
<dependency>
<groupId>com.alibaba.csp</groupId>
<artifactId>sentinel-parameter-flow-control</artifactId>
<version>1.5.2</version>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-sentinel</artifactId>
<version>0.1.2.RELEASE</version>
</dependency>
其中Transport 是为了与控制台连接,而parameter是为了热词限流功能,核心都在spring-cloud-starter-alibaba-sentinel 中。
加入以下配置:
#sentinel
spring.cloud.sentinel.transport.port = 12000
project.name = project-name
csp.sentinel.statistic.max.rt = 4900
feign.sentinel.enabled = true
spring.cloud.sentinel.transport.dashboard = dashboardip:8085
其中 spring.cloud.sentinel.transport.port 是dashboard连接客户端的port,注意别和其他端口冲突
通过feign.sentinel.enabled 打开对feign的支持
通过project.name来指定项目在dashboard的名字
csp.sentinel.statistic.max.rt 最大有效响应时长(ms),超出此值则按照此值记录
启动项目,可以在控制台看到服务列表中,多了客户端的机器,可以对有请求记录的接口进行配置,为了使配置不在加载到内存,使用push模式,集成Apollo:
<dependency>
<groupId>com.alibaba.csp</groupId>
<artifactId>sentinel-datasource-apollo</artifactId>
<version>1.5.2</version>
</dependency>
然后在能被扫描到的service包中添加同步代码:
@Component
public class SentinelApolloKeyRefresh implements InitializingBean {
public static final String FLOW_DATA_ID_POSTFIX = "-flow-rules";
@Value("${project.name}")
private String projectName;
@Override
public void afterPropertiesSet() throws Exception {
// Apollo 的命名空间
String namespaceName = "space.apollo.sentinel.rule";
// 限流规则的Key, 在Apollo中用此Key
String flowRuleKey = String.format("%s%s", projectName, FLOW_DATA_ID_POSTFIX);
// 限流规则的默认值
String defaultFlowRules = "[]";
// 注册数据源
ReadableDataSource<String, List<FlowRule>> flowRuleDataSource = new ApolloDataSource<>(namespaceName,
flowRuleKey, defaultFlowRules, source -> JSON.parseObject(source, new TypeReference<List<FlowRule>>() {
}));
FlowRuleManager.register2Property(flowRuleDataSource.getProperty());
}
}
ApolloDataSource 中集成了监听Apollo配置更新和reload,至此接入结束。
关于生产环境的使用注意事项
- 生产环境可能会在意sentinel产生的日志,日志可以参考官方文档,不光有文件的集成方式,还有api可供调用。
- 上面的方案依赖Apollo,官方提供对Nacos、Zookeeper的支持,找你们稳定的版本做数据源,同步是从数据源同步到内存的。
- 热词、降级没做持久化,也需要配合注解使用。
- 因为有心跳机制,别内网访问不通(我司真就有这种,好好的内网,还有访问不通的情况...)
最后,感谢Github sentinel非常详细的文档:https://github.com/alibaba/Sentinel/wiki