在前面的學習過程中,Sentinel 的規則,也就是我們之前定義的限流規則,是通過代碼的方式定義好的。這是初始化時需要做的事情,Sentinel 提供了基於API的方式修改規則:
FlowRuleManager.loadRules(List<FlowRule> rules); // 修改流控規則
DegradeRuleManager.loadRules(List<DegradeRule> rules); // 修改降級規則
SystemRuleManager.loadRules(List<SystemRule> rules); // 修改系統規則
AuthorityRuleManager.loadRules(List<AuthorityRule> rules); // 修改授權規則
當我們接入了控制台后,可以通過控制台進行規則的動態修改,問題是當應用程序重啟后規則信息就會恢復到初始化的階段,也就是說后面修改的值會丟失,因為規則信息都是存儲在應用的內存中。
為了解決這個問題Sentinel 提供了DataSource 擴展的功能,官方推薦通過控制台設置規則后將規則推送到統一的規則中心,客戶端實現 ReadableDataSource 接口端監聽規則中心實時獲取變更,流程如下:

擴展的常見方式有推和拉兩種模式:
- 拉模式:客戶端主動向某個規則管理中心定期輪詢拉取規則,這個規則中心可以是 RDBMS、文件,甚至是 VCS 等。這樣做的方式是簡單,缺點是無法及時獲取變更;
- 推模式:規則中心統一推送,客戶端通過注冊監聽器的方式時刻監聽變化,比如使用 Nacos、Apollo、Zookeeper 等配置中心。這種方式有更好的實時性和一致性保證。
今天我們主要是講如何使用 Apollo 來配置規則進行持久化,Apollo是攜程開源的配置中心,非常好用
Github地址:https://github.com/ctripcorp/apollo
在我的書中也有對Apollo使用的詳細介紹,等出版了再通知大家。
首先集成需要的依賴:
<dependency>
<groupId>com.alibaba.csp</groupId>
<artifactId>sentinel-datasource-apollo</artifactId>
<version>1.4.1</version>
</dependency>
然后創建 ApolloDataSource 並將其注冊至對應的 RuleManager 上即可。比如:
private static void loadRules() {
// Apollo 中的應用名稱,自己定義的
String appId = "SampleApp";
// Apollo 的地址
String apolloMetaServerAddress = "http://localhost:8080";
System.setProperty("app.id", appId);
System.setProperty("apollo.meta", apolloMetaServerAddress);
// 指定環境
System.setProperty("env", "DEV");
// Apollo 的命名空間
String namespaceName = "application";
// 限流規則的Key, 在Apollo中用此Key
String flowRuleKey = "flowRules";
// 限流規則的默認值
String defaultFlowRules = "[]";
// 注冊數據源
ReadableDataSource<String, List<FlowRule>> flowRuleDataSource = new ApolloDataSource<>(namespaceName,
flowRuleKey, defaultFlowRules, source -> JSON.parseObject(source, new TypeReference<List<FlowRule>>() {
}));
FlowRuleManager.register2Property(flowRuleDataSource.getProperty());
}
到此為止配置就結束了,詳細的解釋我都寫了注釋哈。官方文檔也是這么寫的,問題是如果你剛接觸會一頭霧水的,為什么?
你不知道在Apollo中怎么配置啊,我們講的就是說可以用Apollo來作為存儲,持久化規則,那么規則怎么配置就需要我們自己去想。
我也是通過看源碼才知道怎么去配置的,帶着大家一起來看源碼吧!
主要就是new ApolloDataSource這里,參數都是通過這里傳進去的
public ApolloDataSource(String namespaceName, String flowRulesKey, String defaultFlowRuleValue,
Converter<String, T> parser) {
super(parser);
Preconditions.checkArgument(!Strings.isNullOrEmpty(namespaceName), "Namespace name could not be null or empty");
Preconditions.checkArgument(!Strings.isNullOrEmpty(flowRulesKey), "FlowRuleKey could not be null or empty!");
this.flowRulesKey = flowRulesKey;
this.defaultFlowRuleValue = defaultFlowRuleValue;
this.config = ConfigService.getConfig(namespaceName);
initialize();
RecordLog.info(String.format("Initialized rule for namespace: %s, flow rules key: %s",
namespaceName, flowRulesKey));
}
這邊就是對傳入的參數賦值,然后看下面這行:
this.config = ConfigService.getConfig(namespaceName);
這就是通過命名空間去Apollo中獲取配置,獲取完后就執行初始化
private void initialize() {
initializeConfigChangeListener();
loadAndUpdateRules();
}
initializeConfigChangeListener是初始化配置的監聽器,當配置發生修改時會進入該監聽器,也就是說在這個監聽器里需要監聽配置的修改,然后更新規則
private void initializeConfigChangeListener() {
config.addChangeListener(new ConfigChangeListener() {
@Override
public void onChange(ConfigChangeEvent changeEvent) {
ConfigChange change = changeEvent.getChange(flowRulesKey);
//change is never null because the listener will only notify for this key
if (change != null) {
RecordLog.info("[ApolloDataSource] Received config changes: " + change.toString());
}
loadAndUpdateRules();
}
}, Sets.newHashSet(flowRulesKey));
}
loadAndUpdateRules就是更新規則的邏輯了
private void loadAndUpdateRules() {
try {
T newValue = loadConfig();
if (newValue == null) {
RecordLog.warn("[ApolloDataSource] WARN: rule config is null, you may have to check your data source");
}
getProperty().updateValue(newValue);
} catch (Throwable ex) {
RecordLog.warn("[ApolloDataSource] Error when loading rule config", ex);
}
}
那么配置是怎么來的呢,請看loadConfig
@Override
public T loadConfig() throws Exception {
return loadConfig(readSource());
}
public T loadConfig(S conf) throws Exception {
T value = parser.convert(conf);
return value;
}
readSource就是獲取我們配置的flowRulesKey的值,那么配置其實就是一個字符串,然后下面通過Json轉換
public String readSource() throws Exception {
return config.getProperty(flowRulesKey, defaultFlowRuleValue);
}
我們再返過來看看注冊的代碼:
// 注冊數據源
ReadableDataSource<String, List<FlowRule>> flowRuleDataSource = new ApolloDataSource<>(namespaceName,
flowRuleKey, defaultFlowRules, source -> JSON.parseObject(source, new TypeReference<List<FlowRule>>() {
}));
重點是ource -> JSON.parseObject(source, new TypeReference<List
我們配置一個試試看:
flowRules = [{"grade":1,"count":11,"resource":"HelloWorld"}]
點擊保存並且發布,可以在initializeConfigChangeListener里面設置一個斷點,你會發現,當發布配置之后,這邊馬上就會進來,然后執行其他的邏輯,到此為止整個流程結束。
歡迎加入我的知識星球,一起交流技術,免費學習猿天地的課程(http://cxytiandi.com/course)
PS:目前星球中正在星主的帶領下組隊學習Sentinel,等你哦!


