Skip to content

Nacos

是什么

Nacos是一个为云原生应用提供的服务动态发现、配置管理、服务管理的平台。

支持3种集群模式:单机模式、集群模式、多集群模式。

单机模式多用于测试和单机试用,集群模式多用于生产环境,确保高可用,多集群模式多用于多数据中心场景

TIP

nacos可以做服务发现、配置管理和服务管理,支持3种集群模式

单机模式启动

nacos\bin\startup.cmd

配置单机模式

set MODE="standalone"

配置集群模式

set MODE="cluster"

管理后台

http://127.0.0.1:8848/nacos/index.html

用户名和密码都是: nacos

整体框架

Nacos提供了两个核心的服务,Config Service配置中心和Naming Service命名服务。

接着就是Nacos Core,也是Nacos的核心实现部分。

最底层是Consistency Protocol,数据一致性协议,用来实现Nacos集群数据同步。

对外,使用Http协议,提供OpenAPI,客户端程序可以通过该API来完成数据的交互和处理

Nacos2.x的新特性

新增了支持gRPC协议、安全机制比如鉴权、加密。

Nacos中的namespace和group

Nacos中提供了namespace和group命名空间和分组的机制,它是Nacos提供的一种数据模型 namespace用来区分不同环境,group可以专注在业务层面的数据分组。 不同的命名空间下,可以存在相同的Group 或 Data ID 的配置

SpringBoot注册到Nacos

yaml
spring:
  application:
    name: use-nacos-spring-boot-wxm
server:
  port: 7824
dubbo:
  registry:
    address: nacos://127.0.0.1:8848
  protocol:
    name: dubbo
    port: 20880
  application:
    parameters:
      namespace: c7ba173f-29e5-4c58-ae78-b102be11c4f9
      group: use-nacos-spring-boot
nacos:
  discovery:
    server-addr: 127.0.0.1:8848
    namespace: c7ba173f-29e5-4c58-ae78-b102be11c4f9
    register:
      group-name: use-nacos-spring-boot-wxm
      service-name: use-nacos-spring-boot-wxm
    auto-register: true
java
import org.apache.dubbo.config.spring.context.annotation.DubboComponentScan;
import org.apache.dubbo.config.spring.context.annotation.EnableDubbo;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;

@SpringBootApplication
@DubboComponentScan(value = "cn.diyai")
@EnableDubbo
public class UserNacosApplication {
    public static void main(String[] args) {
        SpringApplication.run(UserNacosApplication.class,args);
    }
}


package cn.diyai.service;
public interface PayService {
    String pay();
}

package cn.diyai.service.impl;
import cn.diyai.service.PayService;
import org.apache.dubbo.config.annotation.DubboService;
@DubboService(group = "use-nacos-spring-boot",version = "1.0.0")
public class PayServiceImpl implements PayService {
    @Override
    public String pay() {
        return "payResult";
    }
}


package cn.diyai.controller;
import cn.diyai.service.PayService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;

@RestController(value = "/pay")
public class PayController {

    @Autowired
    PayService payService;

    @GetMapping("/toPay")
    public String pay(){
        return payService.pay();
    }
}
java
@EnableDiscoveryClient(autoRegister = true)

@Import({EnableDiscoveryClientImportSelector.class})

public class NacosServiceRegistry implements ServiceRegistry<Registration> {
    private static final String STATUS_UP = "UP";
    private static final String STATUS_DOWN = "DOWN";
    private static final Logger log = LoggerFactory.getLogger(NacosServiceRegistry.class);
    private final NacosDiscoveryProperties nacosDiscoveryProperties;
    @Autowired
    private NacosServiceManager nacosServiceManager;

    public NacosServiceRegistry(NacosDiscoveryProperties nacosDiscoveryProperties) {
        this.nacosDiscoveryProperties = nacosDiscoveryProperties;
    }
}


register方法
public void register(Registration registration) {
        if (StringUtils.isEmpty(registration.getServiceId())) {
            log.warn("No service to register for nacos client...");
        } else {
            NamingService namingService = this.namingService();
            String serviceId = registration.getServiceId();
            String group = this.nacosDiscoveryProperties.getGroup();
            Instance instance = this.getNacosInstanceFromRegistration(registration);

            try {
                namingService.registerInstance(serviceId, group, instance);
                log.info("nacos registry, {} {} {}:{} register finished", new Object[]{group, serviceId, instance.getIp(), instance.getPort()});
            } catch (Exception var7) {
                log.error("nacos registry, {} register failed...{},", new Object[]{serviceId, registration.toString(), var7});
                ReflectionUtils.rethrowRuntimeException(var7);
            }

        }
    }

Nacos提供的Nacos Console可以完成各种运维操作。核心模块Nacos Server由以下4部分组成:

  1. Open API暴露各种操作,客户端可以调用Open API完成各项事宜。
  2. Config Service和Naming Service分别对应配置功能及服务注册/发现功能。
  3. Nacos Core是整个Nacos Server的核心,上层的Config Service和Naming Service底层都依赖Nacos Core。
  4. Nacos Core底层提供一致性协议算法。

Nacos Service Discovery 和 Spring Web

基于Properties创建实例

java
public static ConfigService createConfigService(Properties properties) throws NacosException {
    try {
        Class<?> driverImplClass = Class.forName("com.alibaba.nacos.client.config.NacosConfigService");
        Constructor constructor = driverImplClass.getConstructor(Properties.class);
        ConfigService vendorImpl = (ConfigService)constructor.newInstance(properties);
        return vendorImpl;
    } catch (Throwable var4) {
        throw new NacosException(-400, var4);
    }
}

Nacos

注册中心 Eureka、Nacos、Consul、Etcd、Zookeeper、。。。

为什么需要?

image-20221230214410459

image-20221231143531701

是什么

https://nacos.io/zh-cn/docs/what-is-nacos.html Nacos是一个为云原生应用提供的服务动态发现、配置管理、服务管理的平台。

它是诞生在阿里巴巴内部的一个项目,经历了多次双十一的挑战,在应用扩展性和大规模服务治理上起到了非常大的作用。 直到2018年开始开源,从开源到现在已经了很多个版本的迭代,目前最新版本是2.1.1

2.x版本在性能上和扩展性上做了非常大的改变,它的核心目标也是拥抱云原生生态。

Nacos整体架构

image-20221230214527211

整个架构还是非常清晰,Nacos提供了两个很核心的服务

  • Config Service,配置中心
  • Naming Service, 命名服务,也就是服务注册与发现

接着就是Nacos Core,也是Nacos的核心实现部分。

最底层是Consistency Protocol,数据一致性协议,用来实现Nacos集群数据同步。 对外,使用Http协议,提供OpenAPI,客户端程序可以通过该API来完成数据的交互和处理

Nacos2.x新特性

Nacos2.x版本主要就引入了两个新特性

  • 引入了gRPC协议的支持,gRPC协议在性能上要远远比Nacos1.x中的 http协议要好。
  • 增加了安全机制,如鉴权、加密

但是这两种升级对我们的日常影响不是很大,主要影响的点在于Nacos2.x的客户端无法兼容Nacos1.x的服务端。

但是Nacos2.x服务端是完全兼容1.x的客户端的。

升级2.0服务端

Nacos安装部署

Nacos是一个独立的中间件,需要单独下载安装才可使用,Nacos支持三种集群模式

  • 单机模式 - 用于测试和单机试用。

  • 集群模式 - 用于生产环境,确保高可用。

  • 多集群模式 - 用于多数据中心场景。

单机环境安装

本次分享的Nacos版本为:2.1.1。 这也是目前Spring Cloud Alibaba生态中最新的release版本 Spring Boot版本:2.6.12 Nacos

  1. 通过这个地址https://github.com/alibaba/nacos/releases/tag/2.1.1下载

  2. 执行tar -zxvf nacos-server-2.1.1.tar.gz命令解压文件

  3. 执行sh startup.sh -m standalone,启动单个节点的nacos server

  4. 访问 http://localhost:8848/nacos 的Nacos Server的控制台。 默认登 录帐号密码:nacos/nacos。

集群环境安装

推荐用户把所有服务列表放到一个vip下面,然后挂到一个域名下面

http://ip1:port/openAPI 直连ip模式,机器挂则需要修改ip才可以使用。

http://SLB:port/openAPI 挂载SLB模式(内网SLB,不可暴露到公网,以免带来安全风险),直连SLB即可,下面挂server真实ip,可读性不好。 http://nacos.com:port/openAPI 域名 + SLB模式(内网SLB,不可暴露到公网,以免带来安全风险),可读性好,而且换ip方便,推荐模式

image-20221230214728228

搭建Nacos集群

  1. 准备好三台虚拟机,如果没有三台,那就直接复制三份,并修改端口
shell
    192.168.8.133:8848
    192.168.8.133:8849
    192.168.8.133:8850
  1. 修改这三个节点的持久化配置(Mysql持久化配置)

    properties
    spring.datasource.platform=mysql
    #### Count of DB:
    db.num=1

Connect URL of DB:

db.url.0=jdbc:mysql://192.168.8.132:3306/nacos? characterEncoding=utf8&connectTimeout=1000&socketTimeo ut=3000&autoReconnect=true&useUnicode=true&useSSL=fal se&serverTimezone=UTC db.user.0=root db.password.0=123456

3. 通过${NACOS_HOME}\conf\cluster.conf.example示例文件,复制一份cluster.conf文件出来

4. 把三个节点的ip和端口写在该配置文件中,这个文件需要每个Nacos
Server节点都修改。
```shell
192.168.8.133:8848
192.168.8.133:8849
192.168.8.133:8850
  1. 启动服务后,在Nacos-Server任意一个节点的控制台页面:集群管理菜单,可以看到如下内容,表示集群配置成功。

image-20221230214852428

  1. 客户端配置方式

    properties
    spring.cloud.nacos.config.server-
    addr=192.168.8.133:8848,192.168.8.133:8849,192.168.8.133:8850
  2. 测试方式,访问其中一个节点,更新一个配置,同步看其他节点的配置 变化。

Nacos注册中心的使用

如果应用程序需要使用Nacos作为注册中心,那么至少要提供下面这些接口。

  • 服务注册接口,应用程序启动以后把服务地址注册保存到注册中心
  • 服务地址获取接口,获取指定服务的所有地址信息
  • 服务地址变更监听接口,实现服务变更的动态监听

服务注册接口和使用

java
void registerInstance(String serviceName, String ip, int port) throws
NacosException;
void registerInstance(String serviceName, String ip, int port, String
clusterName) throws NacosException;


void registerInstance(String serviceName, Instance instance) throws
NacosException;

其中: serviceName: 表示服务名称 ip: 表示服务实例的ip地址 port: 服务实例的端口 clusterName: 集群名 instance: 实例属性 https://github.com/nacos-group/nacos-spring-boot-project

Spring Boot集成Nacos实现服务注册的Demo

spring boot版本:2.6.12,Nacos spring boot版本:0.2.12(最新版) 引入Nacos依赖包

xml
<dependency>
    <groupId>com.alibaba.boot</groupId>
    <artifactId>nacos-discovery-spring-boot-starter</artifactId>
    <version>0.2.12</version>
</dependency>

配置Nacos的服务地址

nacos.discovery.server-addr=192.168.8.133:8848

编写Controller接口

java
@RestController
public class NacosController {
@NacosInjected
    private NamingService namingService;
    @GetMapping("/registry")
    public void registry() throws NacosException {
        Instance instance=new Instance();
        instance.setClusterName("Test1");  //集群名字
        instance.setEnabled(true);  //是否启用
      //临时节点/持久节点, 临时节点是AP模式(采用distro算法),持久节点是CP(采用raft算法)
        instance.setEphemeral(true);
        instance.setHealthy(true);  //健康状态
        instance.setIp("192.168.216.112"); //ip
        instance.setPort(8080); //端口
        instance.setWeight(4.0);  //权重   取值范围 1 到 100,数值越大,权重越大
         namingService.registerInstance("example",instance);

    }

注册成功以后,可以在Nacos的控制台看到注册上去的服务信息。

服务实例查找

当客户端需要获取目标服务的全部实例是,可以通过以下api来完成,它会 返回指定服务名字或者指定集群列表中的所有实例.

java
List<Instance> getAllInstances(String serviceName) throws
NacosException;

List<Instance> getAllInstances(String serviceName, List<String>
clusters) throws NacosException;

演示代码如下,增加下面的接口,根据指定服务名称获取实例列表

java
@RestController
public class NacosController {
    @NacosInjected
    private NamingService namingService;
    @GetMapping("/discovery")
    public List<Instance> get(@RequestParam String serviceName)
throws NacosException {
        return namingService.getAllInstances(serviceName);
}

    }
}

服务事件订阅

当服务提供者出现服务扩容和缩容时,服务消费者需要感知到服务地址的变化,可以通过监听服务来动态感知到服务的变化

java
void subscribe(String serviceName, EventListener listener) throws
NacosException;
void subscribe(String serviceName, List<String> clusters,
EventListener listener) throws NacosException;

其中,listener表示的是服务监听的事件回调。 Nacos还提供了取消监听的接口,定义如下。

java
void unsubscribe(String serviceName, EventListener listener)
throws NacosException;
void unsubscribe(String serviceName, List<String> clusters,
EventListener listener) throws NacosException;

了解了这些点之后,我们再去从源码层面分析Nacos的一些实现细节。当然,我们的分析过程,会基于Spring Cloud Alibaba来做。 增加如下接口,用来注册订阅事件,当我们调用registry发起注册、或者在nacos控制台对服务进行下线的时候,都会触发事件通知。

java
@GetMapping("/sub")
public void subscribe(@RequestParam String serviceName) throws
   NacosException {
  namingService.subscribe(serviceName, new EventListener() {
        @Override
        public void onEvent(Event event) {
            NamingEvent namingEvent=(NamingEvent)event;
            System.out.println("收到事
件:"+namingEvent.getServiceName());
        }
    });
}

Nacos中的Namespace和Group

在Nacos中提供了namespace和group命名空间和分组的机制,它是Nacos提供的一种数据模型。

namespace可以解决多环境以及多租户数据的隔离问题。比如在多套环境下,可以根据指定环境创建不同的namespace,实现多环境隔离。或者在多租户的场景中,每个用户可以维护自己的namespace,实现每个用户的配置数据和注册数据的隔离。 group是分组机制,它的维度是实现服务注册信息或者DataId的分组管理机制,对于group的用法,没有固定的规则,它也可以实现不同环境下的分组,也可以实现同一个应用下不同配置类型或者不同业务类型的分组。

官方建议是,namespace用来区分不同环境,group可以专注在业务层面的数据分组。实际上在使用过程中,最重要的是提前定要统一的口径和规定,避免不同的项目团队混用导致后期维护混乱的 问题。

Nacos中的Namespace和Group关系如下图所示,其中Namespace默认是public,Group默认是DEFAULT_GROUP。

image-20221230215535007

自定义Namespace

name用于进行租户粒度的配置隔离。不同的命名空间下,可以存在相同的Group 或 Data ID 的配置。Namespace 的常用场景之一是不同环境的配置的区分隔离,例如开发测试环境和生产环境的资源(如配置、服务)隔离等。

在没有明确指定配置的情况下, 默认使用的是 Nacos 上 Public 这个namespae。

如果需要使用自定义的命名空间,可以通过以下配置来实现:

1、在Nacos-Console中创建两个命名空间,分别是

image-20221230215604029

2、修改dubbo相关配置项目

properties
dubbo.registries.nacos-
registry.address=nacos://192.168.8.133:8848?
namespace=47201fb3-2b45-4199-87c5-298a748c9385

或者使用这种方式

properties
dubbo.registry.parameters.namespace=5cbb70a5-xxx-xxx-xxx-d43479ae0932

自定义Group

在没有明确指定配置的情况下, 默认使用的是 DEFAULT_GROUP 。如果需要自定义自己的 Group,可以通过以下配置来实现:

properties
dubbo.registries.nacos-
registry.address=nacos://192.168.8.133:8848?
namespace=47201fb3-2b45-4199-87c5-
298a748c9385&group=example

或者使用下面的方式

properties
dubbo.registry.parameters.group=example

Nacos的基本应用

服务端的配置存储 配置的CRUD 配置的动态感知

gRPC协议通信(protobuf+http2)

Dubbo没有集成Nacos作为动态配置中心。 Dubbo - Config Center (维护dubbo.properties 外部化配置) -> nacos Dubbo 集成Nacos做动态配置管理 ? Spring Boot + Dubbo + Nacos Spring Cloud , 注解实现动态感知? @RefeshScope @Value (Nacos服务器上的配置作为外部化配置)

Nacos的基本使用

前面分析了Nacos作为配置中心的基本功能,那么不难分析出,Nacos必然也会提供配置管理相关的API来完成功能的实现。 从远程服务器上获取配置 服务器上配置变更后的通知

从服务器获取配置

用于服务启动的时候从 Nacos 获取配置。

java
public String getConfig(String dataId, String group, long
timeoutMs) throws NacosException

1、dataId string 配置 ID,采用类似 package.class(如com.taobao.tc.refund.log.level)的命名规则保证全局唯一性,class 部分建议是配置的业务含义。全部字符小写。只允许英文字符和 4 种特殊字符("."、":"、"-"、"_"),不超过 256 字

2、group string (Nacos:Test)保证唯一性,只允许英文字符和4种特殊字符("."、":"、"-"、"_"),不超过128字节 配置分组,建议填写产品名:模块名

3、timeout long 读取配置超时时间,单位 ms,推荐值 3000。

引入jar包依赖

xml
<dependency>
    <groupId>com.alibaba.nacos</groupId>
    <artifactId>nacos-client</artifactId>
    <version>2.1.1</version>
</dependency>
<dependency>
</dependency>
<dependency>
    <groupId>com.alibaba.nacos</groupId>
    <artifactId>nacos-api</artifactId>
    <version>2.1.1</version>
    <groupId>com.alibaba.nacos</groupId>
    <artifactId>nacos-common</artifactId>
    <version>2.1.1</version>
</dependency>

编写配置获取代码

java
public class NacosExample {
    public static void main(String[] args) {
        String serverAddr="192.168.8.133:8848";
        String dataId="example";
        String groupId="DEFAULT_GROUP";
        Properties properties=new Properties();
        properties.put("serverAddr",serverAddr);
        try {
            //通过nacosfactory创建一个配置中心的服务
            ConfigService configService=
NacosFactory.createConfigService(properties);
            // 5000表示读取配置的超时时间,如果超时或者出现网络
content=configService.getConfig(dataId,groupId,3000);
故障,会抛出NacosException的异常
            String
            System.out.println(content);
        } catch (NacosException e) {
            e.printStackTrace();
        }
    }
}

上述代码的含义是, 使用NacosFactory构建ConfigService 通过getConfig来获得配置,查询指定groupId下对应的dataId这个数 配置变更订阅

java
 public class NacosMain {
    public static void main(String[] args) {
        String serverAddr="192.168.8.133:8848";
        String dataId="example";
        String groupId="DEFAULT_GROUP";
        Properties properties=new Properties();
        properties.put("serverAddr",serverAddr);
        try {
            //通过nacosfactory创建一个配置中心的服务
            ConfigService configService=
NacosFactory.createConfigService(properties);
            // 5000表示读取配置的超时时间,如果超时或者出现网络故
障,会抛出NacosException的异常
            String
content=configService.getConfig(dataId,groupId,3000);
            configService.addListener(dataId, groupId, new Listener() {
                @Override
                public Executor getExecutor() {
                    return null;
                }
            @Override
           public void receiveConfigInfo(String s) {
             System.out.println("收到配置变更通知:"+s);
                }
        });
        System.out.println(content);
        System.in.read(); //保证程序不执行结束
    } catch (NacosException | IOException e) {
        e.printStackTrace();
    }
}
}

配置 SpringBoot集成Nacos实现动态

下面演示Spring Boot集成Nacos实现动态配置获取 添加jar包依赖

xml
<dependency>
    <groupId>com.alibaba.boot</groupId>
    <artifactId>nacos-config-spring-boot-starter</artifactId>
    <version>0.2.12</version>
</dependency>

注意:版本 0.2.x.RELEASE 对应的是 Spring Boot 2.x 版本,版本 0.1.x.RELEASE 对应的是 Spring Boot 1.x 版本。 编写测试案例 在main方法中增加注解

java
@NacosPropertySource(dataId="example",autoRefreshed = true)
@SpringBootApplication
public class NacosDemoApplication {
    public static void main(String[] args) {
        SpringApplication.run(NacosDemoApplication.class, args);

    }
}

使用 @NacosPropertySource 加载 dataId 为 example 的配置源,并开启自动更新 增加一个controller作为测试

java
@RestController
public class NacosController {
true)
    private String txt;
    @GetMapping("/")
    public String get(){
        return txt;
    }
}

@NacosValue(value = "${txt:default-msg}",autoRefreshed = )