Nacos新版Selector提升节点筛选的扩展性和灵活性

Nacos提供CMDB模块用于将用户的CMDB信息集成到Nacos中,然后配合用户选择的Selector进行节点的就近选择,从而提升性能。具体的实现和使用已经在文章Nacos通过CMDB实现就近访问提升性能中有过介绍。Nacos旧版本的Selector实现逻辑较为简单,主要是按照CMDB资源的label信息进行匹配,由于其属于内置的实现方式,就导致扩展性和灵活性比较差。所以,在2.1版本中我们将其重新设计和实现,在不改变功能的前提下,让用户可以自定义其业务所需Selector的实现逻辑。下面我将从新版本的Selector实现逻辑和使用两个步骤来带你更快的体验新版本的Selector。

Selector插件机制

工作原理

现有的Selector实际包含了两步操作。第一步,准备实例筛选所需的资源信息。第二步,执行筛选逻辑筛选实例。所以我们将Selector的功能进行解耦,将资源准备和筛选逻辑均抽象为独立的功能作为扩展点提供给用户实现,同时提供内置的CMDB资源类型的准备,用户可以灵活的根据业务所需来选择实现自己的功能。


插拔式工作原理

抽象概念

由于功能点的拆分,新版本的Selector引入了一些SPI扩展点,下面简单介绍新版Selector中所提供的一些SPI接口。

SelectorContextBuilder

SelectorContextBuilder是前文所提到的资源准备SPI接口,用于提供给用户封装自定义的资源准备逻辑。用户可以通过实现此SPI接口,将自定义的资源信息加载到Nacos中,提供给对应资源的Selector进行资源筛选逻辑。

  • build方法用于构建资源信息。
  • getContextType返回当前资源准备器的类型(如我们的CMDB资源实际对应我们内嵌至Nacos中的CMDB资源准备)。


    SelectorContextBuilder SPI接口

Selector

Selector用于封装用户的实例筛选逻辑SPI接口,用户可以将外部传入的表达式封装到Selector,然后自定义业务所需筛选逻辑继续实例筛选。

  • parse方法用于外部传入的表达式,封装到Selector中。
  • select用于执行筛选逻辑。
  • getType返回当前Selector类型,同时会在Nacos控制台中展示。
  • getContextType返回当前Selector所需的资源类型,对应前文中提到的SelectorContextBuilder#getContextType。
    值得注意的是,我们的Selector#select方法的入参会来自对应的SelectorContextBuilder#build的返回值,且会根据ContextType一一对应。


    Selector SPI接口

    同时我们内置了CMDB资源的准备,主要是和Nacos内置的CMDB插件进行整合,提供服务的CMDB资源信息。如果用户想要直接使用Nacos准备的CMDB资源而不需要自己准备资源,那么可以继承我们预留的AbstractCmdbSelector。


    AbstractCmdbSelector CMDB资源类型的Selector

此类型的Selector会使用Nacos内置的CMDB资源,用户只需要实现doParse和doSelect方法即可使用CMDB资源作为入参实现自己的实例筛选逻辑,适用于大部分用户的业务场景。

快速入门

实现一个基于CMDB资源的Selector

具体步骤如下:
1.新建一个maven工程,引入依赖nacos-api:

<dependency>
    <groupId>com.alibaba.nacos</groupId>
    <artifactId>nacos-api</artifactId>
    <version>2.1.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.selector.AbstractCmdbSelector,并实现相关方法。

public class ExampleCmdbSelector extends AbstractCmdbSelector<Instance> {

    private List<String> tags = new ArrayList<>();

    public List<String> getTags() {
        return tags;
    }

    public void setTags(List<String> tags) {
        this.tags = tags;
    }

    @Override
    protected List<Instance> doSelect(CmdbContext<Instance> context) {
        return context.getProviders().stream()
                .filter(ci -> tags.contains(ci.getEntity().getName()))
                .map(CmdbContext.CmdbInstance::getInstance)
                .collect(Collectors.toList());
    }

    @Override
    protected void doParse(String expression) throws NacosException {
        tags.addAll(Arrays.asList(expression.split(",")));
    }

    @Override
    public String getType() {
        return "example";
    }
}

4.在src/main/resource/目录下新建目录:META-INF/services


文件目录

5.在src/main/resources/META-INF/services目录下新建文件com.alibaba.nacos.api.selector.Selector,并在文件里将第三步中创建的实现类全名写入该文件:


SPI文件

6.执行maven打包:

mvn package assembly:single -Dmaven.test.skip=true

7.将target目录下的包含依赖的jar包上传到nacos CMDB插件目录:

{nacos.home}/plugins/selector

8.重启nacos Server,即可加载到你的Selector插件到Nacos中,同时可以在Nacos控制台中找到对应的Selector。


自定义Selector

实现一个Tag资源类型的Selector

打包和配置SPI的流程按照上述流程,此处不再赘述。

实现自定义ContextBuilder。

如果IP地址为127.0.0.1,那么此实例标签为A,否则为B。

public class TagContextBuilder implements SelectorContextBuilder<TagContext, String, List<Instance>> {
    @Override
    public TagContext build(String consumer, List<Instance> provider) {
        TagContext tagContext = new TagContext();
        List<TagContext.TagInstance> tagInstances = provider.stream()
                .map(i -> {
                    TagContext.TagInstance tagInstance = new TagContext.TagInstance();
                    tagInstance.setInstance(i);
                    if (i.getIp().equals("127.0.0.1")) {
                        tagInstance.setTag("A");
                    } else {
                        tagInstance.setTag("B");
                    }
                    return tagInstance;
                })
                .collect(Collectors.toList());
        tagContext.setTagInstances(tagInstances);
        return tagContext;
    }

    @Override
    public String getContextType() {
        return "TAG";
    }
}

实现自定义资源类型Selector。

如果标签集合中包含实例的标签,那么返回此实例,否则不返回。

public class TagSelector implements Selector<List<Instance>, TagContext, String> {

    List<String> tags = new ArrayList<>();

    public List<String> getTags() {
        return tags;
    }

    public void setTags(List<String> tags) {
        this.tags = tags;
    }

    private String expression;

    public String getExpression() {
        return expression;
    }

    public void setExpression(String expression) {
        this.expression = expression;
    }

    @Override
    public Selector<List<Instance>, TagContext, String> parse(String expression) throws NacosException {
        if (StringUtils.isBlank(expression)) {
            return this;
        }
        this.expression = expression;
        tags.addAll(Arrays.asList(expression.split(",")));
        return this;
    }

    @Override
    public List<Instance> select(TagContext context) {
        return context.getTagInstances().stream()
                .filter(tagInstance -> tags.contains(tagInstance.getTag()))
                .map(TagContext.TagInstance::getInstance)
                .collect(Collectors.toList());
    }

    @Override
    public String getType() {
        return "tag";
    }

    @Override
    public String getContextType() {
        return "TAG";
    }
}

验证

我们向Nacos注册服务,IP为1.1.1.1。此时我们配置Tag为A的实例应该被查询出。


配置查询标签为A

由于我们实例为1.1.1.1,并不是127.0.0.1,实例的Tag为B。所以查询结果为空。


查询结果

我们更改表达式查询B标签的实例。
配置查询标签为B

此时我们配置Tag为B的实例应该被查询出。


查询结果

至此,我们已经完成了基于内置ContextBuilder的Selector实现和自定义资源类型的Selector实现。

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 222,104评论 6 515
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 94,816评论 3 399
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 168,697评论 0 360
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 59,836评论 1 298
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 68,851评论 6 397
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 52,441评论 1 310
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 40,992评论 3 421
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 39,899评论 0 276
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 46,457评论 1 318
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 38,529评论 3 341
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 40,664评论 1 352
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 36,346评论 5 350
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 42,025评论 3 334
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 32,511评论 0 24
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 33,611评论 1 272
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 49,081评论 3 377
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 45,675评论 2 359