Spring Cloud Alibaba——Nacos服務(wù)發(fā)現(xiàn):CMDB功能解析

前言

CMDB在企業(yè)中,一般用于存放與機(jī)器設(shè)備华坦、應(yīng)用、服務(wù)等相關(guān)的元數(shù)據(jù)不从。當(dāng)企業(yè)的機(jī)器及應(yīng)用達(dá)到一定規(guī)模后就需要這樣一個(gè)系統(tǒng)來(lái)存儲(chǔ)和管理它們的元數(shù)據(jù)惜姐。有一些廣泛使用的屬性,例如機(jī)器的IP椿息、主機(jī)名歹袁、機(jī)房坷衍、應(yīng)用、region等条舔,這些數(shù)據(jù)一般會(huì)在機(jī)器部署時(shí)錄入到CMDB枫耳,運(yùn)維或者監(jiān)控平臺(tái)會(huì)使用這些數(shù)據(jù)進(jìn)行展示或者相關(guān)的運(yùn)維操作。

在服務(wù)進(jìn)行多機(jī)房或者多地域部署時(shí)逞刷,跨地域的服務(wù)訪問(wèn)往往延遲較高嘉涌,一個(gè)城市內(nèi)的機(jī)房間的典型網(wǎng)絡(luò)延遲在1ms左右,而跨城市的網(wǎng)絡(luò)延遲夸浅,例如上海到北京大概為30ms仑最。此時(shí)自然而然的一個(gè)想法就是能不能讓服務(wù)消費(fèi)者和服務(wù)提供者進(jìn)行同地域訪問(wèn)。

在Nacos的服務(wù)發(fā)現(xiàn)組件中帆喇,對(duì)接CMDB警医,然后通過(guò)配置的訪問(wèn)規(guī)則,來(lái)實(shí)現(xiàn)服務(wù)消費(fèi)者到服務(wù)提供者的同地域優(yōu)先坯钦。

服務(wù)的同地域優(yōu)先訪問(wèn)

這實(shí)際上就是一種負(fù)載均衡策略预皇,在Nacos的規(guī)劃中,豐富的服務(wù)端的可配置負(fù)載均衡策略是我們的重要發(fā)展方向婉刀,這與當(dāng)前已有的注冊(cè)中心產(chǎn)品不太一樣吟温。在設(shè)計(jì)如何在開源的場(chǎng)景中,支持就近訪問(wèn)的時(shí)候突颊,與企業(yè)自帶的CMDB集成是考慮的一個(gè)核心問(wèn)題鲁豪。除此之外,也在考慮將Nacos自身擴(kuò)展為一個(gè)實(shí)現(xiàn)基礎(chǔ)功能的CMDB律秃。無(wú)論如何爬橡,都需要能夠從某個(gè)地方獲取IP的環(huán)境信息,這些信息要么是從企業(yè)的CMDB中查詢而來(lái)棒动,要么是從自己內(nèi)置的存儲(chǔ)中查詢而來(lái)糙申。

CMDB插件機(jī)制

先不考慮如何將CMDB的數(shù)據(jù)應(yīng)用于負(fù)載均衡,我們需要首先在Nacos里將CMDB的數(shù)據(jù)通過(guò)某種方法獲取船惨。在實(shí)際使用中柜裸,基本上每個(gè)公司都會(huì)通過(guò)購(gòu)買或者自研搭建自己的CMDB,那么為了能夠解耦各個(gè)企業(yè)的CMDB具體實(shí)現(xiàn)掷漱,一個(gè)比較好的策略是使用SPI機(jī)制粘室,約定CMDB的抽象調(diào)用接口,由各個(gè)企業(yè)添加自己的CMDB插件卜范,無(wú)需任何代碼上的重新構(gòu)建,即可在運(yùn)行狀態(tài)下對(duì)接上企業(yè)的CMDB鹿榜。

Nacos CMDB SPI機(jī)制原理

Nacos定義了一個(gè)SPI接口海雪,里面包含了與第三方CMDB約定的一些方法锦爵。用戶依照約定實(shí)現(xiàn)了相應(yīng)的SPI接口后,將實(shí)現(xiàn)打成jar包放置到Nacos安裝目錄下奥裸,重啟Nacos即可讓Nacos與CMDB的數(shù)據(jù)打通险掀。整個(gè)流程并不復(fù)雜,但是理解CMDB SPI接口里方法和相應(yīng)概念的含義不太簡(jiǎn)單湾宙。在這里對(duì)CMDB機(jī)制的相關(guān)概念和接口含義做一個(gè)詳細(xì)說(shuō)明樟氢。

CMDB抽象概念

實(shí)體(Entity)

實(shí)體是作為CMDB里數(shù)據(jù)的承載方,在一般的CMDB中侠鳄,一個(gè)實(shí)體可以指一個(gè)IP埠啃、應(yīng)用或者服務(wù)。而這個(gè)實(shí)體會(huì)有很多屬性伟恶,例如IP的機(jī)房信息碴开,服務(wù)的版本信息等。

實(shí)體類型(Entity Type)

我們并不限定實(shí)體一定是IP博秫、應(yīng)用或者服務(wù)潦牛,這取決于實(shí)際的業(yè)務(wù)場(chǎng)景。Nacos有計(jì)劃在未來(lái)支持不同的實(shí)體類型挡育,不過(guò)就目前來(lái)說(shuō)巴碗,服務(wù)發(fā)現(xiàn)需要的實(shí)體類型是IP。

標(biāo)簽(Label)

Label是我們抽象出的Entity屬性即寒,Label定義為一個(gè)描述Entity屬性的K-V鍵值對(duì)橡淆。Label的key和value的取值范圍一般都是預(yù)先定義好的,當(dāng)需要對(duì)Label進(jìn)行變更蒿叠,如增加新的key或者value時(shí)明垢,需要調(diào)用單獨(dú)的接口并觸發(fā)相應(yīng)的事件。一個(gè)常見(jiàn)的Label的例子是IP的機(jī)房信息市咽,我們認(rèn)為機(jī)房(site)是Label的key痊银,而機(jī)房的集合(site1, site2, site3)是Label的value,這個(gè)Label的定義就是:site: {site1, site2, site3}施绎。

實(shí)體事件(Entity Event)

實(shí)體的標(biāo)簽的變更事件溯革。當(dāng)CMDB的實(shí)體屬性發(fā)生變化,需要有一個(gè)事件機(jī)制來(lái)通知所有訂閱方谷醉。為了保證實(shí)體事件攜帶的變更信息是最新準(zhǔn)確的致稀,這個(gè)事件里只會(huì)包含變更的實(shí)體的標(biāo)識(shí)以及變更事件的類型,不會(huì)包含變更的標(biāo)簽的值俱尼。

CMDB約定接口
在設(shè)計(jì)與CMDB交互接口的時(shí)候抖单,我們參考了內(nèi)部對(duì)CMDB的訪問(wèn)接口,并與若干個(gè)外部客戶進(jìn)行了討論。我們最終確定了以下要求第三方CMDB插件必須實(shí)現(xiàn)的接口:

獲取標(biāo)簽列表

Set<String> getLabelNames();

這個(gè)方法將返回CMDB中需要被Nacos識(shí)別的標(biāo)簽名集合矛绘,CMDB插件可以按需決定返回什么標(biāo)簽個(gè)Nacos耍休。不在這個(gè)集合的標(biāo)簽將會(huì)被Nacos忽略,即使這個(gè)標(biāo)簽出現(xiàn)在實(shí)體的屬性里货矮。我們?cè)试S這個(gè)集合會(huì)在運(yùn)行時(shí)動(dòng)態(tài)變化羊精,Nacos會(huì)定時(shí)去調(diào)用這個(gè)接口刷新標(biāo)簽集合。

獲取實(shí)體類型

Set<String> getEntityTypes();

獲取CMDB里的實(shí)體的類型集合囚玫,不在這個(gè)集合的實(shí)體類型會(huì)被Nacos忽略喧锦。服務(wù)發(fā)現(xiàn)模塊目前需要的實(shí)體類型是ip,如果想要通過(guò)打通CMDB數(shù)據(jù)來(lái)實(shí)現(xiàn)服務(wù)的高級(jí)負(fù)載均衡抓督,請(qǐng)務(wù)必在返回集合里包含“ip”燃少。

獲取標(biāo)簽詳情

Label getLabel(String labelName);

獲取標(biāo)簽的詳細(xì)信息。返回的Label類里包含標(biāo)簽的名字和標(biāo)簽值的集合本昏。如果某個(gè)實(shí)體的這個(gè)標(biāo)簽的值不在標(biāo)簽值集合里供汛,將會(huì)被視為無(wú)效。

查詢實(shí)體的標(biāo)簽值

String getLabelValue(String entityName, String entityType, String labelName);

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

這里包含兩個(gè)方法涌穆,一個(gè)是獲取實(shí)體某一個(gè)標(biāo)簽名對(duì)應(yīng)的值怔昨,一個(gè)是獲取實(shí)體所有標(biāo)簽的鍵值對(duì)。參數(shù)里包含實(shí)體的值和實(shí)體的類型宿稀。注意趁舀,這個(gè)方法并不會(huì)在每次在Nacos內(nèi)部觸發(fā)查詢時(shí)去調(diào)用,Nacos內(nèi)部有一個(gè)CMDB數(shù)據(jù)的緩存祝沸,只有當(dāng)這個(gè)緩存失效或者不存在時(shí)矮烹,才會(huì)去訪問(wèn)CMDB插件查詢數(shù)據(jù)。為了讓CMDB插件的實(shí)現(xiàn)盡量簡(jiǎn)單罩锐,我們?cè)贜acos內(nèi)部實(shí)現(xiàn)了相應(yīng)的緩存和刷新邏輯奉狈。

查詢實(shí)體

Map<String, Map<String, Entity>> getAllEntities();

Entity getEntity(String entityName, String entityType);

查詢實(shí)體包含兩個(gè)方法:查詢所有實(shí)體和查詢單個(gè)實(shí)體。查詢單個(gè)實(shí)體目前其實(shí)就是查詢這個(gè)實(shí)體的所有標(biāo)簽涩惑,不過(guò)我們將這個(gè)方法與獲取所有標(biāo)簽的方法區(qū)分開來(lái)仁期,因?yàn)椴樵儐蝹€(gè)實(shí)體方法后面可能會(huì)進(jìn)行擴(kuò)展,比查詢所有標(biāo)簽獲取的信息要更多竭恬。

查詢所有實(shí)體則是一次性將CMDB的所有數(shù)據(jù)拉取過(guò)來(lái)跛蛋,該方法可能會(huì)比較消耗性能,無(wú)論是對(duì)于Nacos還是CMDB痊硕。Nacos內(nèi)部調(diào)用該方法的策略是通過(guò)可配置的定時(shí)任務(wù)周期來(lái)定時(shí)拉取所有數(shù)據(jù)赊级,在實(shí)現(xiàn)該CMDB插件時(shí),也請(qǐng)關(guān)注CMDB服務(wù)本身的性能岔绸,采取合適的策略理逊。

查詢實(shí)體事件

List<EntityEvent> getEntityEvents(long timestamp);

這個(gè)方法意在獲取最近一段時(shí)間內(nèi)實(shí)體的變更消息橡伞,增量的去拉取變更的實(shí)體。因?yàn)镹acos不會(huì)實(shí)時(shí)去訪問(wèn)CMDB插件查詢實(shí)體挡鞍,需要這個(gè)拉取事件的方法來(lái)獲取實(shí)體的更新骑歹。參數(shù)里的timestamp為上一次拉取事件的時(shí)間预烙,CMDB插件可以選擇使用或者忽略這個(gè)參數(shù)墨微。

CMDB插件開發(fā)流程

參考:https://github.com/nacos-group/nacos-examples,這里已經(jīng)給出了一個(gè)示例plugin實(shí)現(xiàn)扁掸。

具體步驟如下:

  • 1翘县、新建一個(gè)maven工程,引入依賴nacos-api:
<dependencies>
    <dependency>
        <groupId>com.alibaba.nacos</groupId>
        <artifactId>nacos-api</artifactId>
        <version>1.4.2</version>
    </dependency>
</dependencies>
  • 2谴分、引入打包插件:
<build>
    <plugins>
        <plugin>
            <groupId>org.apache.maven.plugins</groupId>
            <artifactId>maven-assembly-plugin</artifactId>
            <configuration>
                <descriptorRefs>
                    <descriptorRef>jar-with-dependencies</descriptorRef>
                </descriptorRefs>
            </configuration>
        </plugin>
    </plugins>
</build>
  • 3锈麸、定義實(shí)現(xiàn)類,繼承com.alibaba.nacos.api.cmdb.CmdbService牺蹄,并實(shí)現(xiàn)相關(guān)方法忘伞。
package com.yibo.cmdb;

import com.alibaba.nacos.api.cmdb.pojo.*;
import com.alibaba.nacos.api.cmdb.spi.CmdbService;
import java.util.*;
import java.util.concurrent.ConcurrentHashMap;

/**
 * @Author: huangyibo
 * @Date: 2021/7/31 23:59
 * @Description:
 */
public class CmdbServiceImpl implements CmdbService {

    private int index = 1;

    private Map<String,Map<String, Entity>> entityMap = new ConcurrentHashMap<String, Map<String, Entity>>();

    private Map<String,Label> labelMap = new ConcurrentHashMap<String, Label>();

    /**
     * CMDB_DATA_TABLE:模擬cmdb數(shù)據(jù)庫(kù)數(shù)據(jù)
     */
    public CmdbServiceImpl() {
        Label label = new Label();
        label.setName("cluster");
        Set<String> values = new HashSet<String>();
        values.add("shenzhen");
        values.add("shanghai");
        label.setValues(values);
        labelMap.put(label.getName(),label);
        entityMap.put(PreservedEntityTypes.ip.name(),new HashMap<String, Entity>());

        //深圳cluster的ip
        Entity entityShen = new Entity();
        entityShen.setName("192.168.50.253");
        entityShen.setType(PreservedEntityTypes.ip.name());
        Map<String,String> lableShen = new HashMap<String, String>();
        lableShen.put("cluster","shenzhen");
        entityShen.setLabels(lableShen);
        entityMap.get(PreservedEntityTypes.ip.name()).put(entityShen.getName(),entityShen);

        //上海cluster的ip
        Entity entityShang = new Entity();
        entityShang.setName("192.168.50.175");
        entityShang.setType(PreservedEntityTypes.ip.name());
        Map<String,String> lableShang = new HashMap<String, String>();
        lableShang.put("cluster","shanghai");
        entityShang.setLabels(lableShang);
        entityMap.get(PreservedEntityTypes.ip.name()).put(entityShang.getName(),entityShang);

    }

    public Set<String> getLabelNames() {
        Set<String> labelNames = new HashSet<>();
        labelMap.forEach((key,value) -> {
            labelNames.add(key);
        });
        return labelNames;
    }

    public Set<String> getEntityTypes() {
        Set<String> types = new HashSet<>();
        entityMap.forEach((key,value) -> {
            types.add(key);
        });
        return types;
    }

    public Label getLabel(String labelName) {
        return labelMap.get(labelName);
    }

    public String getLabelValue(String entityName, String entityType, String labelName) {
        return entityMap.get(entityType).get(entityName).getLabels().get(labelName);
    }

    public Map<String, String> getLabelValues(String entityName, String entityType) {
        return entityMap.get(entityType).get(entityName).getLabels();
    }

    public Map<String, Map<String, Entity>> getAllEntities() {
        return entityMap;
    }

    public List<EntityEvent> getEntityEvents(long timestamp) {
        Entity entity = new Entity();
        entity.setName("1.1.1.1");
        entity.setType(PreservedEntityTypes.ip.name());
        Map<String, String> labels = new HashMap<>();
        labels.put("cluster1", "x1" + ((index % 3) + 1));
        labels.put("cluster2", "x2" + ((index++ % 3) + 1));

        entity.setLabels(labels);

        entityMap.get(PreservedEntityTypes.ip.name()).put("1.1.1.1", entity);

        EntityEvent entityEvent = new EntityEvent();
        entityEvent.setEntityName("1.1.1.1");
        entityEvent.setEntityType("PreservedEntityTypes.ip.name()");
        entityEvent.setType(EntityEventType.ENTITY_ADD_OR_UPDATE);
        List<EntityEvent> list = new ArrayList<>();
        list.add(entityEvent);

        return list;
    }

    public Entity getEntity(String entityName, String entityType) {
        return entityMap.get(entityType).get(entityName);
    }
}
  • 4、在src/main/resource/目錄下新建目錄:META-INF/services沙兰。
    • 且在該目錄下新建com.alibaba.nacos.api.cmdb.CmdbService文件
    • 將CmdbService接口的實(shí)現(xiàn)類全名寫入該文件
com.yibo.cmdb.CmdbServiceImpl
  • 5氓奈、代碼自測(cè)完成后,執(zhí)行命令進(jìn)行打包:
mvn package assembly:single -Dmaven.test.skip=true
  • 6鼎天、將target目錄下的包含依賴的jar包上傳到nacos CMDB插件目錄:
{nacos.home}/plugins/cmdb
  • 7舀奶、在nacos的application.properties里打開加載插件開關(guān):
nacos.cmdb.loadDataAtStart=true

重啟nacos Server,即可加載到您實(shí)現(xiàn)的nacos-cmdb插件獲取您的CMDB數(shù)據(jù)斋射。

使用Selector實(shí)現(xiàn)同機(jī)房?jī)?yōu)先訪問(wèn)

在拿到CMDB的數(shù)據(jù)之后育勺,就可以運(yùn)用CMDB數(shù)據(jù)的強(qiáng)大威力來(lái)實(shí)現(xiàn)多種靈活的負(fù)載均衡策略了,下面舉例來(lái)說(shuō)明如何使用CMDB數(shù)據(jù)和Selector來(lái)實(shí)現(xiàn)就近訪問(wèn)罗岖。

假設(shè)目前Nacos已經(jīng)通過(guò)CMDB拿到了一些IP的機(jī)房信息涧至,且它們對(duì)應(yīng)的標(biāo)簽信息如下:

192.168.50.253
    cluster: shenzhen

192.168.50.175
    cluster: shanghai

服務(wù)詳情

然后我們修改服務(wù)的“服務(wù)路由類型”,并配置為基于同cluster優(yōu)先的服務(wù)路由:


編輯服務(wù)路由類型
這里我們將服務(wù)路由類型選擇為標(biāo)簽桑包,然后輸入標(biāo)簽的表達(dá)式:

CONSUMER.label.cluster= PROVIDER.label.cluster

這個(gè)表達(dá)式的格式和我們抽象的Selector機(jī)制有關(guān)南蓬,在這里您需要記住的就是,任何一個(gè)如下格式的表達(dá)式:

CONSUMER.label.labelName = PROVIDER.label.labelName

將能夠?qū)崿F(xiàn)基于同labelName優(yōu)先的負(fù)載均衡策略捡多。

以上蓖康,便是我們?cè)贜acos中通過(guò)打通CMDB,實(shí)現(xiàn)就近訪問(wèn)的實(shí)踐垒手。

參考:
https://www.cnblogs.com/yunqishequ/p/10266700.html

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末蒜焊,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子科贬,更是在濱河造成了極大的恐慌泳梆,老刑警劉巖鳖悠,帶你破解...
    沈念sama閱讀 206,378評(píng)論 6 481
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場(chǎng)離奇詭異优妙,居然都是意外死亡乘综,警方通過(guò)查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,356評(píng)論 2 382
  • 文/潘曉璐 我一進(jìn)店門套硼,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái)卡辰,“玉大人,你說(shuō)我怎么就攤上這事邪意【怕瑁” “怎么了?”我有些...
    開封第一講書人閱讀 152,702評(píng)論 0 342
  • 文/不壞的土叔 我叫張陵雾鬼,是天一觀的道長(zhǎng)萌朱。 經(jīng)常有香客問(wèn)我,道長(zhǎng)策菜,這世上最難降的妖魔是什么晶疼? 我笑而不...
    開封第一講書人閱讀 55,259評(píng)論 1 279
  • 正文 為了忘掉前任,我火速辦了婚禮又憨,結(jié)果婚禮上翠霍,老公的妹妹穿的比我還像新娘。我一直安慰自己竟块,他們只是感情好壶运,可當(dāng)我...
    茶點(diǎn)故事閱讀 64,263評(píng)論 5 371
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著浪秘,像睡著了一般蒋情。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上耸携,一...
    開封第一講書人閱讀 49,036評(píng)論 1 285
  • 那天棵癣,我揣著相機(jī)與錄音,去河邊找鬼夺衍。 笑死狈谊,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的沟沙。 我是一名探鬼主播河劝,決...
    沈念sama閱讀 38,349評(píng)論 3 400
  • 文/蒼蘭香墨 我猛地睜開眼,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼矛紫!你這毒婦竟也來(lái)了赎瞎?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 36,979評(píng)論 0 259
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤颊咬,失蹤者是張志新(化名)和其女友劉穎务甥,沒(méi)想到半個(gè)月后牡辽,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 43,469評(píng)論 1 300
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡敞临,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 35,938評(píng)論 2 323
  • 正文 我和宋清朗相戀三年态辛,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片挺尿。...
    茶點(diǎn)故事閱讀 38,059評(píng)論 1 333
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡奏黑,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出票髓,到底是詐尸還是另有隱情攀涵,我是刑警寧澤,帶...
    沈念sama閱讀 33,703評(píng)論 4 323
  • 正文 年R本政府宣布洽沟,位于F島的核電站,受9級(jí)特大地震影響蜗细,放射性物質(zhì)發(fā)生泄漏裆操。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,257評(píng)論 3 307
  • 文/蒙蒙 一炉媒、第九天 我趴在偏房一處隱蔽的房頂上張望踪区。 院中可真熱鬧,春花似錦吊骤、人聲如沸缎岗。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,262評(píng)論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)传泊。三九已至,卻和暖如春鸭巴,著一層夾襖步出監(jiān)牢的瞬間眷细,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 31,485評(píng)論 1 262
  • 我被黑心中介騙來(lái)泰國(guó)打工鹃祖, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留溪椎,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 45,501評(píng)論 2 354
  • 正文 我出身青樓恬口,卻偏偏與公主長(zhǎng)得像校读,于是被迫代替她去往敵國(guó)和親。 傳聞我的和親對(duì)象是個(gè)殘疾皇子祖能,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 42,792評(píng)論 2 345

推薦閱讀更多精彩內(nèi)容