❶ 3. nacos服務發現
1. nacos服務發現原理
2. spring cloud服務協作流程
3.搭建nacos服務端
4. 搭建nacos服務發現客戶端
5. nacos服務發現的數據模型
有兩個微服務A和B, A調用B, 那麼A是如何調用B的呢?我們可以通過http請求,進行調用. 也可以使用rpc進行調用.
不管使用什麼方式, A需要知道B的地址和埠號. 那麼A是如何知道B的地址和埠好的呢? 如上圖:
1. B服務啟動的時候, 會注冊到服務發現中心, 告訴他,我的ip和埠號是什麼?這里應該也是介面調用,通知服務發現中心的.
2. A服務啟動的時候, 也會注冊的服務發現中心, 告訴他, 我的ip和埠號是什麼? 同時, 服務發現中心會告訴我, 當前已注冊的服務的ip和埠號. 這里通過一個介面請求和參數返回就可以實現.
3. 拿到了B服務的ip和port, 接下來就可以調用服務B了.
我們要基於spring cloud生態環境進行開發. 所以,先來了解spring cloud的服務寫作流程
前面注冊和發現就都不說了, 上面說過了, 這里多了兩個東西, 一個是Ribbon, 另一個是feign.
Ribbon是負載均衡器, Feign是遠程調用, 自動進行http請求.
客戶端Service A 要調用ServiceB的實例1和實例2. 那麼到底調用ServiceB的哪個實例呢? 使用Ribbon負載均衡, 要看使用什麼樣的演算法了, 可以使用輪詢演算法, 或者其他演算法, 如加權演算法
負載均衡有兩種: 服務端負載均衡, 客戶端負載均衡
在負載均衡中維護一個可用的服務實例清單, 當客戶端請求來臨時, 負載均衡伺服器按照某種配置好的規則(負載均衡演算法), 從可用服務實例清單中, 選取其一去處理客戶端請求, 這就是服務端負載均衡, 例如Nginx. 通過nginx進行負載均衡, 客戶端發送請求值Nginx, nginx通過負載均衡演算法, 在多個伺服器之間選擇一個進行訪問.
接下來, 我們要講的ribbon, 就屬於客戶端負載均衡, 在ribbon客戶端會有一個服務實例地址列表, 在發送請求前, 通過負載均衡演算法, 選擇一個服務實例, 然後進行訪問, 這是客戶端負載均衡. 即在客戶端進行負載均衡演算法分配.
服務的調用方, 我們就可以理解為是一個客戶端.
負載均衡演算法:
可通過下面的方式, 在spring boot配置中修改默認的負載均衡的策略
<pre style="color: rgb(0, 0, 0); font-family: "Courier New"; font-size: 12px; margin: 5px 8px; padding: 5px;">account-service.ribbon.NFLoadBalanceRuleClassName=com.netflix.loadBalancer.RandomRule</pre>
其中: account-service: 是調用的服務的名稱. 後面的組成部分是固定的.
feign是服務端http介面的調用.
feign可以幫助我們更快捷, 優雅的調用httpApi. 原來我們在調用http請求的時候, 需要使用的是RestTemplate, 傳輸域名和埠號, 進行http調用. 使用feign後, 不用再使用RestTemplate模擬請求了, feign能夠通過服務名, 找到對應的介面. 不需要我們在拼接地址+埠了, 提供了簡單方便的操作.
使用方法:
2. 聲明feign客戶端
新建一個類, 聲明為FeignClient類型的介面. 指定調用的服務名. 然後將指定介面. 這樣在調用的時候, 就可以通過服務名, + 介面, 自動找到對應的項目介面了.
這里上一節已經搭建過了(地址: https://www.cnblogs.com/ITPower/articles/12630193.html ), 在服務的最後, 我們搭建了nacos的集群
因為對於服務發現來說, 有很多配置都是公用的, 因此, 我們搭建一個父工程, 將通用的配置都添加到裡面取.
創建一個maven工廠, 引入jar包即可. pom文件如下
在父工程下創建一個子工程. 我們要做一下三件事
指定服務埠號, nacos服務的地址
需要增加兩個引入. 一個啟用服務發現, 另一個是啟用feign
最終項目結構如下:
其中前3步驟和創建服務生產者是一樣的
在父工程下創建一個子工程. 我們要做一下三件事
指定服務埠號, nacos服務的地址
需要增加兩個引入. 1個啟用服務發現, 另一個是啟用feign
nacos生產上已經注冊發現了兩題服務
同時調用介面, 可以獲取到proctor返回的內容.
我們可以通過啟動多個服務的方式, 來測試服務的負載均衡策略.
1. 修改proctor的啟動埠號為動態埠號. 目的是方便啟動的時候動態配置埠號, 啟動集群
** 2. 修改配置, 添加動態埠號**
vm options配置中設置動態埠號為-Dport=56010, 56011. 點擊復制按鈕, 可以增加一個應用, 然後配置參數後, 啟動兩個應用.
3. 在nacos控制台查看啟動效果
我們看到, nacos的proctor 有兩台服務實例. 點擊詳情可查看具體的服務實例信息:
4. 調用consumer的介面, 訪問proctor, 默認採用輪詢的負載均衡演算法
http://localhost:56020/consumer
nacos的注冊發現是一種三層模型: 即 服務--集群--實例.如下圖:
nacos最外層是服務. 最里層是多台實例. 多個實例之間組成一個服務集群.
命名空間不僅適用於配置管理, 同樣適用於服務發現. namespace的常用場景之一是不同環境的配置隔離.如: 開發, 生成, 測試環境資源的隔離.
proctor啟動了兩個實例, 點擊詳情進去可以看到他是一個集群. 集群里有兩台實例.
5.2 服務.
在命名空間下, 有各個服務.比如我們上面定義的是在public命名空間下, 定義了兩個服務. 一個是proctor, 一個是consumer.
服務有 服務名 和 實例. ****遠程調用的時候, 需要指定服務名.
5.3 實例
實例是基於網路協議通訊, 所以必須要有一個ip:埠號
5.4 元信息
在及群里點擊某一個實例-->編輯, 可以看到如下頁面, 可以設置元信息
那麼元信息是什麼呢? 每台伺服器都可能會設置自己的個性化的信息. 這就是每台伺服器的元數據信息
5.5 實操---指定集群的命名空間為dev
只需要在配置中增加命名空間就可以了.
我們也可以修改集群的名字, 集群的默認名字是DEFAULT. 我們這里將其修改為default. 在控制台dev命名空間下, 可以看到啟動了服務customer.
❷ nacos原理
0、服務容器負責啟動,載入,運行服務提供者。
1、服務提供者在啟動時,向注冊中心注冊自己提供的服務。
2、服務消費者在啟動時,向注冊中心訂閱自己所需的服務。
3、注冊中心返回服務提供者地址列表給消費者,如果有變更,注冊中心將基於長連接推送變更數據給消費者。
4、服務消費者,從提供者地址列表中,基於軟負載均衡演算法,選一台提供者進行調用,如果調用失敗,再選另一台調用。
5、服務消費者和提供者,在內存中累計調用次數和調用時間,定時每分鍾發送一次統計數據到監控中心。
Registry 就是注冊中心,負責服務的注冊與發現。Dubbo 有自己的 Registry 實現,而 Nacos 則是另一種 Registry 的實現。
負載均衡有很多種實現方式,包括輪詢法,隨機方法法,對請求ip做hash後取模等等,從負載的維度考慮又分為:服務端負載均衡和客戶端負載均衡。
Nacos 的客戶端在獲取到服務的完整實例列表後,會在客戶端進行負載均衡演算法來獲取一個可用的實例,模式使用的是隨機獲取的方式。
❸ 八(一) nacos 本地多實例(偽集群)
ncaos 參考文檔: https://nacos.io/zh-cn/docs/cluster-mode-quick-start.html
一,ncaos 配置兩個
(1)復制一份ncaos1.4,修改application.properties文件埠為8849
(2)使用 cluster.conf 文件,配置ip:port
(3)分別啟動 http://localhost:8848/nacos/index.html#/login , http://localhost:8849/nacos/index.html#/login
二,使用 第三章 nacosa項目
(1)將config.server-addr 配置為 localhost:8848,localhost:8849
(2)數據源使用同一個地址的庫
(3)本地通過修改port 啟動多個nacosa實例啟動,查看nacos 中心
(4)任意修改配置nacosa.yml 文件,發現兩個實例均同步nacos數據
❹ nacos原理
nacos目前是集成到spring cloud alibaba里去的,也就是在spring cloud的標准之下實現了一些東西,spring cloud自己是有一個介面,叫做ServiceRegistry,也就是服務注冊中心的概念,nacos中有一個它的實現類NacosServiceRegistry,實現了register、deregister、close、setStatus、getStatus之類的方法。
自動裝配是一個spring boot的一個概念,自動裝配的意思,其實就是說系統啟動的時候,自動裝配機制會運行,實現一些系統的初始化,自動運行,也就是系統啟動時自動去調用NacosServiceRegistry的register方法去進行服務注冊。而且除了注冊之外,還會通過schele線程池去提交一個定時調度任務,源碼如下:
this.exeutorService.schele(new BeatReactor.BeatTask(beatInfo), beatInfo.getPeriod(), TimeUnit.MILLISECONDS),這就是一個心跳機制,定時發送心跳給nacos server。
然後會訪問nacos server的open api,其實就是http介面,他有一個介面:http://31.208.59.24:8848/nacos/v1/ns/instance?serviceName=xx&ip=xx&port=xx,這么一個東西,也沒什麼特別的,這里就是訪問注冊介面罷了
nacos server那裡是基於一個ConcurrentHashMap作為注冊表來放服務信息的,直接會構造一個Service放到map里,然後對Service去addInstance添加一個實例,本質裡面就是在維護信息,同時還會建立定時檢查實例心跳的機制。最後還會基於一致性協議,比如說raft協議,去把注冊同步給其他節點。
服務發現的本質其實也是nacos server上的一個http介面,就是:http://31.208.59.24:8848/nacos/v1/ns/instance/list?serviceName=xx,就這么一個介面,然後就會啟動定時任務,每隔10s拉取一次最新的實例列表,然後服務端還會監聽他服務的狀態,有異常就會基於UDP協議反向通知客戶端這次服務異常變動。
❺ Nacos動態配置原理淺談
SpringBoot環境引入配置中心依賴
查看spring-cloud-starter-alibaba-nacos-config的spring.factories文件
首先載入初始化:org.springframework.cloud.alibaba.nacos.的NacosPropertySourceLocator對象
ps:何時載入 請參考: https://blog.csdn.net/m0_37607945/article/details/107762760 )
重點關注NacosPropertySourceLocator.locate方法
那locate方法具體什麼時候執行呢?
spring容器在初始化,准備上下文時,會調用所有實現ApplicationContextInitializer介面的類然後遍歷執行其initialize()方法
而nacos則正是利用了spring的這種自定義PropertySourceLoader載入機制與spring完美結合,說明一個好的框架的擴展性設計是多麼重要,同樣如果從事自研中間件的小夥伴也必須對spring的各種機制,功能點必須非常熟悉才能寫出優秀的框架。
接下來就回到了:NacosPropertySourceLocate的locate方法
利用反射創建NacosConfigService實例
接下來我們看看其構造函數都做了什麼操作
依次初始化命名空間,以及http的包裝類,實際執行的是serverHttpAgent,以及ClientWorker(長輪詢),重點看一下ClientWorker
可以看到ClientWorker裡面初始化了兩個線程池,一個是定時執行任務線程池,一個是不定長線程池,同時啟動了定時任務線程池,設定每10毫秒執行一次:checkConfigInfo()方法。(先記得有這么回事)
繼續看locate 載入方法:
先載入共享配置類文件,即配置:spring.cloud.nacos.config. shared-dataids的文件
再載入配置為:spring.cloud.nacos.config.ext-config 的列表文件,
再去載入系統默認配置
內部方法調用邏輯大同小異,都是調用loadNacosDataIfPresent方法
繼續跟,走到loadNacosData方法
走到NacosConfigService的getConfig方法,getConfig方法會先去查詢本地文件(降級策略),本地文件存在則返回,本地文件不存在則調用http介面獲取,至此,配置中心初始化拉取數據完畢。
我們在nacos控制台修改了數據,客戶端又是如何快速感知到的呢?
入口在:NacosConfigAutoConfiguration的nacosContextRefresher方法
nacosContextRefresher實現了ApplicationListener<ApplicationReadyEvent>,會在spring容器發布ApplicationReadyEvent事件時,觸發監聽操作
針對每個配置文件注冊監聽
首先聲明一個Lister邏輯,然後放到listerMap中,key為dataId,lister內部邏輯主要是收到更新配置後,更新md5值,然後利用spring applicaiton發布RefreshEvent事件。
緊接著調用
configService.addListener(dataId, group, listener);
看下其內部處理邏輯
簡單概括就是將配置信息封裝成了一個cacheData對象,然後放到hashmap中
再次回到上文中的,ClientWorker的定時任務線程池中checkConfigInfo方法,每隔10s會去執行一次,
此處的longintTaskCount 自己理解應該一直是0,因為listenerSize 為配置文件數目不會超過3000,然後ParamUtil.getPerTaskConfigSize()也是固定值為3000,因此longingTaskCount為0,currentLongingTaskCount也為0,也就是if條件會永遠不滿足,但debug發現longingTaskCount會變為1,但是不知道為啥(希望大神解惑)
繼續看LongPollingRunnable的run方法
如果沒有用到本地配置,並且本地配置文件確實存在,則採用本地配置
如果是採用的本地配置。並且本地文件刪除了 ,則設置setUseLocalConfigInfo(false)
檢查md5值是否有變更,如有通知發送監聽
執行lister的receiveConfigInfo()方法
總結:客戶端通過定時任務線程池來監聽配置,當服務端配置發生變更時,客戶端會通過拉(長輪詢)方式得到最新的值並保存在cacheData中,然後於cacheData的listener的md5值做對比,如果有更新則通知,觸發lister的reveiveConfig方法;
來看下服務端的長輪詢處理:
發起長輪詢請求,對應http介面:post請求,/v1/cs/configs/listener,並設置超時時間30s,邏輯是如果30s內配置發生了變更,則會立馬返回,否則等待29s後執行檢查判斷配置是否發生變更返回。然後繼續發起輪詢請求,循環往復
服務端長輪詢介面處理邏輯:
將請求設為非同步,並封裝成ClientLongPolling
ClientLongPolling 的run邏輯:
1.創建一個調度的任務,調度的延時時間為 29.5s,(29.5由客戶端默認傳遞超時時間30s-服務端設置的500ms得來)
2.將該 ClientLongPolling 自身的實例添加到一個 allSubs 中去
3.延時時間到了之後,首先將該 ClientLongPolling 自身的實例從 allSubs 中移除
4.獲取服務端中保存的對應客戶端請求的 groupKeys 是否發生變更,將結果寫入 response 返回給客戶端
allSubs則必然和客戶端配置變更有必然聯系,查看服務端修改配置方法:post /v1/cs/configs/
先持久化,再去發布configDataChangeEvent事件
而我們的LongPollService 監聽的則是LocalDataChangeEvent事件,似乎和ConfigDataChangeEvent沒關系,其實不然
繼續跟進ConfigController的ConfigChangePublisher
.notifyConfigChange(new ConfigDataChangeEvent(....)))方法
AsyncNotifyService中注冊監聽邏輯
會執行一個AsyncTask任務,從而觸發一個http get介面:
也就是:
mpService是負責將配置保存到磁碟的服務類
看到確實發布了LocalDataChangeEvent事件,
然後又回到了上圖:LongPollingService 的onEvent方法,接著看DataChangeTask的邏輯,
首先遍歷allStubs隊列,然後找出當前的ClientLongPolling,
從隊列中移除,然後response寫入變更的groupKey
總結:可以看到nacos實際上是利用了推+拉 結合的方式來獲取配置,當沒有配置發生變更時,會hang住請求,默認等待(30-0.5)29.5秒後返回,而一旦發生數據變更,又會立刻推送變更數據寫入到response,然後客戶端更新配置;
以上則是動態配置原理,如果有不對的地方請指出;
參考: https://www.jianshu.com/p/acb9b1093a54
❻ 聊聊nacos的LocalConfigInfoProcessor
本文主要研究一下nacos的LocalConfigInfoProcessor
nacos-1.1.3/client/src/main/java/com/alibaba/nacos/client/config/impl/LocalConfigInfoProcessor.java
nacos-1.1.3/client/src/main/java/com/alibaba/nacos/client/config/impl/CacheData.java
LocalConfigInfoProcessor的getFailover方法首先會通過getFailoverFile獲取本地配置文件,然後通過readFile讀取;getSnapshot方法首先通過getSnapshotFile獲取snapshot文件,然後通過readFile讀取;saveSnapshot方法會存儲新的config;cleanAllSnapshot方法會清除snapshot目錄下所有緩存文件
❼ nacos和eureka的區別是什麼
nacos和eureka的區別區別如下:
springcloud eureka是注冊中心,負責微服務的注冊與發現,起到承上啟下的作用,在微服務架構中相當於人體的 大腦,很重要,nacos是阿里巴巴出的,功能類似eureka。
nacos的部署方式與springcloud eureka不太一樣,euraka是需要創建springboot項目,然後將euraka服務端通過gav的方式載入進來,然後部署項目。nacos是直接從阿里巴巴nacos的官網下載jar包,啟動服務。
Eureka Server:
之間通過復制的方式完成數據的同步,Eureka還提供了客戶端緩存機制,即使所有的Eureka Server都掛掉,客戶端依然可以利用緩存中的信息消費其他服務的API。綜上,Eureka通過心跳檢查、客戶端緩存等機制,確保了系統的高可用性、靈活性和可伸縮性。
❽ Nacos服務發現
Nacos 另一個非常重要的特性就是服務注冊與發現,說到服務的注冊與發現相信大家應該都不陌生,它們是服務治理的最基礎功能。
Nacos 支持幾乎所有主流類型的 「服務」 的發現、配置和管理。
了解過 Dubbo 的同學,應該對 Dubbo 的架構非常熟悉,最經典的一張架構圖如下所示:
圖中的6個步驟的含義解釋如下:
0、服務容器負責啟動,載入,運行服務提供者。
1、服務提供者在啟動時,向注冊中心注冊自己提供的服務。
2、服務消費者在啟動時,向注冊中心訂閱自己所需的服務。
3、注冊中心返回服務提供者地址列表給消費者,如果有變更,注冊中心將基於長連接推送變更數據給消費者。
4、服務消費者,從提供者地址列表中,基於軟負載均衡演算法,選一台提供者進行調用,如果調用失敗,再選另一台調用。
5、服務消費者和提供者,在內存中累計調用次數和調用時間,定時每分鍾發送一次統計數據到監控中心。
其中圖中最上方的 Registry 就是注冊中心,負責服務的注冊與發現。Dubbo 有自己的 Registry 實現,而 Nacos 則是另一種 Registry 實現。
相對服務注冊而言服務發現就簡單很多了。就是Nacos客戶端調用Open API或者SDK查詢服務列表,服務端接受到請求後根據將查詢到服務包裝成json格式返回。
既然如此,客戶端是啥時候發起服務列表查詢?
如果客戶端查的時差內,剛好有服務實例有down掉,那客戶端的請求豈不是有請求到剛好down的服務實例?下面進行解答。
客戶端有一個HostReactor類,在com.alibaba.nacos.client.naming.core包下。
HostReactor它裡面有一個UpdateTask線程,每 1s 發送一次pull拉取請求,獲取服務最新的地址列表。
更新服務的核心邏輯在updateService方法中:
再看看processServiceJson方法, 本地維護一個Map<String,ServiceInfo> serviceInfoMap 存儲服務信息,同時調用 DiskCache.write(serviceInfo, this.cacheDir) 方法把服務信息寫入本地緩存文件中;
服務端採取的是基於push的方式向客戶端通知,由於服務端和服務提供者(各個微服務provider)建立了心跳機制,一旦某個服務出現故障,服務端察覺出後,會發送一個push消息給Nacos客戶端,也就是我們的消費者。這個push消息是使用DatagramSocket來實現的。
服務消費者收到服務端發來的push消息之後,使用HostReactor中提供的ServiceInfo processServiceJson(String json)方法解析消息,並更新本地服務地址列表。
可以參照下面的圖更容易理解服務動態感知原理,包括:客戶端主動輪訓查詢服務列表及服務端Push變故後的服務列表。
❾ 軟體更新丨Spring Cloud Alibaba發布第二個版本,Spring發來賀電
還是熟悉的面孔,還是熟悉的味道,不同的是,這次的 配方升級 了。
時隔 51天,Spencer Gibb再次在Spring官網的博客頁面宣布:Spring Cloud Alibaba發布了其開源後的 第二個版本0.2.1 ,隨後,Spring Cloud 官方Twitter也轉發了此消息。聖誕節的前一周,Josh Long向他的老朋友許曉斌發來賀電:
視頻地址:https://spring.io/blog/2018/12/26/spring-tips-bootiful-alibaba
一、新版本概要
Spring Cloud Alibaba RocketMQ
Spring Cloud Alibaba SchelerX
Spring Cloud Alibaba Nacos Config
Spring Cloud Alibaba Nacos Discovery
Spring Cloud Alibaba Sentinel
二、新版本背後的思考
Spring Cloud Alibaba Nacos Discovery
Nacos Discovery 在這個版本最大的更新就是支持在初始化的時候不使用本地文件緩存,目前初始化的時候已經默認不使用本地文件緩存。
為什麼要有緩存?首先我們來了解一下本地緩存的概念,為什麼需要這個本地緩存?
我們都知道,服務注冊與發現應該只是服務調用中的輔助性的一個環節,而不是一個關鍵的環節。一個良好的服務注冊與發現的設計,需要保證以下兩點。
要實現以上兩點,緩存就不可或缺,而為了適應不同的場景,緩存又可以分成內存緩存和本地文件緩存,他們的概念和適用場景如下。
內存中的緩存
將服務提供者列表維護在內存中,每次調用時直接從內存中的列表獲取節點即可。內存緩存通過定時任務更新,或者在收到服務注冊中心的推送之後再更新。確保了即使在服務注冊中心宕機的情況下,也能保證服務仍能正常調用。
本地文件緩存
將上述提到的內存中的緩存,保留在本地的某個文件中,這樣在調用服務注冊中心失敗的時候,可以從本機的文件緩存中獲取服務提供者列表。這樣就保證了在服務注冊中心宕機的情況下,應用在重啟後也能找到服務提供者。
為什麼要關閉
有了以上背景知識後,讀者可能會有疑問,既然緩存這么好,你們為什麼默認要把它置為默認關閉呢?
我們在發布出第一個版本之後,很多用戶反饋,為什麼我服務下線之後還是有節點,仍舊可以被查詢到呢?這樣導致我這個監控數據完全不準,你們這個有 bug,完全不對。其實這是阿里巴巴在多年業務積累的經驗,對於服務發現來說,一個即使是已經過時的節點,也比沒有任何數據好。而且還有可能其實這個服務只是和服務注冊中心失去了心跳,但是應用本身是正常的。
當然,這也暴露了我們設計中存在的一些問題,沒有把服務發現本身,以及緩存的分層給做好,兩者糅合在一塊。所以在這一次的版本更新中我們還是選擇適配開源標准,默認關閉了本地文件緩存,留了一個開關給用戶自由選擇。
Spring Cloud Alibaba Nacos Config
Nacos Config 在這個版本中有兩個大的特性,支持了「共享配置」,修正了動態刷新的語義和行為。
共享配置的實現
在第一個版本還沒發布到時候,社區里對配置管理中心的討論就沒停止過,其中聽到最多的反饋應該就是支持多個應用共享一個配置。我們也通過 github issue 的方式,徵集了很多意見,詳情見 #12 , #141。
後來我們將這個模型抽象了一下,認清這些需求本質是一個應用可以從多個 DataID 和 GroupID 組合中獲取配置,並且這些配置還可以單獨指定優先順序和是否動態刷新。
最後我們推薦了這種使用方式,既保證了使用場景的靈活性,又保證了業務語義的明確性。更多詳情可以參考 WIKI。
注意 data-id 的值必須帶文件擴展名,文件擴展名支持 properties、yaml 和 yml。通過這種自定義擴展的配置項,既可以支持一個應用從多個配置項中獲取數據,也解決多個應用間配置共享的問題。
頭腦風暴,@fudali 同學還提出了更加靈活的一種方式 #161,就是可以通過一個配置項來配置所有的 DataID 的信息,然後可以通過修改這個配置項,可以修改整體配置項的邏輯。
這是一個非常好的想法,只不過這一期中我們沒有實現,原因是這種方式太靈活了。我們認為配置管理其實是一件很嚴肅的事情,太靈活導致生產中變更比較不可控。
雖然目前的邏輯也可以支持這種用法,但是我們並沒有把這種模式當做推薦模式,後續如果社區有更多的反饋認為這是一個強烈的需求,歡迎提 PR。
動態刷新的修正
簡單好用、實時可監控的動態刷新也許是 Nacos Config 相對於其他開源中間件相比最核心的優勢了,不同於 Spring Cloud Config Server 必須使用 Spring Cloud Bus 才能實現動態刷新,Nacos Config 無需依賴其他任何中間件就可以實現實時動態刷新,而且推送成功與否和刷新 歷史 還支持實時查詢。
但是我們發現在第一個版本中的實現發現兩個問題:
在這個版本中,我們修復了這兩個問題。
首先,動態刷新不再是直接去調用 ContextRefresher.refresh() 方法,而是 publish 了一個 RefreshEvent,讓 spring-cloud-commons 里的 RefreshEventListener 去觸發這個 ContextRefresher.refresh() 方法。
其次,我們修正了動態刷新的語義後,這一次是真正做到了,只有 refresh 屬性為 true 的配置項,才會在運行的過程中變更為新的值,refresh 屬性為 false 的配置項再也不用擔心應用在運行的過程中發生莫名其妙的變更了。
更深入一點,在上個月 SpringOne Tour 中,我們和 Spring Cloud 的創始人 Spencer 聊到 Spring Cloud 的 Context.refresh() 成本太高,會刷新整個 Spring Context。他反復強調了兩次 Context.refresh() 只對 @RefreshScope 和 @ConfigurationProperties 有效,成本一點也不高。
之前我們接收到很多社區的反饋都是 Nacos Config 動態刷新支不支持 xxxx,支不支持 xxxx。之前我們都是回答只支持 @RefreshScope 和 @ConfigurationProperties ,如果他內置沒有支持,那就得自己加上相應的註解。
今天我們可以很愉快地回復,他監聽了 RefreshEvent 也能直接支持。而且如果添加 @RefreshScope 和 @ConfigurationProperties 都不滿足你的需求時,可以通過實現自己的 RefreshEventListener 更多高級的玩法。
Spring Cloud Alibaba Sentinel
Sentinel 在這個版本中有三個大的特性:全面支持 FeignClient ,完善了 RestTemplate 的支持,添加了熱點限流、集群限流。
FeignClient 集成 Sentinel
其實在這之前,Sentinel 支持 FeignClient 已經設計了很久了,之前我們的想法做一個兼容性較強的方案,支持 Sentinel 和 Hystrix 在 FeignClient 中同時使用,盡量做到對原有應用的侵入性做到最小。
這個方案我們也思考調研了很久,但是實現難度確實比較大,需要修改 FeignClient 的代碼才能實現兩者共存。正好前段時間在 Spring Cloud 屆最大的新聞就是 Hystrix 宣布不在維護了,於是我們就換了一個思路,直接使用 Sentinel 替代 Hystrix,不再去追求支持兩者共存。
我們實現了自己的 Feign.Builder,在構建的 FeignClient 執行調用的過程中,通過 SentinelInvocationHandler 完成 Sentinel 的流量統計和保護的動作。如果想使用 Sentinel 為 FeignClient 限流降級,首先需要引入 sentinel-starter 的依賴,然後打開 Sentinel 限流降級的開關 feign.sentinel.enabled=true ,就完成了 Sentinel 的接入。如果需要使用更加定製化的功能,則需要在 @FeignClient 添加 fallback 和 configuration 這些屬性的配置。
注意 @FeignClient 註解中的所有屬性,Sentinel 都做了兼容。
RestTemplate 集成 Sentinel
Spring Cloud Alibaba Sentinel 支持對 RestTemplate 的服務調用使用 Sentinel 進行保護,補全了 Hystrix 這一塊的空白。接入的方式也不復雜,在構造 RestTemplate bean 的時候需要加上 @SentinelRestTemplate 註解,然後在控制台配置相應的規則即可。
在觸發了限流降級時,默認的處理方式是返回 RestTemplate request block by sentinel 信息。
RestTemplate 的限流降級 ?Sentinel 也承包了!
熱點參數限流和集群限流
首先解釋一下什麼是熱點參數限流和集群限流。
熱點參數限流會統計傳入參數中的熱點參數,並根據配置的限流閾值與模式,對包含熱點參數的資源調用進行限流。熱點參數限流可以看做是一種特殊的流量控制,僅對包含熱點參數的資源調用生效。
集群流控主要解決的問題是:當我們需要控制整個集群流量總量,但是單機流量可能會不均勻,如果是單機維度去限制的話會無法精確地限制總體流量,因此需要引入集群維度的流量控制。
Sentinel v1.4.0 的 新功能 ,也能第一時間愉快地在 Spring Cloud Alibaba 上使用了。
三、新組件
Spring Cloud Alibaba RocketMQ
Spring Cloud Stream 是一個用於構建基於消息的微服務應用框架,它基於 SpringBoot 來創建具有生產級別的單機 Spring 應用,並且使用 Spring Integration 與 Broker 進行連接。它提供了消息中間件的統一抽象,推出了 publish-subscribe、consumer groups、partition 這些統一的概念。
RocketMQ 是一款開源的分布式消息系統,基於高可用分布式集群技術,提供低延時的、高可靠的消息發布與訂閱服務。具有以下特點:能夠保證嚴格的消息順序、提供豐富的消息拉取模式、高效的訂閱者水平擴展能力、實時的消息訂閱機制、億級消息堆積能力。
在這次的 Spring Cloud Stream Binder RocketMQ 的實現中,我們適配了 Spring Cloud Stream 對於 message 抽象的 API,支持了 RocketMQ 的事務消息。消息訂閱時支持以 tags、SQL 表達式過濾消息,同時還支持順序、並發、延遲以及廣播消費模式。
Spring Cloud Alibaba SchelerX
SchelerX 是阿里中間件團隊開發的一款分布式任務調度產品,提供秒級、精準、高可靠、高可用的定時(基於 Cron 表達式)任務調度服務,同時提供分布式的任務執行模型,如網格任務,網格任務支持海量子任務均勻分配到所有 Worker(schelerx-client)上執行。
簡單易用的輕量分布式任務調度
您不需要關心調度邏輯,只需要在在 JobProcessor 介面的實現中添加業務邏輯即可,然後在自主運維控制台配置上一個 Job 即可完成使用。
高可用的分布式任務
不管是 SchelerX 服務端還是客戶端都是分布式架構設計,任務可以在多台客戶端機器里的任何一台機器執行,如果客戶端出現宕機的情況,服務端會自動選擇正常運行的客戶端去執行 Job,每個 Job 在服務端的不同機器均有備份,SchelerX 服務端任意宕掉部分機器仍能保證 Job 正常調度。
友好的用戶界面
SchelerX 提供了非常友好的頁面方便您創建、刪除或修改 Job,提供了立即觸發執行一次的功能,方便您測試以及關鍵時刻手動立即執行一次,還提供了 歷史 執行記錄查詢的功能,您可以看到任何一個 Job 過去 100 次的 歷史 執行記錄。
功能強大
提供了秒級、精準的定時任務調度服務,且提供了豐富的任務執行模型,包括單機執行,廣播執行,以及子任務的分布式執行。
四、What's Next?
Spring Cloud Alibaba Cloud SLS 針對日誌類數據的一站式服務,在阿⾥巴巴集團經歷大量大數據場景錘煉⽽成。您⽆需開發就能快捷地完成日誌數據採集、消費、投遞以及查詢分析等功能,提升運維、運營效率,建立 DT 時代海量日誌處理能力。
Spring Cloud Alibaba Dubbo Dubbo 是一款流行的開源 RPC 框架,我們會把 Dubbo 整合到 Spring Cloud Alibaba 中,讓大家在開發 Dubbo 時也能享受到 Spring Cloud 帶來的便利。
致謝
Spring Cloud Alibaba 從開源建設以來,受到了很多社區同學的關注。社區的每一個 issue ,每一個 PR,都是對整個項目的幫助,都在為建設更好用的 Spring Cloud 添磚加瓦。
↓↓↓
❿ Nacos Config Spring Cloud
這里分析下Nacos Config在Spring Cloud下的使用。整體調用鏈路如下表所示。
載入了spring.cloud.nacos相關配置,生成NacosConfigProperties。
初始化了NacosConfigManager,主要是根據NacosConfigProperties初始化了ConfigService,這個也是Nacos Client 對外保留的核心對象,用於獲取配置,添加監聽等。
用於初始化了NacosPropertySourceLocator對象,實現了PropertySourceLocator介面,用於載入額外的配置信息。
載入分布式的配置,形如spring.cloud.nacos.config.shared-configs[0]=xxx 的配置。
載入擴展配置,如spring.cloud.nacos.config.extension-configs[0]=xxx ,其實我沒搞明白這倆有啥區別。
載入應用主配置信息,這里會載入默認的兩個配置,dataIdPrefix+group與dataIdPrefix.fileExtension+group,如果不想載入可以通過配置spring.cloud.nacos.config.group=來避免載入,只載入上面的shared-configs與extension-configs的配置。
這里會判斷是否有父Context並且包含了NacosConfigProperties,如果有從父Context中直接獲取。
初始化了NacosRefreshHistory,用於存儲Nacos數據更新歷史,最大存儲20條。
初始化了NacosConfigManager,與上面的一致。
初始化了NacosContextRefresher,這個是比較核心的類,這個類實現了ApplicationListener,監聽ApplicationReadyEvent事件,在Application完成的時候,執行Nacos變化監聽。
() --> registerNacosListener() --> 這里會新建一個Listener,並通過configService#addListener()方法添加到Nacos的變化監聽中。當收到變化的時候,首先添加了刷新紀錄nacosRefreshHistory#addRefreshRecord(),其次發布了RefreshEvent事件。
在發布了RefreshEvent事件之後,就可以使用 Spring Cloud自帶的RefreshScope機制 來實現屬性的刷新了。在Spring Cloud 監聽到變化,會執行ContextRefresher#refresh()。首先會重新載入引導信息,也就會重新調用NacosPropertySourceLocator#locate(),從而從Nacos中讀取最新的配置。其次會清空緩存對象,重新以最新的Enviroment生成對象,來達到屬性更新的目的。
整體來說Nacos Config在Spring Cloud的實現比較簡單。核心使用了Spring Cloud的RefreshScope機制來實現對象屬性的動態刷新。