Nacos

Nacos(Dynamic Naming and Cofiguration Service):一个更易于构建云原生应用的动态服务发现、配置管理和服务管理平台。

Nacos = 注册中心 + 配置中心 = Eureka + Config + Bus

安装部署

从Github上下载Nacos的发行包:nacos-server-1.4.1.zip

需要的系统环境:

  • 64位系统
  • JDK8以上
  • Maven 3.3以上

修改启动的startup.cmd文件:

默认的启动模式为集群模式,可以使用命令startup.cmd -m standalone进入单机模式,也可以修改cmd文件中的MODE为单机模式。

  1. rem .....
  2. rem 修改启动的模式为单机模式,默认为集群模式
  3. rem set MODE="cluster"
  4. set MODE="standalone"
  5. rem .........

执行startup.cmd启动nacos,浏览器访问:http://localhost:8848/nacos

用户名密码默认为:nacos/nacos

nacos 1.2.0以后,打开web页面直接就已经登录了nacos账户

服务注册

不同注册中心的对比

不同注册中心对比:

服务注册与发现框架 CAP 控制台管理 社区活跃度
Eureka AP 支持
ZooKeeper CP 不支持
Consul CP 支持
Nacos AP + CP
(一般使用AP)
支持

Nacos既支持AP模型,又支持CP模型,可以进行切换:

C是所有节点在同一时间看到的数据是一致的;

A是所有的请求都会收到响应。

模式如何选择:

一般来说,如果不需要存储服务级别的信息且服务实例是通过nacos-client注册,并能够保持心跳上报,那么就可以选择AP模式。当前主流的服务如 Spring Cloud、Dubbo服务,都适用于AP模式,AP模式为了服务的可能性而减弱了一致性,因此AP模式下只支持注册临时实例。

如果需要在服务级别编辑或者存储配置信息,那么CP是必须,K8S服务和DNS服务则适用于CP模式。CP模式下支持注册持久化实例,此时是以Raft协议为集群运行模式,该模式下注册实例之前必须先注册服务,如果服务不存在则会返回错误

nacos切换模式为CP:

curl -X PUT '$NACOS_SERVER:8848/nacos/v1/ns/operator/switches?entry=serverMode&value=CP'

临时实例:

Eureka、Zookeeper

  • 客户端上报健康状态
  • 摘除不健康实例
  • 非持久化

持久化实例:

Consul、CoreDNS

  • 服务端探测健康状态
  • 保留不健康实例
  • 持久化 | | Nacos | Eureka | Consul | CoreDNS | Zookeeper | | —- | —- | —- | —- | —- | —- | | 一致性协议 | CP + AP | AP | CP | / | CP | | 健康检查 | TCP/HTTP/MySQL/Client_Beat | Client_Beat | TCP/HTTP/gRPC/Cmd | / | Client_Beat | | 负载均衡 | 权重/DSL/metadata/CMDB | Ribbon | Fabio | RR | / | | 雪崩保护 | 支持 | 支持 | 不支持 | 不支持 | 不支持 | | 自动注销实例 | 支持 | 支持 | 不支持 | 不支持 | 支持 | | 访问协议 | HTTP/DNS/UDP | HTTP | HTTP/DNS | DNS | TCP | | 监听支持 | 支持 | 支持 | 支持 | 不支持 | 支持 | | 多数据中心 | 支持 | 支持 | 支持 | 不支持 | 不支持 | | 跨注册中心 | 支持 | 不支持 | 支持 | 不支持 | 不支持 | | SpringCloud集成 | 支持 | 支持 | 支持 | 不支持 | 不支持 | | Dubbo集成 | 支持 | 不支持 | 不支持 | 不支持 | 支持 | | K8s继承 | 支持 | 不支持 | 支持 | 支持 | 不支持 |

服务提供者注册示例

  1. 在父pom中引入spring cloud alibaba的依赖管理
    <dependencies>
     <dependency>
         <groupId>com.alibaba.cloud</groupId>
         <artifactId>spring-cloud-alibaba-dependencies</artifactId>
         <version>2.2.2.RELEASE</version>
         <type>pom</type>
     </dependency>
    </dependencies>
    
  1. 创建模块,在模块pom中加入 nacos服务发现的依赖
    <dependencies>
     <!-- nacos服务发现依赖 -->
     <dependency>
         <groupId>com.alibaba.cloud</groupId>
         <artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
     </dependency>
     <dependency>
         <groupId>org.springframework.boot</groupId>
         <artifactId>spring-boot-starter-web</artifactId>
     </dependency>
     <dependency>
         <groupId>org.springframework.boot</groupId>
         <artifactId>spring-boot-starter-actuator</artifactId>
     </dependency>
    </dependencies>
    
  1. 在application.yml配置文件中配置 nacos服务的地址
    server:
    port: 9002
    spring:
    application:
     name: nacos-payment-provider
    cloud:
     nacos:
       discovery:
         server-addr: localhost:8848 # Nacos地址
    management:
    endpoints:
     web:
       exposure:
         include: '*'
    
  1. 在主启动类加上服务发现注解
    @SpringBootApplication
    @EnableDiscoveryClient
    public class PaymentMain9001 {
     public static void main(String[] args) {
         SpringApplication.run(PaymentMain9001.class, args);
     }
    }
    
  1. 编写示例controller

    @RestController
    public class PaymentController {
    
     @Value("${server.port}")
     private String serverPort;
    
     @GetMapping("/payment/nacos/{id}")
     public String getPayment(@PathVariable("id") Integer id) {
         return "nacos registry, serverPort: " + serverPort + "\t id: " + id;
     }
    }
    
  1. 启动项目后,在 nacos的web控制台服务列表中即可看到该服务nacos-payment-provider

    service名称通过 spring.cloud.nacos.discovery.service配置,如果未配置该属性,则取 spring.application.name

nacos集成了ribbon,自带负载均衡。

服务消费者示例

  1. 创建模块,在模块pom中加入nacos的服务发现依赖
    <dependencies>
     <!-- nacos服务发现依赖 -->
     <dependency>
         <groupId>com.alibaba.cloud</groupId>
         <artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
     </dependency>
     <dependency>
         <groupId>org.springframework.boot</groupId>
         <artifactId>spring-boot-starter-web</artifactId>
     </dependency>
     <dependency>
         <groupId>org.springframework.boot</groupId>
         <artifactId>spring-boot-starter-actuator</artifactId>
     </dependency>
    </dependencies>
    
  1. 修改application.yml配置文件
    server:
    port: 83
    spring:
    application:
     name: nacos-order-consumer
    cloud:
     nacos:
       discovery:
         server-addr: localhost:8848
    # 自定义的变量,消费者将要去访问的微服务名称
    service-url:
    nacos-user-service: http://nacos-payment-provider
    
  1. 因为nacos中加入了ribbon,所以需要创建配置类,在restTemplate中加上@LoadBalance负载均衡注解

    @Configuration
    public class ApplicationContextConfig {
    
     @Bean
     @LoadBalanced
     public RestTemplate getRestTemplate() {
         return new RestTemplate();
     }
    }
    
  1. 编写主启动类,加上服务发现注解
    @SpringBootApplication
    @EnableDiscoveryClient
    public class OrderConsumer83 {
     public static void main(String[] args) {
         SpringApplication.run(OrderConsumer83.class, args);
     }
    }
    
  1. 编写示例controller

    @RestController
    @Slf4j
    public class OrderNacosController {
    
     @Autowired
     private RestTemplate restTemplate;
    
     @Value("${service-url.nacos-user-service}")
     private String serviceURL;
    
     @GetMapping("/consumer/payment/nacos/{id}")
     public String paymentInfo(@PathVariable("id") Integer id) {
         return restTemplate.getForObject(serviceURL + "/payment/nacos/" + id, String.class);
     }
    }
    

配置中心

使用配置中心的配置

  1. 创建项目,在pom中加入nacos的服务发现依赖、配置中心依赖
    <dependencies>
     <!-- nacos注册中心依赖 -->
     <dependency>
         <groupId>com.alibaba.cloud</groupId>
         <artifactId>spring-cloud-starter-alibaba-nacos-config</artifactId>
     </dependency>
     <!-- nacos服务发现依赖 -->
     <dependency>
         <groupId>com.alibaba.cloud</groupId>
         <artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
     </dependency>
     <dependency>
         <groupId>org.springframework.boot</groupId>
         <artifactId>spring-boot-starter-web</artifactId>
     </dependency>
     <dependency>
         <groupId>org.springframework.boot</groupId>
         <artifactId>spring-boot-starter-actuator</artifactId>
     </dependency>
    </dependencies>
    
  1. 创建配置文件:bootstrap.yml和application.yml

    nacos同springcloud-config一样,在项目初始化时,要保证先从配置中心进行配置拉取。拉取配置之后,才能保证项目的正常启动。

springboot中配置文件的加载是存在优先级顺序的,bootstrap优先级高于application。


bootstrap.yml

server:
  port: 3377
spring:
  application:
    name: nacos-config-client  # spring.application.name作为dataId的一部分
  cloud:
    nacos:
      discovery:
        server-addr: localhost:8848 # nacos服务注册中心地址
      config:
        server-addr: localhost:8848 # nacos配置中心地址
        file-extension: yaml  # 指定yaml格式的配置


application.yml

spring:
  profiles:
    active: dev
  1. 编写主启动类,添加服务发现注解
    @SpringBootApplication
    @EnableDiscoveryClient
    public class NacosConfigClientMain3377 {
     public static void main(String[] args) {
         SpringApplication.run(NacosConfigClientMain3377.class, args);
     }
    }
    
  1. 编写controller示例

    @RestController
    @RefreshScope  // 通过Spring Cloud的 @RefreshScope注解实现配置的自动更新
    public class ConfigClientController {
    
     @Value("${config.info}")
     private String configInfo;
    
     @GetMapping("/config/info")
     public String getConfigInfo() {
         return configInfo;
     }
    }
    

配置中心的配置

在 Nacos Spring Cloud中,dataId的完整格式如下:

${prefix}-${spring.profiles.active}.${file-extension}
  • prefix:默认为spring.application.name的值。也可以通过配置项spring.cloud.nacos.config.prefix进行配置
  • spring.profiles.active:当前环境对应的profile。当spring.profiles.active为空时,对应的连接符-也将不存在,即dataId的格式变为 ${prefix}.${file-extension}
  • file-extension:配置内容的数据格式,可以通过配置项 spring.cloud.nacos.config.file-extension来配置,只支持 propertiesyaml类型

例如上面的示例项目中:

spring.application.name为:nacos-config-client

spring.profiles.active为:dev

spring.cloud.nacos.config.file-extension为:yaml。

则需要在nacos中的配置管理—>配置列表中添加配置:

DataId:nacos-config-client-dev.yaml

配置内容中填入相关配置,例如:

config:
  info: nacos config center, version = 1

发布后,访问示例程序的controller中即可读到 config.info的值。

在nacos中修改config info的值,点击发布后, 项目不需要重启即可直接获取到最新的配置信息。

分类配置

Nacos支持基于Namespace和Group的配置分组管理,以便用户更灵活的根据自己的需要按照环境或者应用、模块等分组管理微服务以及Spring的大量配置,在配置管理中主要提供了配置历史版本、回滚、订阅者查询等核心管理能力。

Nacos 基于 Namespace(命名空间)帮助用户逻辑隔离多个命名空间,帮助用户更好的管理测试、预发、生产等多环境服务和配置,让每个环境的统一配置(如数据库数据源)可以定义不同的值。

Group和DataId逻辑上区分两个目标对象。Group可以把不同的微服务划分到同一个分组中。

Service就是微服务,一个service可有包含多个Cluster(集群),Nacos默认Cluster是DEFAULT。

Cluster是对指定微服务的一个虚拟划分。比如为了容灾,将Service分别部署在杭州机房和广州机房,就可以给杭州机房的Service起一个集群名称(HZ),给广州的Service起一个集群名称(GZ),还可以尽量让同一个机房的微服务互相调用以提升性能。

Instance就是微服务实例。

默认情况:Namespace=public,Group=DEFAULT_GROUP,默认Cluster是DEFAULT

DataID方案:

指定spring.profile.active和配置文件的DataID来使不同环境下读取不同的配置。

默认空间、默认分组情况下,创建dev和test两个DataID。

Group方案:

通过Group实现环境区分。

示例:

在Nacos的配置列表中添加两个配置:

DataID= nacos-config-client-info.yaml ,Group=DEV_GROUP

DataID= nacos-config-client-info.yaml ,Group=TEST_GROUP

在项目的bootstrap.yml中加group的配置:

spring:
  cloud:
    nacos:
      config:
        group: DEV_GROUP  # 配置中心的配置分组

修改application.yaml中的环境配置:

spring:
  profiles:
    active: info  # DataID中的环境配置

Namespace方案:

创建dev、test的Namespace,通过Namespace实现环境区分。

示例:

在Nacos的命名空间中创建两个命名空间:dev、test。

Nacos1.4之后,命名空间的ID可以自定义。之前版本的命名空间ID是自动生成的一串UUID。

到配置列表中,在对应的命名空间下添加对应的配置。

在项目的bootstrap.yml中加入命名空间的配置:

spring:
  cloud:
    nacos:
      config:
        namespace: test  # 此处配置的是命名空间的ID,在旧版本的Nacos中,该ID为一串UUID

Namespace、Group、DataID可以搭配一起使用。

集群和持久化配置

架构:

默认Nacos使用嵌入式derby数据库实现数据的存储,所以如果启动多个默认配置下的Nacos节点,数据存储是存在一致性问题的。为了解决这个问题,Nacos采用了集中式存储的方式来支持集群化部署,目前只支持MySQL的存储。

Nacos支持三种部署模式:

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

创建Mysql环境:

使用docker启动Mysql:

# 从docker官方镜像库拉取最新的 MySQL 8.0.24 的镜像
docker pull mysql
# 将镜像运行为一个容器
docker run -d -p 3306:3306 -e MYSQL_DATABASE=nacos -e MYSQL_ROOT_PASSWORD=nacos --name mysql_nacos mysql

Dbeaver连接 Mysql 8.0.24 技巧:

操作系统选择:Platform Independent,下载对应的压缩包

跳转到登录页面后,点击下方的 No thanks, just start my download

  • Public Key Retrieval is not allowed报错:

    如果是URL访问,则在URL后面加上参数:allowPublicKeyRetrieval=true

使用Dbeaver访问,则在驱动属性中将 allowPublicKeyRetrieval设置为true

Nacos使用MySQL数据库做持久化:

将 conf/nacos-mysql.sql 在 mysql数据库中执行一下。

修改配置文件:conf/application.properties

放开注释并修改配置文件中的Mysql数据库配置

### If use MySQL as datasource:
spring.datasource.platform=mysql

### Count of DB:
db.num=1

### Connect URL of DB:
db.url.0=jdbc:mysql://192.168.29.131:3306/nacos?characterEncoding=utf8&connectTimeout=1000&socketTimeout=3000&autoReconnect=true&useUnicode=true&useSSL=false&serverTimezone=UTC
db.user.0=root
db.password.0=nacos

nacos 1.4.1支持 MySQL 8+。

如果使用的低版本的nacos,需要更换nacos依赖的mysql驱动版本。

集群配置:

在Linux服务器上安装 JDK8,并配置环境变量:

rpm -ivh jdk-8u181-linux-x64.rpm

安装后的 JDK 位于:/usr/java/jdk1.8.0_181-amd64。

将nacos-server-1.4.1.tar.gz上传到服务器并解压:

tar -zxvf nacos-server-1.4.1.tar.gz

修改conf/application.properties中数据库的配置,修改为使用Mysql数据库。

将conf/cluster.conf.example 复制出一份 conf/cluster.conf,里面编写每个nacos集群节点的IP和端口号:

端口号为8848时,可以只写IP不写端口号

# 192.168.29.142:3333
# 192.168.29.142:4444
# 192.168.29.142:5555
# 一台服务器启动多个nacos比较占用内存,使用三台服务器进行启动

192.168.29.142:3333
192.168.29.143  # 端口号为8848时可以省略
192.168.29.144

此处使用单机的不同端口模拟集群环境。编辑Nacos的启动脚本 startup.sh,使它能够接受不同的启动端口:

希望达到的目标:startup.sh -o 指定端口号 即可指定端口号启动nacos

# .........

export PORT=8848  # 声明一个PORT变量
while getopts ":m:f:s:c:p:o:" opt   # 此处加上一个参数o
do
    case $opt in
        m)
            MODE=$OPTARG;;
        f)
            FUNCTION_MODE=$OPTARG;;
        s)
            SERVER=$OPTARG;;
        c)
            MEMBER_LIST=$OPTARG;;
        p)
            EMBEDDED_STORAGE=$OPTARG;;
        o)
            PORT=$OPTARG;;   # 将参数o的值赋给 PORT 变量
        ?)
        echo "Unknown parameter"
        exit 1;;
    esac
done

# .........
JAVA_OPT="${JAVA_OPT} -Dserver.port=${PORT}"

配置143服务器的Nginx负载均衡:

/usr/local/nginx/conf/nginx.conf

upstream nacoscluster {
    server 192.168.29.142:3333;  # 142服务器上的Nacos
    server 192.168.29.143:8848;  # 143服务器上的Nacos
    server 192.168.29.144:8848;  # 144服务器上的Nacos
}

server {
    listen       8888;  # Nginx的端口
    server_name  localhost;

    location / {
        #root   html;
        #index  index.html index.htm;
        proxy_pass http://nacoscluster;
    }

    # ......
}

启动nginx后,在浏览器访问:http://192.168.29.143:8888/nacos 即可访问Nacos;

项目中原来配置Nacos路径的位置,改为Nginx的代理路径

server:
  port: 9003
spring:
  application:
    name: nacos-payment-provider
  cloud:
    nacos:
      discovery:
        server-addr: 192.168.29.143:8888  # nginx地址
#        server-addr: localhost:8848 # Nacos地址
management:
  endpoints:
    web:
      exposure:
        include: '*'