Dubbo學習系列之十一(Dashboard+Nacos規則推送)

中國武術,門派林立,都是號稱多少代的XXX傳人,結果在面對現代武術時,常常被KO秒殺,爲啥,光靠宣傳和口號撐門面,終究是靠不住,必須得有真貨 ,得經得住考驗,因此不能只說Sentinel有多好,也得給出些證據,那麼,前文實踐了規則生成和使用,再來看看SentinelDashboard,體驗下是否真如宣傳的那麼強大,並配合Nacos作規則統一配置和推送,下面咱們就來操做一把,內容較多,分兩部分。html

工具: 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.3java

難度:  新手--戰士--老兵--大師git

目標github

  1. 使用SentinelDashboard實現規則配置web

  2. 使用Nacos實現規則統一管理、持久化和推送spring

步驟sql

總體框架依舊,多模塊微服務架構商城系統後臺,一個共享模塊,多個功能模塊,暫時無前臺。json

 

Part Onebootstrap

用SentinelDashboard實現規則配置api

 

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>&nbsp;&nbsp;流控規則同步</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>&nbsp;&nbsp;流控規則同步</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運行後,須要應用先運行產生實際流量,纔會開始統計規則監控數據。

 

推薦閱讀:

相關文章
相關標籤/搜索