阿里巴巴是如何打通 CMDB,實現就近訪問的?

CMDB在企業中,通常用於存放與機器設備、應用、服務等相關的元數據。當企業的機器及應用達到必定規模後就須要這樣一個系統來存儲和管理它們的元數據。有一些普遍使用的屬性,例如機器的IP、主機名、機房、應用、region等,這些數據通常會在機器部署時錄入到CMDB,運維或者監控平臺會使用這些數據進行展現或者相關的運維操做。java

在服務進行多機房或者多地域部署時,跨地域的服務訪問每每延遲較高,一個城市內的機房間的典型網絡延遲在1ms左右,而跨城市的網絡延遲,例如上海到北京大概爲30ms。此時天然而然的一個想法就是能不能讓服務消費者和服務提供者進行同地域訪問。git

咱們在集團內部的實踐中,這樣的需求是經過和CMDB打通來實現的。Nacos的服務發現組件中,對接CMDB,而後經過配置的訪問規則,來實現服務消費者到服務提供者的同地域優先。github

<div data-type="alignment" data-value="center" style="text-align:center"> <div data-type="p">圖1 服務的同地域優先訪問</div> </div>apache

這實際上就是一種負載均衡策略,在Nacos的規劃中,豐富的服務端的可配置負載均衡策略是咱們的重要發展方向,這與當前已有的註冊中心產品不太同樣。在設計如何在開源的場景中,支持就近訪問的時候,與企業自帶的CMDB集成是咱們考慮的一個核心問題。除此以外,咱們也在考慮將Nacos自身擴展爲一個實現基礎功能的CMDB。不管如何,咱們都須要可以從某個地方獲取IP的環境信息,這些信息要麼是從企業的CMDB中查詢而來,要麼是從本身內置的存儲中查詢而來。api

CMDB插件機制

先不考慮如何將CMDB的數據應用於負載均衡,咱們須要首先在Nacos裏將CMDB的數據經過某種方法獲取。在實際使用中,基本上每一個公司都會經過購買或者自研搭建本身的CMDB,那麼爲了可以解耦各個企業的CMDB具體實現,一個比較好的策略是使用SPI機制,約定CMDB的抽象調用接口,由各個企業添加本身的CMDB插件,無需任何代碼上的從新構建,便可在運行狀態下對接上企業的CMDB。緩存

<div data-type="alignment" data-value="center" style="text-align:center"> <div data-type="p">圖2 Nacos CMDB SPI機制原理</div> </div>網絡

如圖2所示,Nacos定義了一個SPI接口,裏面包含了與第三方CMDB約定的一些方法。用戶依照約定實現了相應的SPI接口後,將實現打成jar包放置到Nacos安裝目錄下,重啓Nacos便可讓Nacos與CMDB的數據打通。整個流程並不複雜,可是理解CMDB SPI接口裏方法和相應概念的含義不太簡單。在這裏對CMDB機制的相關概念和接口含義作一個詳細說明。架構

CMDB抽象概念

實體(Entity)

實體是做爲CMDB裏數據的承載方,在通常的CMDB中,一個實體能夠指一個IP、應用或者服務。而這個實體會有不少屬性,例如IP的機房信息,服務的版本信息等。app

實體類型(Entity Type)

咱們並不限定實體必定是IP、應用或者服務,這取決於實際的業務場景。Nacos有計劃在將來支持不一樣的實體類型,不過就目前來講,服務發現須要的實體類型是IP。負載均衡

標籤(Label)

Label是咱們抽象出的Entity屬性,Label定義爲一個描述Entity屬性的K-V鍵值對。Label的key和value的取值範圍通常都是預先定義好的,當須要對Label進行變動,如增長新的key或者value時,須要調用單獨的接口並觸發相應的事件。一個常見的Label的例子是IP的機房信息,咱們認爲機房(site)是Label的key,而機房的集合(site1, site2, site3)是Label的value,這個Label的定義就是:site: {site1, site2, site3}。

實體事件(Entity Event)

實體的標籤的變動事件。當CMDB的實體屬性發生變化,須要有一個事件機制來通知全部訂閱方。爲了保證明體事件攜帶的變動信息是最新準確的,這個事件裏只會包含變動的實體的標識以及變動事件的類型,不會包含變動的標籤的值。

CMDB約定接口

在設計與CMDB交互接口的時候,咱們參考了內部對CMDB的訪問接口,並與若干個外部客戶進行了討論。咱們最終肯定了如下要求第三方CMDB插件必須實現的接口:

獲取標籤列表

Set<String> getLabelNames();

這個方法將返回CMDB中須要被Nacos識別的標籤名集合,CMDB插件能夠按需決定返回什麼標籤個Nacos。不在這個集合的標籤將會被Nacos忽略,即便這個標籤出如今實體的屬性裏。咱們容許這個集合會在運行時動態變化,Nacos會定時去調用這個接口刷新標籤集合。

獲取實體類型

Set<String> getEntityTypes();

獲取CMDB裏的實體的類型集合,不在這個集合的實體類型會被Nacos忽略。服務發現模塊目前須要的實體相似是ip,若是想要經過打通CMDB數據來實現服務的高級負載均衡,請務必在返回集合裏包含「ip」。

獲取標籤詳情

Label getLabel(String labelName);

獲取標籤的詳細信息。返回的Label類裏包含標籤的名字和標籤值的集合。若是某個實體的這個標籤的值不在標籤值集合裏,將會被視爲無效。

查詢實體的標籤值

String getLabelValue(String entityName, String entityType, String labelName);
Map<String, String> getLabelValues(String entityName, String entityType);

這裏包含兩個方法,一個是獲取實體某一個標籤名對應的值,一個是獲取實體全部標籤的鍵值對。參數裏包含實體的值和實體的類型。注意,這個方法並不會在每次在Nacos內部觸發查詢時去調用,Nacos內部有一個CMDB數據的緩存,只有當這個緩存失效或者不存在時,纔會去訪問CMDB插件查詢數據。爲了讓CMDB插件的實現儘可能簡單,咱們在Nacos內部實現了相應的緩存和刷新邏輯。

查詢實體

Map<String, Map<String, Entity>> getAllEntities();
Entity getEntity(String entityName, String entityType);

查詢實體包含兩個方法:查詢全部實體和查詢單個實體。查詢單個實體目前其實就是查詢這個實體的全部標籤,不過咱們將這個方法與獲取全部標籤的方法區分開來,由於查詢單個實體方法後面可能會進行擴展,比查詢全部標籤獲取的信息要更多。

查詢全部實體則是一次性將CMDB的全部數據拉取過來,該方法可能會比較消耗性能,不管是對於Nacos仍是CMDB。Nacos內部調用該方法的策略是經過可配置的定時任務週期來定時拉取全部數據,在實現該CMDB插件時,也請關注CMDB服務自己的性能,採起合適的策略。

查詢實體事件

List<EntityEvent> getEntityEvents(long timestamp);

這個方法意在獲取最近一段時間內實體的變動消息,增量的去拉取變動的實體。由於Nacos不會實時去訪問CMDB插件查詢實體,須要這個拉取事件的方法來獲取實體的更新。參數裏的timestamp爲上一次拉取事件的時間,CMDB插件能夠選擇使用或者忽略這個參數。

CMDB插件開發流程

參考 https://github.com/nacos-group/nacos-examples,這裏已經給出了一個示例plugin實現。 具體步驟以下:

  1. 新建一個maven工程,引入依賴nacos-api:
    <dependency>
                <groupId>com.alibaba.nacos</groupId>
                <artifactId>nacos-api</artifactId>
                <version>0.7.0</version>
            </dependency>
  2. 引入打包插件:
    <plugin>
                    <groupId>org.apache.maven.plugins</groupId>
                    <artifactId>maven-assembly-plugin</artifactId>
                    <configuration>
                        <descriptorRefs>
                            <descriptorRef>jar-with-dependencies</descriptorRef>
                        </descriptorRefs>
                    </configuration>
                </plugin>
  3. 定義實現類,繼承com.alibaba.nacos.api.cmdb.CmdbService,並實現相關方法。

  1. 在src/main/resource/目錄下新建目錄:META-INF/services

  1. 在src/main/resources/META-INF/services目錄下新建文件com.alibaba.nacos.api.cmdb.CmdbService,並在文件裏將第三步中建立的實現類全名寫入該文件:

  1. 代碼自測完成後,執行命令進行打包:
    mvn package assembly:single -Dmaven.test.skip=true
  2. 將target目錄下的包含依賴的jar包上傳到nacos CMDB插件目錄:
    {nacos.home}/plugins/cmdb
  3. 在nacos的application.properties裏打開加載插件開關:
    nacos.cmdb.loadDataAtStart=true
  4. 重啓nacos Server,便可加載到您實現的nacos-cmdb插件獲取您的CMDB數據。

使用Selector實現同機房優先訪問

在拿到CMDB的數據以後,就能夠運用CMDB數據的強大威力來實現多種靈活的負載均衡策略了,下面舉例來講明如何使用CMDB數據和Selector來實現就近訪問。

假設目前Nacos已經經過CMDB拿到了一些IP的機房信息,且它們對應的標籤信息以下:

11.11.11.11
    site: x11

22.22.22.22
    site: x12

33.33.33.33
    site: x11

44.44.44.44
    site: x12

55.55.55.55
    site: x13

11.11.11.十一、22.22.22.2二、33.33.33.3三、44.44.44.44和55.55.55.55.55都包含了標籤site,且它們對應的值分別爲x十一、x十二、x十一、x十二、x13。咱們先註冊一個服務,下面掛載IP11.11.11.11和22.22.22.22。

<div data-type="alignment" data-value="center" style="text-align:center"> <div data-type="p">圖3 服務詳情</div> </div>

而後咱們修改服務的「服務路由類型」,並配置爲基於同site優先的服務路由:

<div data-type="alignment" data-value="center" style="text-align:center"> <div data-type="p">圖4 編輯服務路由類型</div> </div>

這裏咱們將服務路由類型選擇爲標籤,而後輸入標籤的表達式:

CONSUMER.label.site = PROVIDER.label.site

這個表達式的格式和咱們抽象的Selector機制有關,具體將會在另一篇文章中介紹。在這裏您須要記住的就是,任何一個以下格式的表達式:

CONSUMER.label.labelName = PROVIDER.label.labelName

將可以實現基於同labelName優先的負載均衡策略。

而後假設服務消費者的IP分別爲33.33.33.3三、44.44.44.44和55.55.55.55,它們在使用以下接口查詢服務實例列表:

naming.selectInstances("nacos.test.1", true)

那麼不一樣的消費者,將獲取到不一樣的實例列表。33.33.33.33獲取到11.11.11.11,44.44.44.44將獲取到22.22.22.22,而55.55.55.55將同時獲取到11.11.11.11和22.22.22.22。

以上,即是咱們在Nacos中經過打通CMDB,實現就近訪問的實踐。Nacos是阿里巴巴開源的服務註冊與配置管理產品,參考:《阿里啓動新項目:Nacos,比 Eureka 更強!》。

本文原創首發於公衆號:Java技術棧(id:javastack),關注公衆號在後臺回覆 "架構" 可獲取更多,轉載請原樣保留本信息。

相關文章
相關標籤/搜索