一、概述
Nacos 官网地址:https://nacos.io
我们先进入它的官网看看,官网说的很清楚了Nacos
的2个核心能力
所以Nacos可以作为:
- 服务注册中心
- Nacos 支持基于 DNS 和基于 RPC 的服务发现
- 服务提供者使用原生SDK、OpenApi注册 Service 后,服务消费者可以使用DNS TODO或HTTP&API查找和发现服务
- Nacos提供对服务的健康监测,阻止向不健康的服务或者实例发送请求
- 配置中心
- 动态配置服务可以让使用者以中心化、外部化和动态化的方式管理所有环境的应用配置和服务配置
- 动态配置消除了配置变更时重新部署应用和服务的需要,让配置管理变得更加高效和敏捷
- 配置中心化管理让实现无状态服务变得更简单,让服务按需弹性扩展变得更容易
其他:
Nacos支持AP和CP模式的切换
C是所有节点在同一时间看到的数据是一致的;而A的定义是所有的请求都会收到响应。
何时选择使用何种模式?
—般来说,如果不需要存储服务级别的信息且服务实例是通过nacos-client注册,并能够保持心跳上报,那么就可以选择AP模式。当前主流的服务如Spring cloud和Dubbo服务,都适用于AP模式,AP模式为了服务的可能性而减弱了一致性,因此AP模式下只支持注册临时实例。
如果需要在服务级别编辑或者存储配置信息,那么CP是必须,K8S服务和DNS服务则适用于CP模式。CP模式下则支持注册持久化实例,此时则是以Raft协议为集群运行模式,该模式下注册实例之前必须先注册服务,如果服务不存在,则会返回错误。
切换命令:
1
| curl -X PUT '$NACOS_SERVER:8848/nacos/v1/ns/operator/switches?entry=serverMode&value=CP
|
二、安装Nacos-server
2.1 下载编译后压缩包
下载地址: 最新稳定版本 下载nacos-server-$version.zip
2.2 启动服务
而Nacos
支持三种服务部署方式
- 单机模式:用户测试和单机使用
- 集群模式:生产环境使用,保证服务的高可用
- 多集群模式: 用于多数据中心场景
这里就使用单机模式了
Linux/Unix/Mac
启动命令(standalone代表着单机模式运行,非集群模式):
1
| sh startup.sh -m standalone
|
如果您使用的是ubuntu系统,或者运行脚本报错提示[[符号找不到,可尝试如下运行:
1
| bash startup.sh -m standalone
|
Windows
启动命令(standalone代表着单机模式运行,非集群模式):
1
| startup.cmd -m standalone
|
双击 startup.cmd 文件也行
2.3 访问Nacos-server
浏览器访问 127.0.0.1:8848 就能看到Nacos的Web页面了,默认的账号密码都是 nacos
三、nacos的注册中心
3.1 创建服务提供者模块
新建module:nacos-provider
pom.xml文件如下(注意spring-boot-starter-web
也一定要引入,因为有被使用):
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55
| <?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 https://maven.apache.org/xsd/maven-4.0.0.xsd"> <modelVersion>4.0.0</modelVersion> <parent> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-parent</artifactId> <version>2.3.2.RELEASE</version> <relativePath/> </parent>
<groupId>com.apps</groupId> <artifactId>nacos-provider</artifactId> <version>0.0.1-SNAPSHOT</version> <name>nacos-provider</name>
<properties> <java.version>1.8</java.version> <nacos.version>2.2.6.RELEASE</nacos.version> </properties>
<dependencies> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter</artifactId> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency> <dependency> <groupId>com.alibaba.cloud</groupId> <artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId> <version>${nacos.version}</version> </dependency> <dependency> <groupId>junit</groupId> <artifactId>junit</artifactId> <version>4.13.2</version> <scope>test</scope> </dependency> </dependencies>
<build> <plugins> <plugin> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-maven-plugin</artifactId> </plugin> </plugins> </build>
</project>
|
application.yml文件如下:
1 2 3 4 5 6 7 8 9 10 11 12
| server: port: 7001
spring: application: name: service-provider cloud: nacos: discovery: server-addr: 192.168.6.206:8848
|
主启动类:
(@EnableDiscoveryClient) SpringCloud的服务注册发现标签,让注册中心能够发现,扫描到该服务
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
| package com.apps;
import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.cloud.client.discovery.EnableDiscoveryClient;
@SpringBootApplication @EnableDiscoveryClient public class NacosDemoApplication {
public static void main(String[] args) { SpringApplication.run(NacosDemoApplication.class, args); }
}
|
编写控制器:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27
| package com.apps.controller;
import org.springframework.beans.factory.annotation.Value; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RestController;
@RestController @RequestMapping("/provider") public class ProviderController {
@Value("${server.port}") private String port;
@GetMapping("/info") public String getPayment(){ return "nacos注册表,服务器端口:" + port ; }
}
|
然后启动该模块,在nacos控制台就能看到该实例已经注册到nacos:
3.2 创建服务消费者模块
新建module:nacos-consumer
pom.xml文件如下(注意spring-boot-starter-web
也一定要引入,因为有被使用):
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50
| <?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 https://maven.apache.org/xsd/maven-4.0.0.xsd"> <modelVersion>4.0.0</modelVersion> <parent> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-parent</artifactId> <version>2.3.2.RELEASE</version> <relativePath/> </parent>
<groupId>com.apps</groupId> <artifactId>nacos-consumer</artifactId> <version>0.0.1-SNAPSHOT</version> <name>nacos-consumer</name>
<properties> <java.version>1.8</java.version> <nacos.version>2.2.6.RELEASE</nacos.version> </properties>
<dependencies> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter</artifactId> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-test</artifactId> <scope>test</scope> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency> <dependency> <groupId>com.alibaba.cloud</groupId> <artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId> <version>${nacos.version}</version> </dependency> </dependencies>
<build> <plugins> </plugins> </build>
</project>
|
application.yml文件如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14
| server: port: 10083
spring: application: name: service-consumer cloud: nacos: discovery: server-addr: 192.168.6.206:8848
service-url: nacos-user-service: http://service-provider
|
主启动类:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
| package com.apps;
import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.cloud.client.discovery.EnableDiscoveryClient;
@SpringBootApplication @EnableDiscoveryClient public class NacosConsumerApplication {
public static void main(String[] args) { SpringApplication.run(NacosConsumerApplication.class, args); }
}
|
配置类:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26
| package com.apps.config;
import org.springframework.cloud.client.loadbalancer.LoadBalanced; import org.springframework.context.annotation.Bean; import org.springframework.stereotype.Component; import org.springframework.web.client.RestTemplate;
@Component public class JavaConfig {
@LoadBalanced @Bean public RestTemplate restTemplate(){ return new RestTemplate(); }
}
|
控制器:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32
| package com.apps.controller;
import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Value; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RestController; import org.springframework.web.client.RestTemplate;
@RestController @RequestMapping("consumer") public class ConsumerController {
@Value("${service-url.nacos-user-service}") private String serviceUrl;
@Autowired private RestTemplate restTemplate;
@GetMapping(value = "/info") public String getClientServerResult(){ return restTemplate.getForObject(serviceUrl + "/provider/info", String.class); }
}
|
然后启动消费者模块,在nacos控制台可以看到服务列表
然后启动消费者模块,在nacos控制台可以看到服务列表,便可以使用服务了,当前我们可以启动多个服务名相同服务实例来使用Nacos的负载均衡。
3.3 Nacos的负载均衡
nacos支持负载均衡是因为spring-cloud-starter-alibaba-nacos-discovery内含netflix-ribbon包(这里要感谢技术大牛浩哥
的指导)。
为了省事,我们可以直接copy一个provider进行启动
修改名字,好区别。并且指定服务端口,这里指定权重更高,所以优先VM的参数
copy完成后,启动。就有两个相同服务名的实例了
然后通过consumer的AIP访问provider的接口,通过打印可以知道是轮询访问10001/10002
四、nacos作为配置中心
4.1 创建配置模块
新建module:nacos-config
pom.xml文件如下(注意这里引入的是spring-cloud-starter-alibaba-nacos-config
):
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47
| <?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 https://maven.apache.org/xsd/maven-4.0.0.xsd"> <modelVersion>4.0.0</modelVersion> <parent> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-parent</artifactId> <version>2.3.2.RELEASE</version> <relativePath/> </parent>
<groupId>com.apps</groupId> <artifactId>nacos-config</artifactId> <version>0.0.1-SNAPSHOT</version> <name>nacos-config</name> <description>nacos-config</description>
<properties> <java.version>1.8</java.version> <nacos.version>2.2.6.RELEASE</nacos.version> </properties>
<dependencies> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter</artifactId> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-test</artifactId> <scope>test</scope> </dependency>
<dependency> <groupId>com.alibaba.cloud</groupId> <artifactId>spring-cloud-starter-alibaba-nacos-config</artifactId> <version>${nacos.version}</version> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency> </dependencies>
</project>
|
nacos和springcloud-config一样,在项目初始化时,要保证先从配置中心进行配置拉取,拉取配置之后,才能保证项目的正常启动。
springboot中配置文件的加载是存在优先级顺序的,bootstrap优先级高于application。所以我们拉取配置需要写在bootstrap.yml文件中,启动完bootstrap.yml把相关配置拉取下来后才能正常启动项目。
配置bootstrap.yml文件
1 2 3 4 5 6 7 8 9 10 11
| server: port: 3377 spring: application: name: nacos-config cloud: nacos: config: server-addr: 192.168.6.206:8848 file-extension: yaml group: DEFAULT_GROUP
|
配置application.yml文件
1 2 3
| spring: profiles: active: dev
|
4.2 在nacos控制台增加配置信息
前面我们在bootstrap.yml文件中配置了spring.application.name
,是因为它是构成 Nacos 配置管理 dataId
字段的一部分(是什么dataId等一些技术名词我会在文章后面讲解)。
在Nacos Spring Cloud 中,dataId
的完整格式如下:
1
| ${prefix}-${spring.profiles.active}.${file-extension}
|
prefix
默认为 spring.application.name
的值,也可以通过配置项 spring.cloud.nacos.config.prefix
来配置。
spring.profiles.active
即为当前环境对应的 profile,详情可以参考 Spring Boot文档。 注意:当 spring.profiles.active
为空时,对应的连接符 -
也将不存在,dataId 的拼接格式变成 ${prefix}.${file-extension}
file-exetension
为配置内容的数据格式,可以通过配置项 spring.cloud.nacos.config.file-extension
来配置。目前只支持 properties
和 yaml
类型。
最后公式:
1
| ${spring.application.name}-${spring.profiles.active}.${spring.cloud.nacos.config.file-extension}
|
接下来在nacos控制台新增配置
保存后,创建控制器
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30
| package com.apps.controller;
import org.springframework.beans.factory.annotation.Value; import org.springframework.cloud.context.config.annotation.RefreshScope; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RestController;
@RefreshScope @RestController public class NacosConfigController {
@Value("${user.name}") private String name; @Value("${user.age}") private String name;
@RequestMapping(value = "config/info") public String getConfigValue() { System.err.println("user name :" +name+"; age: "+age); return this.name + this.age; }
}
|
启动项目,访问请求,控制台打印
nacos控制台修改yml参数后(也可以发送Open API修改),又访问请求,配置信息就修改了。
五、名词理念
5.1 namespace和group和dataId三者的关系
三个东西是为了解多项目多环境的问题。
实际开发中,通常一个系统会准备
- dev开发环境
- test测试环境
- prod生产环境
如何保证指定环境启动时服务能正确读取到Nacos上相应环境的配置文件呢?
一个大型分布式微服务系统会有很多微服务子项目,每个微服务项目又都会有相应的开发环境、测试环境、预发环境、正式环境…那怎么对这些微服务配置进行管理呢?
nacos默认的namespace是public,默认的group是DEFAULT_GROUP
类似Java里面的package名和类名,最外层的namespace是可以用于区分部署环境的,Group和DatalD逻辑上区分两个目标对象。
Nacos默认的Namespace是public,Namespace主要用来实现隔离。
比方说我们现在有三个环境:开发、测试、生产环境,我们就可以创建三个Namespace,不同的Namespace之间是隔离的。
Group默认是DEFAULT_GROUP,Group可以把不同的微服务划分到同一个分组里面去
Service就是微服务:一个Service可以包含多个Cluster (集群),Cluster是对指定微服务的一个虚拟划分。
比方说为了容灾,将Service微服务分别部署在了杭州机房和广州机房,这时就可以给杭州机房的Service微服务起一个集群名称(HZ) ,给广州机房的Service微服务起一个集群名称(GZ),还可以尽量让同一个机房的微服务互相调用,以提升性能。
最后是Instance,就是微服务的实例
我们可以在nacos控制台创建namespace和group。
创建dev和test的namespace
回到配置列表,就能看到我们创建的namespace
分别在test和dev的namespace里新建配置文件
在bootstrap.yml中指定group和namespace就可以获取不同的配置文件了。(记住dataId的生成规则)
1 2 3 4 5 6 7 8 9 10 11 12
| spring: application: name: cloud-nacos-config cloud: nacos: config: server-addr: localhost:8848 file-extension: yaml group: TEST_GROUP namespace: 155a7fd7-6834-4787-80f7-35f56dd9f8fb
|
5.2 Nacos配置管理模型
对于Nacos配置管理,通过Namespace、group、Data ID能够定位到一个配置集
配置集(Data ID)
在系统中,一个配置文件通常就是一个配置集,一个配置集可以包含了系统的各种配置信息,例如一个配置集可能包含了数据源、线程池、日志级别等配置项。每个配置集都可以定义一个有意义的名称,就是配置集的ID即Data ID
配置项
就是我们的配置内容
配置集中包含的一个个配置内容就是配置项。它代表一个具体的可配置的参数与其值域,通常以key=value的形式存在。例如我们常配置系统的日志输出级别( logLevel=INFO |WARN|ERROR)就是一个配置项
配置分组(Group)
配置分组是对配置集进行分组,通过一个有意义的字符串(如Buy或Trade ) 来表示
**不同的配置分组下可以有相同的配置集( DataID)**。当您在Nacos上创建一个配置时,如果未填写配置分组的名称,则配置分组的名称默认采用DEFAULT_GROUP。配置分组的常见场景:可用于区分不同的项目或应用,例如学生管理系统的配置集可以定义一个group为:STUDENT_GROUP
命名空间(Namespace)
命名空间( namespace )可用于进行不同环境的配置隔离
例如可以隔离开发环境、测试环境和生产环境,因为它们的配置可能各不相同,或者是隔离不同的用户,不同的开发人员使用同一个nacos管理各自的配置,可通过namespace隔离。
不同的命名空间下,可以存在相同名称的配置分组(Group)或配置集。
获取某配置集的代码:需要指定
nacos服务地址,必须指定
namespace,不指定默认为public
group,不指定默认为DEFAULT_GROUP
dateId,必须指定
5.3 服务发现概念简介
分布式系统中,包含多个独立的服务,服务之间存在需要调用的需求,应该如何调用?
服务调用时必须知道目标服务实例的 IP、端口、API 接口。
其中 API 信息可以通过服务接口文档中获知,但 IP、端口 都是动态的,每次部署服务实例的时候可能都会变。
知道了目标服务实例的地址,就相当于发现了对方。具体怎么发现目标服务?这就是服务发现机制。
如上图,在服务实例数量少的时候,每个服务实例可以自己维护相关服务实例的地址信息,也就是自己维护一个通讯录。
例如通过配置文件来记录,相关服务实例地址发生变更的时候就修改一下,有点麻烦,但还可以忍受。
但是,当服务实例数量变大之后,就无法自己维护了,相关服务数量多、地址变化频繁。
此时必须有一种更优的发现机制,就出现了服务注册中心。
如上图,每个服务实例启动之后,都主动向注册中心登记自己的地址信息,这样注册中心便拥有了所有服务实例的记录,类似于查号台。
当某个服务实例停止运行的时候,主动让注册中心销毁自己的信息。
如果服务实例不是主动停止,而是因为故障等原因死掉的,如何处理?需要注册中心能够主动清理。
注册中心要能够实时知道各个服务实例的状态,通过心跳机制来实现,实例定时向注册中心发送请求,表明自己还活着,如果心跳没了,注册中心就可以对其清理。
一个服务实例在调用另一个服务时,可以根据服务名称从注册中心获取此服务的实例信息列表,从中选取一个实例进行调用。
以上就是注册中心这种服务发现机制的工作方式。
5.4 分布式配置概念简介
每个服务都会有自己的配置信息,例如 SpringBoot 项目中的配置文件,服务运行时会读取配置文件中的配置项。
一个服务通常会启动多个实例,来提供其可靠性,那么每个实例中就都会包含这个配置文件,一个服务的各个实例中配置都是相同的。
如果要改某个配置项的值,怎么办?
修改配置文件,然后重新部署此服务的所有实例。麻烦低效。
如果把服务的配置信息提出来,不放在自己的配置文件中,而是放到一个第三方的配置中心,服务实例从配置中心读取属于自己的配置,而且,在配置中心里面修改配置项之后,所有相关实例都可以立即拿到最新值,不用重新部署了,这样就方便很多。
所以分布式配置有2大好处:
- 方便维护,集中维护优于分散式维护每个服务的配置文件。
- 动态更新,配置修改后直接生效,不用重新部署。
此外,Nacos 还可以做配置的版本管理,轻松实现历史版本的回滚。