中國武術,門派林立,都是號稱多少代的XXX傳人,結果在面對現代武術時,經常被KO秒殺,為啥,光靠宣傳和口號撐門面,終究是靠不住,必須得有真貨 ,得經得住考驗,所以不能只說Sentinel有多好,也得給出些證據,那么,前文實踐了規則生成和使用,再來看看SentinelDashboard,體驗下是否真如宣傳的那么強大,並配合Nacos做規則統一配置和推送,下面我們就來操作一把,內容較多,分兩部分。
作者原創文章,謝絕一切轉載!
本文只發表在"公眾號"和"博客園",其他均屬復制粘貼!如果覺得排版不清晰,請查看公眾號文章。
工具: Idea201902/JDK11/ZK3.5.5/Gradle5.4.1/RabbitMQ3.7.13/Mysql8.0.11/Lombok0.26/Erlang21.2/postman7.5.0/Redis3.2/RocketMQ4.5.2/Sentinel1.6.3/SpringBoot2.1.6/Nacos1.1.3
難度: 新手--戰士--老兵--大師
目標:
-
使用SentinelDashboard實現規則配置
-
使用Nacos實現規則統一管理、持久化和推送
步驟:
整體框架依舊,多模塊微服務架構商城系統后台,一個共享模塊,多個功能模塊,暫時無前台。
Part One:
用SentinelDashboard實現規則配置
1.先看下SentinelDashboard界面:
左側為接入的應用和機器,並將各類規則獨立管理,工作區之一就是各類監控數據,實現了規則管理的可視化操作和系統監控台。
2.引入sentinel-dubbo-adapter
這個依賴,可以將Dubbo 的服務接口和方法(包括調用端和服務端)自動設置成為 Sentinel 中的資源,
compile group: 'com.alibaba.csp', name: 'sentinel-dubbo-adapter', version: '1.6.3'
3.進入SentinelDashboard的jar包目錄,使用命令運行:
java -Dserver.port=8718 -Dcsp.sentinel.dashboard.server=localhost:8718 -Dproject.name=sentinel-dashboard -Dcsp.sentinel.api.port=8719 -jar sentinel-dashboard.jar
-
Dserver.port=8718 控制台端口,sentinel控制台是一個spring boot程序。
-
Dcsp.sentinel.dashboard.server=localhost:8718 控制台的地址,指定控制台后客戶端會自動向該地址發送心跳包。
-
Dproject.name=sentinel-dashboard 指定Sentinel控制台程序的名稱
-
Dcsp.sentinel.api.port=8719 (默認8719) 客戶端提供給Dashboard訪問或者查看Sentinel的運行訪問的參數
另外:csp.sentinel.dashboard.server這個配置是用在客戶端,這里Sentinel控制台也使用是用於自己監控自己程序的api,否則無法顯示控制台的api情況,當然這個也可以根據情況不顯示。
啟動成功,注意下圖中的log地址,所有規則執行過程都有日志記錄:
4.啟動項目,ZK-->Redis-->RabbitMQ-->RocketMQ-->Nacos-->business-->stock-->logistic-->finance,RabbitMQ我安裝為window服務,設置為自動啟動,當然,其他如Redis也可以照做,只要你硬件夠硬。
5.URL訪問:http://localhost:8718,可以看到business模塊中有dubbo的@Service注解服務都自動識別為resource,這樣代碼中就可以直接使用了,印證前面的第2點,
同時,前文通過代碼設置的流控規則也可見於此:
this.initFlowQpsRule("saveOrder");
優先級問題:
-
如果我這里將saveOrder閥值設置為0,結果會怎樣?測試-->保存訂單成功,說明
代碼內規則優先級高於外部配置
。 -
如果有多個對同一resource的相同類型的規則,經驗證,結論為
按最嚴格規則
執行,下圖中情形將按照閥值為0執行熱點規則:
6.再對saveOrder修改為一個“熱點規則”,即可實時注入到應用內存中,規則立即生效,運行postman測試,就會顯示拒絕結果!
另外:當應用關閉,那么這些規則將全部清除,因心跳檢測會失敗,沒有應用可以使用這些規則,也說明規則並未持久化
。
Part Two:
使用Nacos實現規則統一管理、持久化和推送
1.背景知識:sentinel規則緩存在應用機器內存中,前篇通過API硬編碼規則的方式一般僅用於測試和演示,生產上一般通過動態規則源的方式來管理規則,數據源擴展常見的實現方式有:
拉模式:客戶端主動向某個規則管理中心定期輪詢拉取規則,這個規則中心可以是 RDBMS、文件,甚至是 VCS 等。簡單,缺點是無法及時獲取變更;
推模式:規則中心統一推送,客戶端通過注冊監聽器的方式時刻監聽變化,比如使用 Nacos、Zookeeper 等配置中心。有更好的實時性和一致性保證。
流程為:配置中心控制台/Sentinel 控制台 → 配置中心 → Sentinel 數據源 → Sentinel
Sentinel 目前支持以下數據源擴展:
Pull-based: 文件、Consul (since 1.7.0)
Push-based: ZooKeeper, Redis, Nacos, Apollo
2.這里實現基於Nacos的push模式的動態流控規則,以改造stock模塊為例,添加依賴:
testCompile group: 'com.alibaba.csp', name: 'sentinel-datasource-nacos', version: '1.6.3'
//
compile group: 'com.alibaba.nacos', name: 'nacos-api', version: '1.1.3'
3.因為SentinelDashboard原始版本沒有同步Nacos的邏輯,故需要修改源碼做適配,下載源碼項目(V1.7.0)后,pom文件中,注釋掉scope行,使datasource-nacos生效:
<!-- for Nacos rule publisher sample -->
<dependency>
<groupId>com.alibaba.csp</groupId>
<artifactId>sentinel-datasource-nacos</artifactId>
<!--此行注釋掉<scope>test</scope>-->
</dependency>
4.將\Sentinel\sentinel-dashboard\src\test\java\com\alibaba\csp\sentinel\dashboard\rule\nacos
這個nacos目錄復制到\Sentinel\sentinel-dashboard\src\main\java\com\alibaba\csp\sentinel\dashboard\rule
下,形成如下結構:
5.看下這幾個類: com.alibaba.csp.sentinel.dashboard.rule.nacos.NacosConfigUtil 工具類,實際就是定義了一些常量,略,
public final class NacosConfigUtil { public static final String GROUP_ID = "SENTINEL_GROUP"; public static final String FLOW_DATA_ID_POSTFIX = "-flow-rules"; public static final String PARAM_FLOW_DATA_ID_POSTFIX = "-param-rules"; public static final String CLUSTER_MAP_DATA_ID_POSTFIX = "-cluster-map"; /** * cc for `cluster-client` */ public static final String CLIENT_CONFIG_DATA_ID_POSTFIX = "-cc-config"; /** * cs for `cluster-server` */ public static final String SERVER_TRANSPORT_CONFIG_DATA_ID_POSTFIX = "-cs-transport-config"; public static final String SERVER_FLOW_CONFIG_DATA_ID_POSTFIX = "-cs-flow-config"; public static final String SERVER_NAMESPACE_SET_DATA_ID_POSTFIX = "-cs-namespace-set"; private NacosConfigUtil() {} }
com.alibaba.csp.sentinel.dashboard.rule.nacos.NacosConfig 配置類,注入String和List<FlowRuleEntity>互相轉換的轉換器,這里使用了JDK8語法,返回函數
:
@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("localhost"); } }
com.alibaba.csp.sentinel.dashboard.rule.nacos.FlowRuleNacosProvider 即從Nacos獲得應用的流控規則:
@Component("flowRuleNacosProvider") 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("flowRuleNacosPublisher") 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)); } }
6.修改com.alibaba.csp.sentinel.dashboard.controller.v2.FlowControllerV2,原文:
@Autowired @Qualifier("flowRuleDefaultProvider") private DynamicRuleProvider<List<FlowRuleEntity>> ruleProvider; @Autowired @Qualifier("flowRuleDefaultPublisher") private DynamicRulePublisher<List<FlowRuleEntity>> rulePublisher;
修改要注入的Bean,使用前面復制來的Provider和Publisher,修改為:
@Autowired @Qualifier("flowRuleNacosProvider") private DynamicRuleProvider<List<FlowRuleEntity>> ruleProvider; @Autowired @Qualifier("flowRuleNacosPublisher") private DynamicRulePublisher<List<FlowRuleEntity>> rulePublisher;
7.修改文件: Sentinel\sentinel-dashboard\src\main\webapp\resources\app\scripts\directives\sidebar\sidebar.html 將
<!--<li ui-sref-active="active">-->
<!--<a ui-sref="dashboard.flow({app: entry.app})">-->
<!--<i class="glyphicon glyphicon-filter"></i> 流控規則同步</a>-->
<!--</li>-->
修改為如下,開啟SentinelDashboard左側的菜單:
<li ui-sref-active="active" ng-if="entry.appType==0"> <a ui-sref="dashboard.flow({app: entry.app})"> <i class="glyphicon glyphicon-filter"></i> 流控規則同步</a> </li>
8.重新打包,運行sentinel-dashboard.jar(見第3點),重新生成的jar運行效果:
9.測試:
測試1:在SentinelDashboard【流控規則同步】中新建流控規則,規則會存儲到Nacos;
測試2:直接在Nacos上修改流控規則,然后刷新SentinelDashboard,顯示也會被修改;
測試3:重啟Sentinel控制台,並重啟微服務;刷新控制台,可以發現規則依然存在。
SentinelDashboard中建立一個流控規則:
對應Nacos中的生成的內容:
至此,完成了流控規則的持久化!其他規則持久化類似,略。
10.最后是微服務中同步Nacos,需使用sentinel-datasource-nacos
依賴,然后創建 NacosDataSource 並將其注冊至對應的 RuleManager 上即可: 改造下logistic模塊,先寫一個工具類:com.biao.mall.logistic.util.SentinelRuleUtil 注意這里的變量值是從bootstrap.yml加載的,為啥?因為從application.yml中將無法取值。
public class SentinelRuleUtil { // nacos server ip private static final String remoteAddress = "${spring.cloud.sentinel.datasource.flow.nacos.server-addr}"; // nacos group private static final String groupId = "${spring.cloud.sentinel.datasource.flow.nacos.groupId}"; // nacos dataId private static final String dataId = "${spring.cloud.sentinel.datasource.flow.nacos.dataId}"; // if change to true, should be config NACOS_NAMESPACE_ID private static boolean isDemoNamespace = false; // fill your namespace id,if you want to use namespace. // for example: 0f5c7314-4983-4022-ad5a-347de1d1057d,you can get it on nacos's console private static final String NACOS_NAMESPACE_ID = "0f5c7314-4983-4022"; public static void loadFlowRules(){ // public NacosDataSource(final String serverAddr, final String groupId, final String dataId, // Converter<String, T> parser) ReadableDataSource<String, List<FlowRule>> flowDataSource = new NacosDataSource<>(remoteAddress,groupId,dataId, source -> JSON.parseObject(source, new TypeReference<List<FlowRule>>(){ })); FlowRuleManager.register2Property(flowDataSource.getProperty()); } private static void loadMyNamespaceRules() { Properties properties = new Properties(); properties.put("serverAddr",remoteAddress); properties.put("namespace",NACOS_NAMESPACE_ID); //NacosDataSource(final Properties properties, final String groupId, final String dataId, // Converter<String, T> parser) ReadableDataSource<String, List<FlowRule>> flowRuleDataSource = new NacosDataSource<>(properties,groupId,dataId, source -> JSON.parseObject(source, new TypeReference<List<FlowRule>>() { })); FlowRuleManager.register2Property(flowRuleDataSource.getProperty()); }
11.再修改下com.biao.mall.logistic.controller.DubboDeliveryController 使用注解定義一個資源,然后使用上面的Util加載從Nacos獲取規則,
@SentinelResource(value = "saveOneDelivery") @PostMapping("/delivery/one") public ResEntity<String> saveOneDelivery(@RequestBody String jsonString) throws BlockException { // Method method = this.getClazz().getMethod("saveOneDelivery"); // String resourceName = "saveOneDelivery"; // try(Entry entry = SphU.entry(resourceName)){ JSONObject jsonObject = (JSONObject) JSON.parse(jsonString); String orderId = jsonObject.getString("orderId"); //保存待發一個物流單 deliveryService.saveLogisticSheet(orderId); //響應封裝 ResEntity<String> resEntity = new ResEntity<>(); resEntity.setCode(ResConstant.SUCCESS_CODE); resEntity.setMsg(ResConstant.SUCCESS_STRING); resEntity.setData("delivery received."); return resEntity; }
12.在com.biao.mall.logistic.controller.DubboDeliveryController中使用靜態塊動態加載規則。
@RestController public class DubboDeliveryController { static { //從Nacos動態加載規則,static標簽,可以讓規則只加載一次 SentinelRuleUtil.loadFlowRules(); } ... }
13.測試: 先在SentinelDashboard中建一個流控規則,然后可以看到自動同步到Nacos中,訪問logistic模塊,SentinelDashboard中就可以看到監控數據,貼圖,略。
14.代碼地址:其中的day 14
https://github.com/xiexiaobiao/dubbo-project.git
后記:
1.目前SentinelDashboard可下載官方版本為1.6.3,下載地址:https://github.com/alibaba/Sentinel/releases,1.7.0需要自行編譯打包生成,生成jar包可能遇到的問題,我已在前文(Dubbo學習系列之十(Sentinel之限流與降級))中的后記部分做了些說明。
2.若RocketMQ安裝在Linux虛擬機上,推薦在正常運行時,保存個Snapshot,每次使用,直接恢復到快照,快捷迅速,
3.必須注意:QPS統計的是通過REST調用的請求數量;
4.SentinelDashboard運行后,需要應用先運行產生實際流量,才會開始統計規則監控數據。
推薦閱讀: