一、简介

1、概述

Nacos(Dynamic Naming and Configuration Service)是一个开源的动态服务发现、配置管理和服务管理平台。

2、下载

下载nacos-server-xxx.zip后解压。

3、运行

切换到bin目录,执行以下命令(standalone代表着单机模式运行,非集群模式):

startup.cmd -m standalone

访问http://localhost:8848/nacos即可打开Nacos管理控制台:

二、核心功能

1、命名空间

Nacos的命名空间(Namespace)可以实现多环境(如开发、测试、生产)或多租户的隔离,可以为不同的环境创建不同的命名空间,实现配置和服务的完全隔离。

Nacos通过Namespace、Group、Data ID​等元素来唯一确定一个配置或服务,不同的命名空间下,可以存在相同的Group 或 Data ID的配置。

2、服务发现与注册

服务提供者(Provider)在启动时,会向Nacos服务器注册自己的服务信息(例如IP、端口、服务名等);服务消费者(Consumer)通过Nacos查询并获取可用的服务实例列表,从而实现服务间的发现与调用。

  • 服务注册
curl -X POST "http://127.0.0.1:8848/nacos/v1/ns/instance?serviceName=nacos.naming.serviceName&ip=20.18.7.10&port=8080"

  • 服务发现
curl -X GET "http://127.0.0.1:8848/nacos/v1/ns/instance/list?serviceName=nacos.naming.serviceName"
  • Java样例
package demo;

import java.util.Properties;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import com.alibaba.nacos.api.NacosFactory;
import com.alibaba.nacos.api.PropertyKeyConst;
import com.alibaba.nacos.api.naming.NamingService;
import com.alibaba.nacos.api.naming.listener.Event;
import com.alibaba.nacos.api.naming.listener.EventListener;
import com.alibaba.nacos.api.naming.listener.NamingEvent;

public class NacosNamingExample {

	public static void main(String[] args) throws Exception{
		Logger log = LoggerFactory.getLogger(NacosNamingExample.class);
		
		Properties properties = new Properties();
		//Nacos地址
		properties.put(PropertyKeyConst.SERVER_ADDR, "localhost:8848");
		//指定命名空间
		properties.put(PropertyKeyConst.NAMESPACE, "dev");
		
		NamingService namingService = NacosFactory.createNamingService(properties);
		
		//注册服务
		namingService.registerInstance("testService", "10.0.0.1", 8080);
		
		wait2Sync();
		
		//订阅服务
		namingService.subscribe("testService", new EventListener() {
			@Override
			public void onEvent(Event event) {
				if(event instanceof NamingEvent) {
					NamingEvent namingEvent = (NamingEvent) event;
					log.info("serviceName: {}", namingEvent.getServiceName());
					log.info("instances: {}", namingEvent.getInstances());
				}
			}
		});
		
		namingService.registerInstance("testService", "10.0.0.2", 8080);;
		
		wait2Sync();
	}
	
	/**
	 * 等待同步nacos
	 */
	private static void wait2Sync() {
        try {
            Thread.sleep(3000);
        } catch (InterruptedException e) {
        	e.printStackTrace();
        }
    }
}

输出:

serviceName: testService
instances: [Instance{instanceId='10.0.0.1#8080#DEFAULT#DEFAULT_GROUP@@testService', ip='10.0.0.1', port=8080, weight=1.0, healthy=true, enabled=true, ephemeral=true, clusterName='DEFAULT', serviceName='DEFAULT_GROUP@@testService', metadata={}}]
serviceName: testService
instances: [Instance{instanceId='10.0.0.2#8080#DEFAULT#DEFAULT_GROUP@@testService', ip='10.0.0.2', port=8080, weight=1.0, healthy=true, enabled=true, ephemeral=true, clusterName='DEFAULT', serviceName='DEFAULT_GROUP@@testService', metadata={}}]

3、动态配置管理

Nacos提供了一个中心化的配置服务器,可以通过Nacos控制台统一管理所有环境的配置信息。当配置需要变更时,无需重启应用,Nacos就能将变更的配置实时推送到客户端并生效,极大减少了系统停机时间,为实现灰度发布、版本控制等高级特性提供了基础。

  • 发布配置
curl -X POST "http://127.0.0.1:8848/nacos/v1/cs/configs?dataId=nacos.cfg.dataId&group=test&content=HelloWorld"
  • 获取配置
curl -X GET "http://127.0.0.1:8848/nacos/v1/cs/configs?dataId=nacos.cfg.dataId&group=test"
  • Java样例
package demo;

import java.util.Properties;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import com.alibaba.nacos.api.NacosFactory;
import com.alibaba.nacos.api.PropertyKeyConst;
import com.alibaba.nacos.api.config.ConfigService;
import com.alibaba.nacos.api.config.listener.AbstractListener;
import com.alibaba.nacos.client.config.listener.impl.PropertiesListener;

public class NacosConfigExample {

	public static void main(String[] args) throws Exception{
		Logger log = LoggerFactory.getLogger(NacosConfigExample.class);
		
		Properties properties = new Properties();
		//Nacos地址
		properties.put(PropertyKeyConst.SERVER_ADDR, "127.0.0.1:8848");
		
		ConfigService configService = NacosFactory.createConfigService(properties);
		
		String group = "testGroup";
		String dataId = "testDataId";
		String content = "connectTimeoutInMills=5000";
		
		//监听配置
		configService.addListener(dataId, group, new PropertiesListener() {
			@Override
			public void innerReceive(Properties properties) {
				//Properties类型
				log.info("receive: {}", properties);
			}
		});
		
		configService.addListener(dataId, group, new AbstractListener() {
			@Override
			public void receiveConfigInfo(String configInfo) {
				//其他类型配置,例如json、yaml或pojo对象,可按需做反序列化
				log.info("receive(String): {}", configInfo);
			}
		});
		
		//发布配置
		boolean result = configService.publishConfig(dataId, group, content);
		log.info("publishConfig: {}", result);
		
		wait2Sync();
		
		//查询配置
		String config = configService.getConfig(dataId, group, 3000);
		log.info("getConfig: {}", config);
		
		//更新配置
		String newContent = "connectTimeoutInMills=3000";
		boolean updateResult = configService.publishConfig(dataId, group, newContent);
		log.info("updateConfig: {}", updateResult);
		
		wait2Sync();
		
		//删除配置
		boolean removeResult = configService.removeConfig(dataId, group);
		log.info("removeConfig: {}", removeResult);
		
		wait2Sync();
		
		config = configService.getConfig(dataId, group, 3000);
		log.info("getConfig: {}", config);
	}
	
	/**
	 * 等待同步nacos
	 */
	private static void wait2Sync() {
        try {
            Thread.sleep(3000);
        } catch (InterruptedException e) {
        	e.printStackTrace();
        }
    }
}

输出:

publishConfig: true
receive: {connectTimeoutInMills=5000}
receive(String): connectTimeoutInMills=5000
getConfig: connectTimeoutInMills=5000
updateConfig: true
receive: {connectTimeoutInMills=3000}
receive(String): connectTimeoutInMills=3000
removeConfig: true
receive(String): null
getConfig: null

三、部署模式

1、单机

  • 启动

执行以下命令:

startup.cmd -m standalone

或修改startup.cmd中的MODE变量后直接运行startup.cmd

set MODE="standalone"
  • 使用MySQL

    单机模式时默认使用内置数据库Derby实现数据的存储,可以通过配置使用MySQL(5.6.5+)作为数据存储,步骤如下:

    • 初始化nacos数据库

    执行conf\mysql-schema.sql文件完成初始化。

    • 配置数据源

    修改conf\application.properties文件,增加数据库配置:

      spring.sql.init.platform=mysql
    	
      db.num=1
    
      db.url.0=jdbc:mysql://127.0.0.1:3306/nacos?characterEncoding=utf8&connectTimeout=1000&socketTimeout=3000&autoReconnect=true&useUnicode=true&useSSL=false&serverTimezone=UTC
      db.user.0=nacos
      db.password.0=nacos
    

2、集群

集群模式通常用于生产环境,确保高可用;需要3个或3个以上Nacos节点才能构成集群。

步骤如下:

  • 配置集群

conf\cluster.conf中配置节点信息:

200.8.9.16:8848
200.8.9.17:8848
200.8.9.18:8848
  • 开启默认鉴权插件(可选)

Nacos是一个内部微服务组件,需要在可信的内部网络中运行,不可暴露在公网环境,防止带来安全风险。

Nacos提供简单的鉴权实现,为防止业务错用的弱鉴权体系,不是防止恶意攻击的强鉴权体系。

如果运行在不可信的网络环境或者有强鉴权诉求,可以参考鉴权插件实现自定义鉴权。

可以通过如下步骤开启默认鉴权:

修改conf\application.properties文件,增加鉴权配置:

nacos.core.auth.enabled=true
nacos.core.auth.system.type=nacos
#base64字符串
nacos.core.auth.plugin.nacos.token.secret.key=${自定义,保证所有节点一致}
nacos.core.auth.server.identity.key=${自定义,保证所有节点一致}
nacos.core.auth.server.identity.value=${自定义,保证所有节点一致}
  • 配置MySQL数据源

    • 执行conf\mysql-schema.sql文件完成初始化

    • 修改conf\application.properties配置文件

      spring.sql.init.platform=mysql
    	
      db.num=1
    
      db.url.0=jdbc:mysql://127.0.0.1:3306/nacos?characterEncoding=utf8&connectTimeout=1000&socketTimeout=3000&autoReconnect=true&useUnicode=true&useSSL=false&serverTimezone=UTC
      db.user.0=nacos
      db.password.0=nacos
    

启动三个节点后,访问http://localhost:8848/nacos/,需要登录:

输入nacos/nacos即可登录成功。

登录后在节点列表中可以看到Nacos节点信息(此处使用同一机器不同端口启动多个Nacos服务):

四、其他

  • 配置鉴权插件后客户端连接时需传递认证信息
properties.setProperty(PropertyKeyConst.USERNAME, "nacos");
properties.setProperty(PropertyKeyConst.PASSWORD, "nacos");
  • 自定义用户信息

使用BCrypt加密密码:

//创建BCrypt加密器实例
BCryptPasswordEncoder encoder = new BCryptPasswordEncoder();

//明文密码
String rawPassword = "hello";

//加密密码
String encodedPassword = encoder.encode(rawPassword);
System.out.println("BCrypt加密后的密码: " + encodedPassword);

然后在users和roles表中插入相关信息:

INSERT INTO users (username, password, enabled) VALUES ('xxx', '$2a$10$gUteC5T1metlzuBytrAdyeudXmZ4N025MwIt77Z.sgm7bj6311962', TRUE);

INSERT INTO roles (username, role) VALUES ('xxx', 'ROLE_ADMIN');

之后就可以使用此用户密码登录。

参考资料