文章目录
什么是 Nacos
-
Nacos 是服务注册中心
,是阿里巴巴推出来的一个新开源项目,是致力于帮助我们
发现、配置和管理微服务的
。 -
Nacos 提供了一组简单易用的特性集,帮助快速实现
动态服务发现、服务配置、服务元数据及流量管理。
-
Nacos 是构建以“
服务
”为中心的现代应用架构 (例如微服务范式、云原生范式) 的服务基础设施。
相对于 Spring Cloud Eureka 来说,Nacos 更强大。
Nacos = Spring Cloud Eureka + Spring Cloud Config
-
通俗来说,Nacos是一个注册中心& 配置中心
——
作为注册中心,Nacos可用于替代Spring Cloud中的Eureka、Spring Cloud Zookeeper Discovery、Spring Cloud Consul Discovery
;
作为配置中心,Nacos可用于替代Spring Cloud Config、Spring Cloud Zookeper Config、Spring Cloud Consul Config。
-
Nacos 可以与 Spring, Spring Boot, Spring Cloud 集成,并能代替 Spring Cloud Eureka, Spring Cloud Config
-
通过
Nacos Serve
r 和
spring-cloud-starter-alibaba-nacos-config
实现
配置的动态变更
-
通过
Nacos Server
和
spring-cloud-starter-alibaba-nacos-discovery
实现
服务的注册与发现
Nacos 架构
整体架构分为
用户层、业务层、内核层和插件
,用户层主要解决用户使用的易用性问题,业务层主要解决服务发现和配置管理的功能问题,内核层解决分布式系统一致性、存储、高可用等核心问题,插件解决扩展性问题。
用户层
- OpenAPI:暴露标准Rest风格HTTP接口,简单易用,方便多语言集成
- Console:易用控制台,做服务管理、配置管理等操作
- SDK:多语言 SDK,目前几乎支持所有主流编程语言
- Agent:Sidecar 模式运行,通过标准 DNS 协议与业务解耦
- CLI:命令行对产品进行轻量化管理,像 git 一样好用
业务层
- 服务管理:实现服务 CRUD,域名 CRUD,服务健康状态检查,服务权重管理等功能
- 配置管理:实现配置管 CRUD,版本管理,灰度管理,监听管理,推送轨迹,聚合数据等功能
- 元数据管理:提供元数据 CURD 和打标能力,为实现上层流量和服务灰度非常关键
内核层
- 插件机制:实现三个模块可分可合能力,实现扩展点 SPI 机制,用于扩展自己公司定制
- 事件机制:实现异步化事件通知,SDK 数据变化异步通知等逻辑,是Nacos高性能的关键部分
- 日志模块:管理日志分类,日志级别,日志可移植性(尤其避免冲突),日志格式,异常码+帮助文档
- 回调机制:SDK 通知数据,通过统一的模式回调用户处理。接口和数据结构需要具备可扩展性
- 寻址模式:解决 Server IP 直连,域名访问,Nameserver 寻址、广播等多种寻址模式,需要可扩展
- 推送通道:解决 Server 与存储、Server 间、Server 与 SDK 间高效通信问题
- 容量管理:管理每个租户,分组下的容量,防止存储被写爆,影响服务可用性
- 流量管理:按照租户,分组等多个维度对请求频率,长链接个数,报文大小,请求流控进行控制
- 缓存机制:容灾目录,本地缓存,Server 缓存机制,是 Nacos 高可用的关键
- 启动模式:按照单机模式,配置模式,服务模式,DNS 模式模式,启动不同的模块
- 一致性协议:解决不同数据,不同一致性要求情况下,不同一致性要求,是 Nacos 做到 AP 协议的关键
- 存储模块:解决数据持久化、非持久化存储,解决数据分片问题
插件
- Nameserver:解决 Namespace 到 ClusterID 的路由问题,解决用户环境与 Nacos 物理环境映射问题
- CMDB:解决元数据存储,与三方 CMDB 系统对接问题,解决应用,人,资源关系
- Metrics:暴露标准 Metrics 数据,方便与三方监控系统打通
- Trace:暴露标准 Trace,方便与 SLA 系统打通,日志白平化,推送轨迹等能力,并且可以和计量计费系统打通
- 接入管理:相当于阿里云开通服务,分配身份、容量、权限过程
- 用户管理:解决用户管理,登录,SSO 等问题
- 权限管理:解决身份识别,访问控制,角色管理等问题
- 审计系统:扩展接口方便与不同公司审计系统打通
- 通知系统:核心数据变更,或者操作,方便通过SMS系统打通,通知到对应人数据变更
Nacos能做什么。
- Nacos是以服务为主要服务对象的中间件,Nacos支持所有主流的服务发现、配置和管理。
-
Nacos主要提供以下四大功能:
- 服务发现和服务健康监测
- 动态配置服务
- 动态DNS服务
- 服务及其元数据管理
注册中心原理
在使用注册中心时,一共有三种角色:服务提供者(
Service Provider
)、服务消费者(
Service Consumer
)、注册中心(
Registry
)。
在一些文章中,服务提供者被称为 Server,服务消费者被称为 Client。
三个角色交互如下图所示:
① Provider:
-
启动时,向
Registry
注册自己为一个服务(Service)的实例(Instance)。 -
同时,定期向
Registry
发送心跳,告诉自己还存活。 -
关闭时,向
Registry
取消注册。
② Consumer:
- 启动时,向 Registry 订阅使用到的服务,并缓存服务的实例列表在内存中。
- 后续,Consumer 向对应服务的 Provider 发起调用时,从内存中的该服务的实例列表选择一个,进行远程调用。
- 关闭时,向 Registry 取消订阅。
③ Registry:
-
Provider 超过一定时间未心跳时,从服务的实例列表移除。
*服务的实例列表发生变化(新增或者移除)时,通知订阅该服务的 Consumer,从而让 Consumer 能够刷新本地缓存。
Provider
和
Consumer
是角色上的定义,
一个服务同时即可以是 Provider 也可以作为 Consumer
。例如说,优惠劵服务可以给订单服务提供接口,同时又调用用户服务提供的接口。
构建应用接入Nacos注册中心
服务提供者
第一步:创建一个Spring Boot应用,可以命名为:alibaba-nacos-discovery-server。作为服务提供者 demo-provider
第二步:编辑pom.xml,加入必要的依赖配置,比如:
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<parent>
<artifactId>labx-01</artifactId>
<groupId>cn.iocoder.springboot.labs</groupId>
<version>1.0-SNAPSHOT</version>
</parent>
<modelVersion>4.0.0</modelVersion>
<artifactId>alibaba-nacos-discovery-server</artifactId>
<properties>
<spring.boot.version>2.2.4.RELEASE</spring.boot.version>
<spring.cloud.version>Hoxton.SR1</spring.cloud.version>
<spring.cloud.alibaba.version>2.2.0.RELEASE</spring.cloud.alibaba.version>
</properties>
<!--
引入 Spring Boot、Spring Cloud、Spring Cloud Alibaba 三者 BOM 文件,进行依赖版本的管理,防止不兼容。
在 https://dwz.cn/mcLIfNKt 文章中,Spring Cloud Alibaba 开发团队推荐了三者的依赖关系
-->
<dependencyManagement>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>${spring.boot.version}</version>
<type>pom</type>
<scope>import</scope>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-dependencies</artifactId>
<version>${spring.cloud.version}</version>
<type>pom</type>
<scope>import</scope>
</dependency>
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-alibaba-dependencies</artifactId>
<version>${spring.cloud.alibaba.version}</version>
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencies>
</dependencyManagement>
<dependencies>
<!-- 引入 SpringMVC 相关依赖,并实现对其的自动配置 -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<!-- 引入 Spring Cloud Alibaba Nacos Discovery 相关依赖,将 Nacos 作为注册中心,并实现对其的自动配置 -->
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
</dependency>
</dependencies>
</project>
在
<dependencyManagement />
中,我们引入了 Spring Boot、Spring Cloud、Spring Cloud Alibaba 三者 BOM 文件,进行依赖版本的管理,防止不兼容。在《Spring Cloud 官方文档 —— 版本说明》文档中,推荐了三者的依赖关系。如下表格:
- 这里,我们选择了 Spring Cloud Alibaba 版本为 2.2.0.RELEASE。
- 当前版版本下,我们使用的 Nacos 版本为 1.1.4。
引入
spring-cloud-starter-alibaba-nacos-discovery
依赖,将 Nacos 作为注册中心,并实现对它的自动配置。
第三步,配置文件
创建
application.yaml
配置文件,添加
Nacos Discovery
配置项。配置如下
spring:
application:
name: demo-provider # Spring 应用名
cloud:
nacos:
# Nacos 作为注册中心的配置项,对应 NacosDiscoveryProperties 配置类
discovery:
server-addr: 127.0.0.1:8848 # Nacos 服务器地址
service: ${spring.application.name} # 注册到 Nacos 的服务名。默认值为 ${spring.application.name}。
server:
port: 18080 # 服务器端口。默认为 8080
重点看
spring.cloud.nacos.discovery
配置项,它是 Nacos Discovery 配置项的前缀,对应
NacosDiscoveryProperties
配置项。
第四步,创建应用启动类,并提供 HTTP 接口。
@SpringBootApplication
@EnableDiscoveryClient
public class DemoProviderApplication {
public static void main(String[] args) {
SpringApplication.run(DemoProviderApplication.class, args);
}
@RestController
static class TestController {
@GetMapping("/echo")
public String echo(String name) {
return "provider:" + name;
}
}
}
①
@SpringBootApplication
注解,被添加在类上,声明这是一个 Spring Boot 应用。Spring Cloud 是构建在 Spring Boot 之上的,所以需要添加。
②
@EnableDiscoveryClient
注解,开启 Spring Cloud 的
注册发现功能
。不过从 Spring Cloud Edgware 版本开始,实际上已经不需要添加 @EnableDiscoveryClient 注解,只需要引入 Spring Cloud 注册发现组件,就会自动开启注册发现的功能。例如说,我们这里已经引入了 spring-cloud-starter-alibaba-nacos-discovery 依赖,就不用再添加 @EnableDiscoveryClient 注解了。
拓展小知识:在 Spring Cloud Common 项目中,定义了 DiscoveryClient
接口,作为通用的发现客户端,提供读取服务和读取服务列表的 API 方法。而想要集成到 Spring Cloud
体系的注册中心的组件,需要提供对应的 DiscoveryClient 实现类。 例如说,Spring Cloud Alibaba Nacos
Discovery 提供了 NacosDiscoveryClient 实现,Spring Cloud Netflix Eureka 提供了
EurekaDiscoveryClient 实现。 如此,所有需要使用到的地方,只需要获取到 DiscoveryClient
客户端,而无需关注具体实现,保证其通用性。
③ TestController 类,提供了 /echo 接口,返回 provider:${name} 结果。
简单测试
① 通过 启动类启动服务提供者,IDEA 控制台输出日志如:
// ... 省略其它日志
2020-02-08 15:25:57.406 INFO 27805 --- [ main] c.a.c.n.registry.NacosServiceRegistry : nacos registry, DEFAULT_GROUP demo-provider 10.171.1.115:18080 register finished
-
服务
demo-provider
注册到 Nacos 上的日志。
② 在启动都ok之后,我们可以访问Nacos的管理页面http://127.0.0.1:8848/nacos/来查看服务列表,此时可以看到如下内容:
服务消费者
接下来,实现一个应用来消费上面已经注册到Nacos的服务。
第一步:创建一个Spring Boot应用,命名为:alibaba-nacos-discovery-client-common,作为服务消费者 demo-consumer。
第二步:编辑pom.xml中的依赖内容,与上面服务提供者的一样即可。
第三步,创建 application.yaml 配置文件,添加相应配置项。配置如下
spring:
application:
name: demo-consumer # Spring 应用名
cloud:
nacos:
# Nacos 作为注册中心的配置项,对应 NacosDiscoveryProperties 配置类
discovery:
server-addr: 127.0.0.1:8848 # Nacos 服务器地址
server:
port: 8081 # 服务器端口。默认为 8080
第四步:创建应用主类,并实现一个HTTP接口,在该接口中调用服务提供方的接口。
@EnableDiscoveryClient
@SpringBootApplication
public class TestApplication {
public static void main(String[] args) {
SpringApplication.run(TestApplication.class, args);
}
@Slf4j
@RestController
static class TestController {
@Autowired
LoadBalancerClient loadBalancerClient;
@GetMapping("/test")
public String test() {
// 通过spring cloud common中的负载均衡接口选取服务提供节点实现接口调用
ServiceInstance serviceInstance = loadBalancerClient.choose("alibaba-nacos-discovery-server");
String url = serviceInstance.getUri() + "/hello?name=" + "abc";
RestTemplate restTemplate = new RestTemplate();
String result = restTemplate.getForObject(url, String.class);
return "Invoke : " + url + ", return : " + result;
}
}
}
这里使用了
Spring Cloud Common
中的
LoadBalancerClient
接口来挑选服务实例信息。然后从挑选出的实例信息中获取可访问的URI,拼接上服务提供方的接口规则来发起调用
第五步,启动服务消费者,然后通过curl或者postman等工具发起访问,下面以curl为例:
$ curl localhost:9000/test
Invoke : http://10.123.18.216:8001/hello?name=didi, return : hello abc
$ curl localhost:9000/test
Invoke : http://10.123.18.216:8002/hello?name=didi, return : hello abc
可以看到,两次不同请求的时候,真正实际调用的服务提供者实例是不同的,也就是说,
通过
LoadBalancerClient
接口在获取服务实例的时候,已经实现了对服务提供方实例的负载均衡
。但是很明显,这样的实现还是比较繁琐,
数据模型
Nacos 数据模型 Key 由三元组唯一确认。如下图所示:
-
作为注册中心时,Namespace + Group + Service
-
作为配置中心时,Namespace + Group + DataId
Namespace 命名空间
用于进行租户粒度的配置隔离。
默认为 public(公共命名空间)
。
不同的命名空间下,可以存在相同的 Group 或 Data ID 的配置
。Namespace 的常用场景之一是不同环境的配置的区分隔离,例如开发测试环境和生产环境的资源(如配置、服务)隔离等。
Group 服务分组
不同的服务可以归类到同一分组。默认为
DEFAULT_GROUP
(默认分组)。
Service 服务
例如说,用户服务、订单服务、商品服务等等。
服务领域模型
Service 可以进一步细拆服务领域模型,如下图:
Instance 实例
提供一个或多个服务的具有可访问网络地址(IP:Port)的进程。
以前面搭建的服务提供者服务为例:
-
如果我们启动
一个 JVM 进程
,就是服务 demo-provider 下的一个实例。 -
如果我们启动
多个 JVM 进程
,就是服务 demo-provider 下的多个实例。
Cluster 集群
同一个服务下的所有服务实例组成一个默认集群(Default)
。集群可以被进一步按需求划分,划分的单位可以是虚拟集群。
例如说,我们将服务部署在多个机房之中,每个机房可以创建为一个虚拟集群。每个服务在注册到 Nacos 时,设置所在机房的虚拟集群。这样,服务在调用其它服务时,可以通过虚拟集群,优先调用本机房的服务。如此,在提升服务的可用性的同时,保证了性能。
Metadata 元数据
Nacos 元数据(如配置和服务)描述信息,如服务版本、权重、容灾策略、负载均衡策略、鉴权配置、各种自定义标签 (label)。
从作用范围来看,分为
服务级别的元信息、集群的元信息及实例的元信息
。
以 Nacos 元数据的
服务版本
举例子。当一个接口实现,出现不兼容升级时,可以用版本号过渡,版本号不同的服务相互间不引用。
可以按照以下的步骤进行版本迁移:
- 在低压力时间段,先升级一半提供者为新版本
- 再将所有消费者升级为新版本
- 然后将剩下的一半提供者升级为新版本
再次 Nacos 元数据的鉴权配置举例子。通过令牌验证在注册中心控制权限,以决定要不要下发令牌给消费者,可以防止消费者绕过注册中心访问提供者。另外,通过注册中心可灵活改变授权方式,而不需修改或升级提供者。
Health Check 健康检查
以指定方式检查服务下挂载的实例的健康度,从而确认该实例是否能提供服务。根据检查结果,实例会被判断为健康或不健康。
对服务发起解析请求时,不健康的实例不会返回给客户端。
健康保护阈值
为了防止因过多实例不健康导致流量全部流向健康实例,继而造成流量压力把健康实例实例压垮并形成雪崩效应,应将健康保护阈值定义为一个 0 到 1 之间的浮点数。
当域名健康实例占总服务实例的比例小于该值时,无论实例是否健康,都会将这个实例返回给客户端。这样做虽然损失了一部分流量,但是保证了集群的剩余健康实例能正常工作。
支持的几种服务消费方式
(RestTemplate、WebClient、Feign):
使用RestTemplate
@EnableDiscoveryClient
@SpringBootApplication
public class TestApplication {
public static void main(String[] args) {
SpringApplication.run(TestApplication.class, args);
}
@Slf4j
@RestController
static class TestController {
@Autowired
RestTemplate restTemplate;
@GetMapping("/test")
public String test() {
String result = restTemplate.getForObject("http://alibaba-nacos-discovery-server/hello?name=abc", String.class);
return "Return : " + result;
}
}
@Bean
@LoadBalanced
public RestTemplate restTemplate() {
return new RestTemplate();
}
}
可以看到,在定义
RestTemplate
的时候,增加了
@LoadBalanced
注解,而在真正调用服务接口的时候,原来host部分是通过手工拼接ip和端口的,直接采用服务名的时候来写请求路径即可。在真正调用的时候,Spring Cloud会将请求拦截下来,然后通过负载均衡器选出节点,并替换服务名部分为具体的ip和端口,从而实现基于服务名的负载均衡调用。
使用WebClient
WebClient是Spring 5中最新引入的,可以将其理解为reactive版的RestTemplate。下面举个具体的例子,它将实现与上面RestTemplate一样的请求调用:
@EnableDiscoveryClient
@SpringBootApplication
public class TestApplication {
public static void main(String[] args) {
SpringApplication.run(TestApplication.class, args);
}
@Slf4j
@RestController
static class TestController {
@Autowired
private WebClient.Builder webClientBuilder;
@GetMapping("/test")
public Mono<String> test() {
Mono<String> result = webClientBuilder.build()
.get()
.uri("http://alibaba-nacos-discovery-server/hello?name=abc")
.retrieve()
.bodyToMono(String.class);
return result;
}
}
@Bean
@LoadBalanced
public WebClient.Builder loadBalancedWebClientBuilder() {
return WebClient.builder();
}
}
在定义
WebClient.Builder
的时候,也增加了
@LoadBalanced
注解,其原理与之前的RestTemplate时一样的。
使用Feign
RestTemplate和WebClient都是Spring自己封装的工具,接下来使用的是Netflix OSS中的成员,通过它可以更方便的定义和使用服务消费客户端。
第一步:在pom.xml中增加openfeign的依赖:
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-openfeign</artifactId>
</dependency>
第二步:定义Feign客户端和使用Feign客户端:
@EnableDiscoveryClient
@SpringBootApplication
@EnableFeignClients
public class TestApplication {
public static void main(String[] args) {
SpringApplication.run(TestApplication.class, args);
}
@Slf4j
@RestController
static class TestController {
@Autowired
Client client;
@GetMapping("/test")
public String test() {
String result = client.hello("didi");
return "Return : " + result;
}
}
@FeignClient("alibaba-nacos-discovery-server")
interface Client {
@GetMapping("/hello")
String hello(@RequestParam(name = "name") String name);
}
}
这里主要先通过
@EnableFeignClients
注解开启扫描
Spring Cloud Feign
客户端的功能;然后又创建一个
Feign
的客户端接口定义。使用
@FeignClient
注解来
指定这个接口所要调用的服务名称
,
接口中定义的各个函数使用Spring MVC的注解就可以来绑定服务提供方的REST接口
,比如下面就是绑定alibaba-nacos-discovery-server服务的/hello接口的例子。最后,在Controller中,注入了Client接口的实现,并调用hello方法来触发对服务提供方的调用。
Nacos作为配置中心
Nacos除了实现了
服务的注册发现之外
,还将
配置中心功能整合在了一起
。Nacos的配置管理模型与淘宝开源的配置中心
Diamond
类似,基础层面都通过
DataId
和
Group
来定位配置内容,除此之外还增加了很多其他的管理功能。
创建配置
第一步:进入Nacos的控制页面,在配置列表功能页面中,点击右上角的“+”按钮,进入“新建配置”页面,如下图填写内容:
其中:
-
Data ID
:填入
alibaba-nacos-config-client.properties
-
Group
:不修改,使用默认值
DEFAULT_GROUP
-
配置格式
:选择
Properties
-
配置内容
:应用要加载的配置内容,这里仅作为示例,做简单配置,比如:
didispace.title=spring-cloud-alibaba-learning
创建应用
第一步:创建一个Spring Boot应用,可以命名为:
alibaba-nacos-config-client
。
第二步:编辑pom.xml,加入必要的依赖配置,比如:
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-nacos-config</artifactId>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>1.18.2</version>
<optional>true</optional>
</dependency>
</dependencies>
上述内容主要三部分:
-
parent
:定义spring boot的版本 -
dependencyManagement
:spring cloud的版本以及spring cloud alibaba的版本 -
dependencies:当前应用要使用的依赖内容。这里主要新加入了
Nacos的配置客户端模块
:
spring-cloud-starter-alibaba-nacos-config
。由于在dependencyManagement中已经引入了版本,所以这里就不用指定具体版本了。
可以看到,这个例子中并没有加入nacos的服务发现模块,所以这两个内容是完全可以独立使用的
第三步:创建应用主类,并实现一个HTTP接口:
@SpringBootApplication
public class TestApplication {
public static void main(String[] args) {
SpringApplication.run(TestApplication.class, args);
}
@Slf4j
@RestController
@RefreshScope
static class TestController {
@Value("${didispace.title:}")
private String title;
@GetMapping("/test")
public String hello() {
return title;
}
}
}
-
@SpringBootApplication
定义是个Spring Boot应用; -
还定义了一个Controller,其中通过
@Value
注解,注入了key为didispace.title的配置(默认为空字符串),这个配置会通过
/test
接口返回,后续我们会通过这个接口来验证Nacos中配置的加载。 -
另外,这里还有一个比较重要的注解
@RefreshScope
,主要用来
让这个类下的配置内容支持动态刷新,也就是当我们的应用启动之后,修改了Nacos中的配置内容之后,这里也会马上生效。
第四步:创建配置文件bootstrap.properties,并配置服务名称和Nacos地址
spring.application.name=alibaba-nacos-config-client
server.port=8001
spring.cloud.nacos.config.server-addr=127.0.0.1:8848
注意:这里必须使用
bootstrap.properties
。同时,
spring.application.name
值必须与上一阶段Nacos中创建的配置
Data Id
匹配(除了.properties或者.yaml后缀)。
第五步:启动上面创建的应用。
2019-01-27 18:29:43.497 INFO 93597 --- [ main] o.s.c.a.n.c.NacosPropertySourceBuilder : Loading nacos data, dataId: 'alibaba-nacos-config-client.properties', group: 'DEFAULT_GROUP'
2019-01-27 18:29:43.498 INFO 93597 --- [ main] b.c.PropertySourceBootstrapConfiguration : Located property source: CompositePropertySource {name='NACOS', propertySources=[NacosPropertySource {name='alibaba-nacos-config-client.properties'}]}
在启动的时候,我们可以看到类似上面的日志信息,这里会输出应用程序要从Nacos中获取配置的
dataId
和
group
。如果在启动之后,发现配置信息没有获取到的时候,可以先从这里着手,看看配置加载的目标是否正确。
第六步:验证配置获取和验证动态刷新
用
curl
或者
postman
等工具,访问接口:
localhost:8001/test
,
一切正常的话,将返回Nacos中配置的
spring-cloud-alibaba-learning
。然后,再通过Nacos页面,修改这个内容,点击发布之后,再访问接口,可以看到返回结果变了。
同时,在应用的客户端,我们还能看到如下日志:
2019-01-27 18:39:14.162 INFO 93597 --- [-127.0.0.1_8848] o.s.c.e.event.RefreshEventListener : Refresh keys changed: [didispace.title]
在Nacos中修改了Key,在用到这个配置的应用中,也自动刷新了这个配置信息。
Nacos配置的加载规则详解
图中的Nacos中创建的配置内容是这样的:
-
Data ID
:alibaba-nacos-config-client.properties
-
Group
:DEFAULT_GROUP
拆解一下,主要有三个元素,它们与具体应用的配置内容对应关系如下:
-
Data ID中的
alibaba-nacos-config-client
:对应客户端的配置
spring.cloud.nacos.config.prefix
,默认值为
${spring.application.name}
,即:
服务名
-
Data ID中的
properties
:对应客户端的配置
spring.cloud.nacos.config.file-extension
,默认值为
properties
-
Group的值
DEFAULT_GROUP
:对应客户端的配置
spring.cloud.nacos.config.group
,默认值为
DEFAULT_GROUP
在采用默认值的应用要加载的配置规则就是:
Data ID=${spring.application.name}.properties
,
Group=DEFAULT_GROUP
。
例子一:
如果我们不想通过服务名来加载,那么可以增加如下配置,就会加载到
Data ID=example.properties
,
Group=DEFAULT_GROUP
的配置内容了:
spring.cloud.nacos.config.prefix=example
例子二:
如果我们想要加载yaml格式的内容,而不是Properties格式的内容,那么可以通过如下配置,实现加载
Data ID=example.yaml
,
Group=DEFAULT_GROUP
的配置内容了:
spring.cloud.nacos.config.prefix=example
spring.cloud.nacos.config.file-extension=yaml
例子三
:如果我们对配置做了分组管理,那么可以通过如下配置,实现加载
Data ID=example.yaml
,
Group=DEV_GROUP
的配置内容了:
spring.cloud.nacos.config.prefix=example
spring.cloud.nacos.config.file-extension=yaml
spring.cloud.nacos.config.group=DEV_GROUP
Nacos的多环境管理
使用Data ID与profiles实现
Data ID
在Nacos中,我们可以理解为就是一个
Spring Cloud应用的配置文件名
。我们知道默认情况下
Data ID
的名称格式是这样的:
${spring.application.name}.properties
,即:
以Spring Cloud应用命名的properties文件。
实际上,
Data ID
的规则中,还包含了环境逻辑,这一点与Spring Cloud Config的设计类似。我们在应用启动时,可以通过
spring.profiles.active
来指定
具体的环境名称
,此时客户端就会把要获取配置的
Data ID
组织为:
${spring.application.name}-${spring.profiles.active}.properties
。
实际上,更原始且最通用的匹配规则,是这样的:
${spring.cloud.nacos.config.prefix}-${spring.profile.active}.${spring.cloud.nacos.config.file-extension}
。而上面的结果是因为
${spring.cloud.nacos.config.prefix}
和
${spring.cloud.nacos.config.file-extension}
都使用了默认值。
第一步:先在Nacos中,根据这个规则,创建两个不同环境的配置内容。比如:
如上图,我们为
alibaba-nacos-config-client
应用,定义了
DEV
和
TEST
的两个独立的环境配置。我们可以在里面定义不同的内容值,以便后续验证是否真实加载到了正确的配置。
第二步:在
alibaba-nacos-config-client
应用的配置文件中,增加环境配置:
spring.profiles.active=DEV
第三步:启动应用,我们可以看到日志中打印了,加载的配置文件:
2019-01-30 15:25:18.216 INFO 96958 --- [ main] o.s.c.a.n.c.NacosPropertySourceBuilder : Loading nacos data, dataId: 'alibaba-nacos-config-client-DEV.properties', group: 'DEFAULT_GROUP'
使用Group实现
第一步:先在Nacos中,通过区分Group来创建两个不同环境的配置内容。比如:
如上图,我们为
alibaba-nacos-config-client
应用,定义了
DEV
环境和
TEST
环境的两个独立的配置,这两个匹配与上一种方法不同,它们的
Data ID
是完全相同的,只是
GROUP
不同。
第二步:在alibaba-nacos-config-client应用的配置文件中,增加Group的指定配置
spring.cloud.nacos.config.group=DEV_GROUP
第三步:启动应用,我们可以看到日志中打印了,加载的配置文件:
2019-01-30 15:55:23.718 INFO 3216 --- [main] o.s.c.a.n.c.NacosPropertySourceBuilder : Loading nacos data, dataId: 'alibaba-nacos-config-client.properties', group: 'DEV_GROUP'
使用Namespace实现
Namespace
,用于进行租户粒度的配置隔离。
不同的命名空间下,可以存在相同的
Group
或
Data ID
的配置
。Namespace的常用场景之一是不同环境的配置的区分隔离,例如:开发测试环境和生产环境的资源(如配置、服务)隔离等。
第一步:先在Nacos中,根据环境名称来创建多个Namespace。比如:
第二步:在配置列表的最上方,可以看到除了
Public
之外,多了几个刚才创建的Namepsace。分别在
DEV
和
TEST
空间下为
alibaba-nacos-config-client
应用创建配置内容:
第三步:在
alibaba-nacos-config-client
应用的配置文件中,增加
Namespace
的指定配置,比如:
spring.cloud.nacos.config.namespace=83eed625-d166-4619-b923-93df2088883a。
这里需要注意
namespace
的配置
不是使用名称,而是使用
Namespace的ID
。
第四步:启动应用,通过访问
localhost:8001/test
接口,验证一下返回内容是否正确。
Nacos配置的多文件加载与共享配置
Nacos配置的多文件加载
Spring应用对Nacos中配置内容的对应关系是通过下面三个参数控制的:
- spring.cloud.nacos.config.prefix
- spring.cloud.nacos.config.file-extension
- spring.cloud.nacos.config.group
默认情况下,会加载
Data ID=${spring.application.name}.properties
,
Group=DEFAULT_GROUP
的配置。
假设现在有这样的一个需求:我们想要对所有应用的Actuator模块以及日志输出做统一的配置管理。所以,我们希望可以将Actuator模块的配置放在独立的配置文件
actuator.properties
文件中,而对于日志输出的配置放在独立的配置文件
log.properties
文件中。通过拆分这两类配置内容,希望可以做到配置的共享加载与统一管理。
这时候,我们只需要做以下两步,就可以实现这个需求 :
第一步:在Nacos中创建
Data ID=actuator.properties
,
Group=DEFAULT_GROUP
和
Data ID=log.properties
,
Group=DEFAULT_GROUP
的配置内容。
第二步:在Spring Cloud应用中通过使用
spring.cloud.nacos.config.ext-config
参数来配置要加载的这两个配置内容,比如:
spring.cloud.nacos.config.ext-config[0].data-id=actuator.properties
spring.cloud.nacos.config.ext-config[0].group=DEFAULT_GROUP
spring.cloud.nacos.config.ext-config[0].refresh=true
spring.cloud.nacos.config.ext-config[1].data-id=log.properties
spring.cloud.nacos.config.ext-config[1].group=DEFAULT_GROUP
spring.cloud.nacos.config.ext-config[1].refresh=true
可以看到,
spring.cloud.nacos.config.ext-config
配置是一个
数组List类型
。每个配置中包含三个参数:
data-id
、
group
,
refresh
;前两个不做赘述,与Nacos中创建的配置相互对应,
refresh
参数控制
这个配置文件中的内容是否支持自动刷新
,
默认情况下,只有默认加载的配置才会自动刷新,对于这些扩展的配置加载内容需要配置该设置时候才会实现自动刷新。
共享配置
通过上面加载多个配置的实现,实际上我们已经可以实现不同应用共享配置了。但是Nacos中还提供了另外一个便捷的配置方式,比如下面的设置与上面使用的配置内容是等价的:
spring.cloud.nacos.config.shared-dataids=actuator.properties,log.properties
spring.cloud.nacos.config.refreshable-dataids=actuator.properties,log.properties
-
spring.cloud.nacos.config.shared-dataids
参数用来
配置多个共享配置的
Data Id
,多个的时候用用逗号分隔
-
spring.cloud.nacos.config.refreshable-dataids
参数用来
定义哪些共享配置的
Data Id
在配置变化时,应用中可以动态刷新,多个Data Id之间用逗号隔开。如果没有明确配置,默认情况下所有共享配置都不支持动态刷新
配置加载的优先级
在使用Nacos配置的时候,主要有以下三类配置:
-
A: 通过
spring.cloud.nacos.config.shared-dataids
定义的共享配置 -
B: 通过
spring.cloud.nacos.config.ext-config[n]
定义的加载配置 -
C:
通过内部规则
(
spring.cloud.nacos.config.prefix
、
spring.cloud.nacos.config.file-extension
、
spring.cloud.nacos.config.group
这几个参数)拼接出来的配置
要弄清楚这几个配置加载的顺序,我们从日志中也可以很清晰的看到,我们可以做一个简单的实验:
spring.cloud.nacos.config.ext-config[0].data-id=actuator.properties
spring.cloud.nacos.config.ext-config[0].group=DEFAULT_GROUP
spring.cloud.nacos.config.ext-config[0].refresh=true
spring.cloud.nacos.config.shared-dataids=log.properties
spring.cloud.nacos.config.refreshable-dataids=log.properties
根据上面的配置,应用分别会去加载三类不同的配置文件,启动应用的时候,将会在日志中看到如下输出:
2019-02-08 21:23:02.665 INFO 63804 --- [main] o.s.c.a.n.c.NacosPropertySourceBuilder : Loading nacos data, dataId: 'log.properties', group: 'DEFAULT_GROUP'
2019-02-08 21:23:02.671 INFO 63804 --- [main] o.s.c.a.n.c.NacosPropertySourceBuilder : Loading nacos data, dataId: 'actuator.properties', group: 'DEFAULT_GROUP'
2019-02-08 21:23:02.677 INFO 63804 --- [main] o.s.c.a.n.c.NacosPropertySourceBuilder : Loading nacos data, dataId: 'alibaba-nacos-config-client.properties', group: 'DEFAULT_GROUP'
后面加载的配置会覆盖之前加载的配置,所以优先级关系是:A < B < C
结论: 后面加载的配置会覆盖之前加载的配置,所以优先级关系是:A < B < C
数据持久化
在搭建Nacos集群之前,我们需要先修改Nacos的数据持久化配置为MySQL存储
。默认情况下,Nacos使用嵌入式数据库实现数据的存储。所以,如果启动多个默认配置下的Nacos节点,数据存储是存在一致性问题的。为了解决这个问题,Nacos采用了集中式存储的方式来支持集群化部署,目前只要支持MySQL的存储。
配置Nacos的MySQL存储只需要下面三步:
-
第一步
:安装数据库,版本要求:5.6.5+ -
第二步
:初始化MySQL数据库,数据库初始化文件:
nacos-mysql.sql
,该文件可以在Nacos程序包下的
conf
目录下获得。执行完成后可以得到如下图所示的表结构:
-
第三步
:修改
conf/application.properties
文件,增加支持MySQL数据源配置,添加(目前只支持mysql)数据源的url、用户名和密码。配置样例如下:
spring.datasource.platform=mysql
db.num=1
db.url.0=jdbc:mysql://localhost:3306/nacos?characterEncoding=utf8&connectTimeout=1000&socketTimeout=3000&autoReconnect=true
db.user=root
db.password=
集群搭建
根据官方文档的介绍,Nacos的集群架构大致如下图所示(省略了集中化存储信息的MySQL):
MySQL数据源配置
在进行集群配置之前,先完成对MySQL数据源的初始化和配置。主要分以下两步:
-
第一步:
初始化MySQL数据库
,数据库初始化文件:
nacos-mysql.sql
,该文件可以在Nacos程序包下的conf目录下获得。 -
第二步:
修改conf/application.properties文件
,增加支持MySQL数据源配置,添加(目前只支持mysql)数据源的url、用户名和密码。配置样例如下:
spring.datasource.platform=mysql
db.num=1
db.url.0=jdbc:mysql://localhost:3306/nacos?characterEncoding=utf8&connectTimeout=1000&socketTimeout=3000&autoReconnect=true
db.user=root
db.password=
集群配置
在Nacos的
conf
目录下有一个
cluster.conf.example
,可以直接把
example
扩展名去掉来使用,也可以单独创建一个
cluster.conf
文件,然后打开将后续要部署的
Nacos实例地址
配置在这里。
127.0.0.1:8841
127.0.0.1:8842
127.0.0.1:8843
注意:Nacos的集群需要
3个或3个以上
的节点,并且确保这三个节点之间是可以
互相访问
的。
启动实例
本地测试
为了可以方便地启动Nacos的三个本地实例,我们可以将
bin
目录下的
startup.sh
脚本复制三份,分别用来启动三个不同端口的Nacos实例,为了可以方便区分不同实例的启动脚本,我们可以把端口号加入到脚本的命名中,比如:
- startup-8841.sh
- startup-8842.sh
- startup-8843.sh
然后,分别修改这三个脚本中的参数,具体如下图的红色部分(端口号根据上面脚本命名分配):
这里我们通过
-Dserver.port
的方式,在启动命令中,为Nacos指定具体的端口号,以实现在本机上启动三个不同的Nacos实例来组成集群。
修改完3个脚本配置之后,分别执行下面的命令就可以在本地启动Nacos集群了:
sh startup-8841.sh
sh startup-8842.sh
sh startup-8843.sh
生产环境
在实际生产环境部署的时候,由于每个实例分布在不同的节点上,我们可以直接使用默认的启动脚本(除非要调整一些JVM参数等才需要修改)。只需要在各个节点的Nacos的
bin
目录下执行
sh startup.sh
命令即可。
Proxy配置
在Nacos的集群启动完毕之后,根据架构图所示,我们还需要提供一个统一的入口给我们用来维护以及给Spring Cloud应用访问。简单地说,就是我们需要为上面启动的的三个Nacos实例做一个可以为它们实现负载均衡的访问点。这个实现的方式非常多,这里就举个用Nginx来实现的简单例子吧。
在Nginx配置文件的http段中,我们可以加入下面的配置内容:
这样,当我们访问:
http://localhost:8080/nacos/
的时候,就会被负载均衡的代理到之前我们启动的三个Nacos实例上了。这里我们没有配置upstream的具体策略,默认会使用线性轮训的方式 。