你好,我是姚秋辰。
在前两节课里,我们已经知道了如何配置Sentinel的降级规则和流量整形规则。不过这套方案还有一个不完美的地方。因为我们配置的这些容错规则并没有被“保存”到某个存储介质中,所以,如果你重新启动Sentinel服务器或者重启应用程序,先前配置的所有规则就都消失不见了。那如何才能解决这个问题呢?
这节课,我将带你对Sentinel的源码做一下二次开发,我们将通过集成Nacos Config来实现一套持久化方案,把Sentinel中设置的限流规则保存到Nacos配置中心。这样一来,当应用服务或Sentinel Dashboard重新启动时,它们就可以自动把Nacos中的限流规则同步到本地,不管怎么重启服务都不会导致规则失效了。
在前两节课的实战环节,我们采取了一种“直连”的方式,将应用程序和Sentinel做了直接集成。在我们引入Nacos Config之后,现有的集成方式会发生些许的变化,我画了一幅图来帮你从架构层面理解新的对接方式。
从上面的图中,你会发现,Sentinel控制台将限流规则同步到了Nacos Config服务器来实现持久化。同时,在应用程序中,我们配置了一个Sentinel Datasource,从Nacos Config服务器获取具体配置信息。
在应用启动阶段,程序会主动从Sentinel Datasource获取限流规则配置。而在运行期,我们也可以在Sentinel控制台动态修改限流规则,应用程序会实时监听配置中心的数据变化,进而获取变更后的数据。
为了将Sentinel与Nacos Config集成,我们需要做两部分改造。
在开始二次开发之前,我们需要将Sentinel的代码下载到本地。你可以从GitHub的Releases页面中找到1.8.2版本,在该版本下的Assets面板中下载Source code源文件。
我们把源码下载到本地并解压之后,你就可以将项目导入到开发工具中了。Sentinel项目下有很多子模块,我们这次主要针对其中的sentinel-dashboard子模块做二次开发。整个改造过程按照先后顺序将分为三个步骤:
首先,你需要打开sentinel-dashboard项目的pom.xml文件,找到其中的依赖项sentinel-datasource-nacos,它是连接Nacos Config所依赖的必要组件。
但这里有一个问题。在Sentinel的源码中,sentinel-datasource-nacos的scope是test,意思是依赖项只在项目编译期的test阶段才会生效。
所以接下来,你需要将这个依赖项的
<dependency>
<groupId>com.alibaba.csp</groupId>
<artifactId>sentinel-datasource-nacos</artifactId>
<!-- 将scope注释掉,改为编译期打包 -->
<!--<scope>test</scope>-->
</dependency>
我们将
依赖项就这么轻松地修改完毕了,接下来我们就可以在后端程序中实现Nacos Config的对接了。
首先,你需要打开sentinel-dashboard项目下的src/test/java目录(注意是test目录而不是main目录),然后定位到com.alibaba.csp.sentinel.dashboard.rule.nacos包。在这个包下面,你会看到4个和Nacos Config有关的类,它们的功能描述如下。
接下来,我们要做两件事,一是在NacosConfig类中配置Nacos连接串,二是在Controller层接入Nacos做限流规则持久化。
我们先从Nacos连接串改起。你需要打开NacosConfig类,找到其中的nacosConfigService方法。这个方法创建了一个ConfigService类,它是Nacos Config定义的通用接口,提供了Nacos配置项的读取和更新功能。FlowRuleNacosProvider和FlowRuleNacosPublisher这两个类都是基于这个ConfigService类实现Nacos数据同步的。我们来看一下改造后的代码。
@Bean
public ConfigService nacosConfigService() throws Exception {
// 将Nacos的注册地址引入进来
Properties properties = new Properties();
properties.setProperty("serverAddr", "localhost:8848");
properties.setProperty("namespace", "dev");
return ConfigFactory.createConfigService(properties);
}
在上面的代码中,我通过自定义的Properties属性构造了一个ConfigService对象,将ConfigService背后的Nacos数据源地址指向了localhost:8848,并指定了命名空间为dev。这里我采用了硬编码的方式,你也可以对上面的实现过程做进一步改造,通过配置文件来注入serverAddr和namespace等属性。
这样,我们就完成了第一件事:在NacosConfig类中配置了Nacos连接串。别忘了还有第二件事,你需要在Controller层接入Nacos来实现限流规则持久化。
接下来,我们就在FlowControllerV2中正式接入Nacos吧。FlowControllerV2对外暴露了REST API,用来创建和修改限流规则。在这个类的源代码中,你需要修改两个变量的Qualifier注解值。你可以参考下面的代码。
@Autowired
// 指向刚才我们从test包中迁移过来的FlowRuleNacosProvider类
@Qualifier("flowRuleNacosProvider")
private DynamicRuleProvider<List<FlowRuleEntity>> ruleProvider;
@Autowired
// 指向刚才我们从test包中迁移过来的FlowRuleNacosPublisher类
@Qualifier("flowRuleNacosPublisher")
private DynamicRulePublisher<List<FlowRuleEntity>> rulePublisher;
在代码中,我通过Qualifier标签将FlowRuleNacosProvider注入到了ruleProvier变量中,又采用同样的方式将FlowRuleNacosPublisher注入到了rulePublisher变量中。FlowRuleNacosProvider和FlowRuleNacosPublisher就是上一步我们刚从test目录Copy到main目录下的两个类。
修改完成之后,FlowControllerV2底层的限流规则改动就会被同步到Nacos服务器了。这个同步工作是由FlowRuleNacosPublisher执行的,它会发送一个POST请求到Nacos服务器来修改配置项。我来带你看一下FlowRuleNacosPublisher类的源码。
@Component("flowRuleNacosPublisher")
public class FlowRuleNacosPublisher implements DynamicRulePublisher<List<FlowRuleEntity>> {
// 底层借助configService与Nacos进行通信
@Autowired
private ConfigService configService;
@Autowired
private Converter<List<FlowRuleEntity>, String> converter;
@Override
public void publish(String app, List<FlowRuleEntity> rules) throws Exception {
AssertUtil.notEmpty(app, "app name cannot be empty");
if (rules == null) {
return;
}
// 发布到Nacos上的配置文件名是:
// app + NacosConfigUtil.FLOW_DATA_ID_POSTFIX
//
// 所属的Nacos group是NacosConfigUtil.GROUP_ID的值
configService.publishConfig(app + NacosConfigUtil.FLOW_DATA_ID_POSTFIX,
NacosConfigUtil.GROUP_ID, converter.convert(rules));
}
}
在上面的代码中,FlowRuleNacosPublisher会在Nacos Config上创建一个用来保存限流规则的配置文件,这个配置文件以“application.name”开头,以“-flow-rules”结尾,而且它所属的Group为“SENTINEL_GROUP”。这里用到的文件命名规则和Group都是通过NacosConfigUtil类中的常量指定的,我把这段代码贴在了下面,你可以参考一下。
public final class NacosConfigUtil {
// 这个是Sentinel注册的配置项所在的分组
public static final String GROUP_ID = "SENTINEL_GROUP";
// 流量整形规则的后缀
public static final String FLOW_DATA_ID_POSTFIX = "-flow-rules";
到这里,我们就完成了对后端程序的改造,将Sentinel限流规则同步到了Nacos。接下来我们需要对前端页面稍加修改,开放一个独立的页面,用来维护那些被同步到Nacos上的限流规则。
首先,我们打开sentinel-dashboard模块下的webapp目录,该目录存放了Sentinel控制台的前端页面资源。我们需要改造的文件是sidebar.html,这个html文件定义了控制台的左侧导航栏。
接下来,我们在导航列表中加入下面这段代码,增加一个导航选项,这个选项指向一个全新的限流页面。
<li ui-sref-active="active">
<a ui-sref="dashboard.flow({app: entry.app})">
<i class="glyphicon glyphicon-filter"></i> 流控规则 极客时间改造</a>
</li>
如果你点击这个新加入的导航选项,就会定向到一个全新的限流页面,你在这个页面上做的所有修改,都会同步到Nacos Config。同时,当Sentinel Dashboard启动的时候,它也会主动从Nacos Config获取上一次配置的限流规则。
到这里,我们的Sentinel二次开发就完成了。接下来,我来带你对微服务模块做一番改造,将微服务程序接入Nacos Config,获取Sentinel限流规则。
微服务端的改造非常简单,我们不需要对代码做任何改动,只需要添加一个新的依赖项,并在配置文件中添加sentinel datasource连接信息就可以了。
在前面的课程中,我们已经将coupon-customer-impl接入了Sentinel控制台,所以这节课我们就继续基于customer服务来做改造吧。
首先,我们需要往coupon-customer-serv的pom文件中添加sentinel-datasource-nacos的依赖项,这个组件用来对接Sentinel和Nacos Config:
<dependency>
<groupId>com.alibaba.csp</groupId>
<artifactId>sentinel-datasource-nacos</artifactId>
</dependency>
然后,我们在application.yml配置文件中找到spring.cloud.sentinel节点,在这个节点下添加一段Nacos数据源的配置。
spring:
cloud:
sentinel:
datasource:
# 数据源的key,可以自由命名
geekbang-flow:
# 指定当前数据源是nacos
nacos:
# 设置Nacos的连接地址、命名空间和Group ID
server-addr: localhost:8848
namespace: dev
groupId: SENTINEL_GROUP
# 设置Nacos中配置文件的命名规则
dataId: ${spring.application.name}-flow-rules
# 必填的重要字段,指定当前规则类型是"限流"
rule-type: flow
在上面的配置项中,有几个重要的点需要强调一下。
首先,你需要整体编译Sentinel源代码,编译完成之后从命令行进入到sentinel-dashboard子模块下的target目录,你会看到一个sentinel-dashboard.jar文件。你可以在命令行执行以下命令,以8080为端口启动Sentinel Dashboard应用:
java -Dserver.port=8080 -Dcsp.sentinel.dashboard.server=localhost:8080 -Dproject.name=sentinel-dashboard -jar sentinel-dashboard.jar
然后,你需要启动coupon-customer-serv服务,并通过postman工具发送一个服务请求,调用requestCoupon服务领取优惠券。
接下来,你可以登录Sentinel Dashboard服务。这时你会看到左侧的导航栏多了一个“流控规则 极客时间改造”的选项。你可以点击这个选项,并手动在当前页面右上方点击“新增流控规则”,为requestCoupon添加一条“QPS=1 快速失败”的流控规则。
最后,打开Nacos Config的配置列表页,你就可以看到一个coupon-customer-serv-flow-rules的配置文件被创建了出来,它的Group是“SENTINEL_GROUP”。
这时,如果我们在Sentinel Dashboard改动这条限流规则,那么改动后的数据会同步到Nacos Config,微服务端将通过监听配置中心的数据变化来实时获取变更后数据。
到这里,我们的限流规则持久化改造就完成了。这里有三个容易踩坑的环节,需要你注意一下。
现在,我们来回顾一下这节课的重点内容。今天我们对Sentinel源代码做了二次改造,将限流规则同步到了Nacos Config。在这个环节里,你需要特别注意“配置一致性”,也就是说控制台中的Nacos连接配置一定要和微服务端保持一致,这是最容易出错的环节。
我们今天的持久化改造主要围绕“限流规则”展开,如果你想继续深入学习Sentinel持久化方案,我建议你结合今天讲的源码二次改造过程,对熔断规则、热点规则等模块做进一步改造,实现多项规则的Nacos数据同步。这个任务相当考验你的源码阅读能力,以及对Sentinel的理解程度,你可以挑战一下。
结合我今天的源码改造过程,请你想一想,如果要改造“熔断规则”,你知道有哪些改动点吗?
好啦,这节课就结束啦。欢迎你把这节课分享给更多对Spring Cloud感兴趣的朋友。我是姚秋辰,我们下节课再见!