Sentinel(二十四)之Sentinel Dashboard中修改规则同步到ZooKeeper
轉載自??Springboot使用Sentinel限流,集成zookeeper完成規則的持久化
上一篇簡單介紹了sentinel限流的基本配置和使用,這一篇我們來稍微深入一點,看看如何將zookeeper繼承進來,用以保存添加的流控規則。
上一篇中我們啟動了dashboard.jar,然后在客戶端中指定了dashboard的地址。之后啟動項目,隨便訪問個接口,之后就能在dashboard的界面上看到相應的請求了,并且能在控制臺上添加一些規則,保存后客戶端就能生效了。
基于內存的推送
那么它的內部原理是什么呢?來簡單了解一下。
從官方文檔可以看到,客戶端在引入了Sentinel后,并指定dashboard的地址,啟動后,將會在客戶端啟動一個http服務,默認占用8719端口。由于我們是引入的SpringCloud的模塊,就已經包含了下面的引入。
引入這個transport模塊的原因就是為了接收dashboard推送過來的配置規則。可以看看官方文檔的介紹,默認就是“原始模式”。所謂的原始模式,就是指客戶端啟動web服務,連上dashboard后,在dashboard配置的規則,由dashboard發起http請求來修改。修改后的規則,直接保存在客戶端內存中,并即時生效。
這種方式原理簡單,一般用于入門測試使用,生產環境不能用。基于內存存儲,在客戶端重啟后,所有規則都會丟失,需要重新配置。而且不適用于客戶端多個實例,因為彼此之間不共享規則,倘若啟動多個實例,需要多次重復配置。很明顯,這不是我們想要的那種結果。
官方提供了三種模式,上面的“原始模式”、“pull模式”、“push模式”。pull模式就是搞個文件存著,隔一會去請求一下,看看有沒有變化,如果變了,就更新到內存,很明顯這種模式存在延遲,也不建議上生產。那就來看看“push模式”吧。
基于zookeeper的推送
從上面可以看到,要想能持久化規則的存儲,并且在多個實例間共享,就需要一個第三方的存儲。讓dashboard對規則的修改能及時存儲到第三方并及時通知客戶端完成修改。官方給了三種示例推薦,Apollo、Nacos、Zookeeper,它們的使用類似,我們以zookeeper為例來看看怎么使用。
客戶端pom文件添加zookeeper的依賴
<dependency><groupId>org.springframework.cloud</groupId><artifactId>spring-cloud-starter-alibaba-sentinel</artifactId></dependency><dependency><groupId>com.alibaba.csp</groupId><artifactId>sentinel-datasource-zookeeper</artifactId></dependency>然后客戶端修改獲取規則的地方為從zookeeper獲取規則。
import com.alibaba.csp.sentinel.datasource.ReadableDataSource;import com.alibaba.csp.sentinel.datasource.zookeeper.ZookeeperDataSource;import com.alibaba.csp.sentinel.slots.block.flow.FlowRule;import com.alibaba.csp.sentinel.slots.block.flow.FlowRuleManager;import com.alibaba.fastjson.JSON;import com.alibaba.fastjson.TypeReference;import org.springframework.beans.factory.annotation.Value;import org.springframework.stereotype.Component;import javax.annotation.PostConstruct;import java.util.List;/*** @author wuweifeng wrote on 2019/7/1.*/@Componentpublic class ZookeeperSentinelConfig {@Value("${spring.application.name}")private String appName;@PostConstructpublic void loadRules() {final String remoteAddress = "127.0.0.1:2181";final String path = "/sentinel_rule_config/" + appName;ReadableDataSource<String, List<FlowRule>> flowRuleDataSource = new ZookeeperDataSource<>(remoteAddress, path,source -> JSON.parseObject(source, new TypeReference<List<FlowRule>>() {}));FlowRuleManager.register2Property(flowRuleDataSource.getProperty());}}這一步很簡單,大家自行去下載個zookeeper的安裝文件,啟動即可。在方法里指定zookeeper的地址和要監聽變化的path,然后注冊一下就好了。客戶端到這里就完畢了。
重新啟動客戶端后,就會變成從zookeeper的固定path里獲取rule規則。之后對該path做的變化,都會即時更新到客戶端,并應用新的規則。
這里我們測試一下:
@RestControllerpublic class TestController {@GetMapping(value = "/hello")public String hello() {return "Hello Sentinel";}@GetMapping(value = "/test")@SentinelResource(value = "TestResource", blockHandler = "handleException")public String test() {return "Hello TestResource";}// Fallback 函數,函數簽名與原函數一致或加一個 Throwable 類型的參數.public String handleException(BlockException ex) {return "handleException";}}上面是一個簡單的Controller,里面定義了一個resource。之后,我們通過對zookeeper的path推送該resource的規則,來測試是否生效。
import org.apache.curator.framework.CuratorFramework;import org.apache.curator.framework.CuratorFrameworkFactory;import org.apache.curator.retry.ExponentialBackoffRetry;import org.apache.zookeeper.CreateMode;import org.apache.zookeeper.data.Stat;/*** @author wuweifeng wrote on 2019/7/1.*/public class ZookeeperConfigSender {private static final int RETRY_TIMES = 3;private static final int SLEEP_TIME = 1000;public static void main(String[] args) throws Exception {final String remoteAddress = "localhost:2181";final String rule = "[\n"+ " {\n"+ " \"resource\": \"TestResource\",\n"+ " \"controlBehavior\": 0,\n"+ " \"count\": 1.0,\n"+ " \"grade\": 1,\n"+ " \"limitApp\": \"default\",\n"+ " \"strategy\": 0\n"+ " }\n"+ "]";CuratorFramework zkClient = CuratorFrameworkFactory.newClient(remoteAddress, new ExponentialBackoffRetry(SLEEP_TIME, RETRY_TIMES));zkClient.start();String appName = "your-app-name";String path = "/sentinel_rule_config/" + appName;Stat stat = zkClient.checkExists().forPath(path);if (stat == null) {zkClient.create().creatingParentContainersIfNeeded().withMode(CreateMode.PERSISTENT).forPath(path, null);}zkClient.setData().forPath(path, rule.getBytes());try {Thread.sleep(3000);} catch (InterruptedException e) {e.printStackTrace();}zkClient.close();}}這就是測試代碼,運行后,就會往zookeeper寫入一個rule,設置名為TestResource的qps為1,并應用到客戶端。無論啟動多少個客戶端實例,都會生效這個rule。
看到這里,其實已經完成了基于push的動態規則功能了,可以通過zkui這種zookeeper界面工具,或者通過代碼來查詢、修改zookeeper里的rule配置(json)來完成對客戶端規則的控制。
那么可能有人會問了,dashboard呢?用那個界面操作不是更方便嗎?
事實上,對客戶端的限流,與dashboard沒一點關系,只用zookeeper就能完成了。那么這時,你再啟動dashboard,當然也是能用的,因為客戶端的web服務還是啟動著的,也能接收到來自于dashboard的推送。只是來自于dashboard的在客戶端重啟后會失效,在zookeeper里的會仍然存在。
那么我們應該改造一下dashboard,讓在界面上的操作也推送到zookeeper里去,這樣就方便多了。
在GitHub上下載Sentinel的源碼,里面有dashboard的工程,我們來修改一下它的代碼就好了。
先修改一下pom文件,把scope注釋掉。
找到rule包,添加個zookeeper文件夾,里面有4個類。
可以直接從工程的test測試代碼里,直接把zookeeper包抄過去就行,并把rule下原來的FlowRuleApiProvider和FlowRuleApiPublisher給注釋掉。
test源碼里已經提供了基于三種中間件的配置代碼了,抄過去就行。
抄過去后,修改一下RULE_ROOT_PATH,保持和客戶端配置的是一致的。
之后找到Controller包下的v2包,如果你設置的FlowRuleZookeeperProvider和publisher兩個bean有名字,可以在autowired時指定為你設置的名字,或者用@Resource。
最后修改一下sidebar.html,將原來的flowV1改為如圖。
這樣就ok了。
重新啟動dashboard項目,重啟客戶端。這樣dashboard就已經和zookeeper關聯起來了,dashboard的操作就由原來的操作客戶端的api,變成了操作zookeeper。你所有在dashboard界面上做的配置,都會存儲到zookeeper中,并實時推送到客戶端。客戶端重啟后,dashboard不受影響。這樣就完成了多實例共享流控規則。
總結
以上是生活随笔為你收集整理的Sentinel(二十四)之Sentinel Dashboard中修改规则同步到ZooKeeper的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 战舰世界电脑配置要求(坦克世界 电脑配置
- 下一篇: Sentinel(二十五)之Sentin