1. 概述
在前面的關於Sentinel的使用中,可以發現,Sentinel-dashboard配置的規則,在我們的微服務以及控制台重啟的時候就清空了,因為他是基於內存的.
所以我們有必要將規則配置進行持久化, Sentinel 為我們提供了多種方式,多種選擇:
官方文檔:https://github.com/alibaba/Sentinel/wiki/在生產環境中使用-Sentinel
而最推薦的方式為Push模式,
我們可以將規則配置到注冊中心中,例如ZooKeeper, Nacos, Apollo等等,當我們的客戶端去連接時,將配置信息實時推送到各個客戶端,概念圖如下:
下面以Nacos 為例
2. 以Nacos演示
首先我們觀察一下,沒有注冊Nacos的情況
手動注冊規則,並進行訪問:
@RestController
@RequestMapping("test")
@Slf4j
public class TestController {
@Autowired
private TestService testService;
/**
* 加載此類后執行
*/
@PostConstruct
public void init() {
List<FlowRule> rules = new ArrayList<>();
//新建一個規則
FlowRule rule = new FlowRule();
//保護的資源
rule.setResource("HelloWorld");
//設置為QPS的規則類型
rule.setGrade(RuleConstant.FLOW_GRADE_QPS);
//值為2,則表示每秒只能訪問此資源兩次
rule.setCount(2);
rules.add(rule);
//加載此資源
FlowRuleManager.loadRules(rules);
}
@GetMapping("testB")
@SentinelResource(value = "HelloWorld",blockHandler = "handler" ,blockHandlerClass = CustomerBlockHandler.class)
public void testA() {
System.out.println("業務代碼執行完畢!");
}
}
打印如下
業務代碼執行完畢!
業務代碼執行完畢!
方法被流控
方法被流控
方法被流控
方法被流控
業務代碼執行完畢!
業務代碼執行完畢!
方法被流控
方法被流控
控制台流控規則顯示如下
說明此時本地注冊的流控規則生效
使用Nacos配置:
添加依賴(pom):
<dependency>
<groupId>com.alibaba.csp</groupId>
<artifactId>sentinel-datasource-nacos</artifactId>
</dependency>
添加訪問數據源配置(yaml):
server:
port: 8401
spring:
application:
name: cloudalibaba-sentinel-service
cloud:
sentinel:
transport:
dashboard: 127.0.0.1:8080
port: 8719
datasource:
dsl: # 名稱隨意
nacos:
server-addr: 192.168.100.108:8848 # nacos地址
dataId: cloudalibaba-sentinel-service #nacos配置的dataId
groupId: DEFAULT_GROUP # nacos配置的groupId
data_type: json # 配置文件類型
rule-type: flow #規則類型
management:
endpoints:
web:
exposure:
include: "*"
此時我們再訪問testB
接口,發現此方法已經無法被流控,並且 控制台也沒有顯示此流控規則了, 說明 我們從Nacos中拉取規則時, 將覆蓋本地規則,此時 Nacos 我們還沒有配置,則 此時流控為空
業務代碼執行完畢!
業務代碼執行完畢!
業務代碼執行完畢!
業務代碼執行完畢!
業務代碼執行完畢!
業務代碼執行完畢!
業務代碼執行完畢!
業務代碼執行完畢!
業務代碼執行完畢!
業務代碼執行完畢!
業務代碼執行完畢!
在Nacos中添加流控配置:
配置如下,其中 DataId ,GroupId, 配置類型 ,要和客戶端中配置的一致,此流控規則為 QPS 為1
[
{
"resource":"/retaLimit/byUrl", // 資源名
"limitApp":"default", //來源應用
"grade":1, //閾值類型,0 表示線程數,1表示QPS
"count":1, // 單機閾值
"strategy":0, // 流控模式, 0表示直接,1表示關聯, 2表示鏈路
"controlBehavior":0, //流控效果, 0表示快速失敗,1表示 Warm Up, 2表示排隊等待
"clusterMode":false //是否集群
}
]
此時客戶端不用重啟, 將自動將新增的流控規則推送到此客戶端中:
快速訪問流控接口:
@GetMapping("testD")
@SentinelResource(value = "/retaLimit/byUrl",blockHandler = "handler" ,blockHandlerClass = CustomerBlockHandler.class)
public void testD() {
System.out.println("業務代碼執行完畢!");
}
打印信息如下:
業務代碼執行完畢!
業務代碼執行完畢!
業務代碼執行完畢!
方法被流控
方法被流控
方法被流控
方法被流控
業務代碼執行完畢!
方法被流控
方法被流控
說明成功將流控規則從Nacos中拉取下來,並此時重啟客戶端后,流控規則仍然存在,實現持久化.
3. 改造Sentinel-dashboard
在上面的演示中, 我們發現此時的dashboard,僅僅只是變成查看 客戶端 從Nacos中拉取下來的配置,並沒有連接到Nacos中, 所以當我們從dashboard中添加規則時,並沒有將規則持久化到Nacos中, 當我們重啟客戶端后,dashboard 新增的規則仍然消失,
但是如果我們每次新增規則,都要以這樣新增 JSON的方式,又比較麻煩,所以我們可以將dashboard進行改造,使之新增規則時,同樣將規則持久化到Nacos中
首先,需要將dashboard 的源碼下載下來: https://github.com/alibaba/Sentinel/tree/master/sentinel-dashboard (源碼下載較慢的話,可以私信或評論發你),並打開(本人下載的是1.8)
查看源碼中,在controller包中,有幾個特殊的Controller, 對應着頁面中的各個規則的CRUD, 以FlowControllerV1
為例:
查看此類,有一個非常重要的字段:
@Autowired
private SentinelApiClient sentinelApiClient;
每個Controller 就是通過這個對象, 與Sentinel客戶端進行Http交互, 將從頁面上增加,刪除的操作,通過調用對應的API 來操作對應的客戶端;
例如保存規則的方法中:
private CompletableFuture<Void> publishRules(String app, String ip, Integer port) {
List<FlowRuleEntity> rules = repository.findAllByMachine(MachineInfo.of(app, ip, port));
return sentinelApiClient.setFlowRuleOfMachineAsync(app, ip, port, rules);
}
調用了此對象的setFlowRuleOfMachineAsync
方法,一直點進去看,會發現最終發送了一個Http請求,
executeCommand(new HttpGet(urlBuilder.toString()));
那么其他方法,其他各種規則配置類都是一樣, 這樣一來, dashboard 將直接與客戶端請求HTTP請求, 下面我們進行改造,使之 讀取規則從Nacos中讀取,保存也往Nacos中保存,.
開始改造:
在此源碼中,其實已經為我們准備了關於此方面的示例,在test包中,
已經有了關於關聯Nacos的示例, 其中 FlowRuleNacosProvider
類和FlowRuleNacosPublisher
類分別繼承了DynamicRuleProvider
接口 和 DynamicRulePublisher
接口, 實現了拉取 和推送的方法, 而這兩個類則可以替換FlowControllerV1
類中sentinelApiClient
字段, 將這四個類拷貝到 主包中,創建Nacos文件夾:
注冊類時修Bean的id:
@Component("myFlowRuleNacosProvider")
public class FlowRuleNacosProvider implements DynamicRuleProvider<List<FlowRuleEntity>> {
@Autowired
private ConfigService configService;
@Autowired
private Converter<String, List<FlowRuleEntity>> converter;
@Override
public List<FlowRuleEntity> getRules(String appName) throws Exception {
String rules = configService.getConfig(appName + NacosConfigUtil.FLOW_DATA_ID_POSTFIX,
NacosConfigUtil.GROUP_ID, 3000);
if (StringUtil.isEmpty(rules)) {
return new ArrayList<>();
}
return converter.convert(rules);
}
}
@Component("myFlowRuleNacosPublisher")
public class FlowRuleNacosPublisher implements DynamicRulePublisher<List<FlowRuleEntity>> {
@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;
}
configService.publishConfig(app + NacosConfigUtil.FLOW_DATA_ID_POSTFIX,
NacosConfigUtil.GROUP_ID, converter.convert(rules));
}
}
並修改NacosConfig
類, 將Nacos的地址改成自己的地址
@Configuration
public class NacosConfig {
@Bean
public Converter<List<FlowRuleEntity>, String> flowRuleEntityEncoder() {
return JSON::toJSONString;
}
@Bean
public Converter<String, List<FlowRuleEntity>> flowRuleEntityDecoder() {
return s -> JSON.parseArray(s, FlowRuleEntity.class);
}
@Bean
public ConfigService nacosConfigService() throws Exception {
return ConfigFactory.createConfigService("192.168.100.108");
}
}
並將依賴中,關於nacos持久化的包,test標簽注釋:
<dependency>
<groupId>com.alibaba.csp</groupId>
<artifactId>sentinel-datasource-nacos</artifactId>
<!--<scope>test</scope>-->
</dependency>
此時,我們只需將所有controller包中的sentinelApiClient
,換成 對應的 Nacos對象即可,例如上面的保存流控規則:
@RestController
@RequestMapping(value = "/v1/flow")
public class FlowControllerV1 {
private final Logger logger = LoggerFactory.getLogger(FlowControllerV2.class);
@Autowired
private InMemoryRuleRepositoryAdapter<FlowRuleEntity> repository;
@Autowired
@Qualifier("myFlowRuleNacosProvider")
private DynamicRuleProvider<List<FlowRuleEntity>> ruleProvider;
@Autowired
@Qualifier("myFlowRuleNacosPublisher")
private DynamicRulePublisher<List<FlowRuleEntity>> rulePublisher;
private void publishRules(/*@NonNull*/ String app) throws Exception {
List<FlowRuleEntity> rules = repository.findAllByApp(app);
rulePublisher.publish(app, rules);
}
}
此時,當我們再保存流控規則時,將調用rulePublisher
的 publish
方法, 對Nacos進行保存,
但是不建議修改原生的版本,官方也給我們提供了示例,即v2
包中FlowControllerV2
類,此時的接口為 "/v2/flow"
@RestController
@RequestMapping(value = "/v2/flow")
public class FlowControllerV2
建議將所有的controller拷貝一份v2版本, 下面就着官方的示例,看如何將頁面上的 菜單也切換到v2的版本上
同樣,對於前端, 也提供了Demo, 在resources/app/scripts/directives/sidebar/sidebar.html
中,修改流控規則的路由,
將
<li ui-sref-active="active" ng-if="!entry.isGateway">
<a ui-sref="dashboard.flowV1({app: entry.app})">
<i class="glyphicon glyphicon-filter"></i> 流控規則</a>
</li>
修改為
<a ui-sref="dashboard.flow({app: entry.app})">
路由規則配置在app.js中:
那么此時,當點擊流控規則按鈕時,將跳轉到 flow_v2.html中, 而這個html也是提供好的,調用的接口都是 "v2":
如圖:當我們再次點擊流控規則時,無論是保存還是獲取,都將調用v2的版本,其他頁面也以此類推
同時,對於客戶端,也需要添加指定對應的 dataId 和 GroupId
datasource:
dsl:
nacos:
server-addr: 192.168.100.109:8848
dataId: cloudalibaba-sentinel-service
groupId: DEFAULT_GROUP
data_type: json
rule-type: flow
flow: # 和 改造后的對應
nacos:
server-addr: 192.168.100.109:8848
dataId: ${spring.application.name}-flow-rules
groupId: SENTINEL_GROUP
data_type: json
rule-type: flow
上面的介紹中,僅僅只是以流控規則為例, 即FlowControllerV1
, 同時,對於Nacos, 也就只創建了FlowRuleNacosProvider
和 FlowRuleNacosPublisher
類, 對於其他 的例如降級規則,系統規則等,都需創建 對應的Nacos替換類, 用於替換相應controller的
sentinelApiClient
字段, 同時前端頁面也是一樣.
還有更簡單的方式,是直接使用阿里雲提供的服務,具體使用,請自行百度或者查看官網: