SpringCloud Alibaba 之Nacos 详细讲解
架构发展历程:单体架构 - 垂直架构 - 分布式架构 - SOA架构(小微,Dubbo调控) - 微服务
1 Nacos基础操作
1.1 Nacos简介【注册中心+配置中心】
服务发现:客人去酒店前台询问有哪些服务可以使用
服务注册:酒店前台统计哪些房间可以使用
注册中心(类似于Eureka)
1.2 启动Nacos
首先在Nacos官网下载Naocs并解压
官网地址:https://nacos.io/zh-cn/index.html
解压缩之后进入到bin目录下,在cmd窗口执行startup.cmd -m standalone 表明单机启动
启动之后,访问http://localhost:8848/nacos我们就可以进入到nacos的访问地址了【账号密码默认:nacos/nacos】
1.3 Nacos小案例【注册一个服务到Nacos上】
注册服务之前需要保证本地或远端Nacos是运行着的
①创建父项目配置依赖(普通的SpringBoot项目,添加web模块即可)
<?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>
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<version>3.8.1</version>
<configuration>
<source>8</source>
<target>8</target>
</configuration>
</plugin>
</plugins>
</build>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.2.13.RELEASE</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>
<groupId>com.zi</groupId>
<artifactId>SpringCloudAlibabaAll</artifactId>
<version>0.0.1-SNAPSHOT</version>
<name>SpringCloudAlibabaAll</name>
<packaging>pom</packaging>
<description>SpringCloudAlibabaAll</description>
<properties>
<java.version>1.8</java.version>
<spring-cloud-alibaba-version>2.2.5.RELEASE</spring-cloud-alibaba-version>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-tomcat</artifactId>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
<dependencyManagement>
<dependencies>
<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>
<modules>
<!-配置其他子模块->
<module>cloudAlibaba-Nacos-9001</module>
</modules>
</project>
②创建子模块,并继承父模块【同时添加nacos依赖】
<?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>com.zi</groupId>
<artifactId>SpringCloudAlibabaAll</artifactId>
<version>0.0.1-SNAPSHOT</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>
<groupId>com.zi</groupId>
<artifactId>cloudalibaba-nacos-9001</artifactId>
<version>0.0.1-SNAPSHOT</version>
<name>cloudalibaba-nacos-9001</name>
<description>cloudalibaba-nacos-9001</description>
<properties>
<java.version>1.8</java.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>
<!--添加nacos discovery-->
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
<version>2.2.5.RELEASE</version>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
</project>
③在启动类上添加对应注解@EnableDiscoveryClient
@SpringBootApplication
@EnableDiscoveryClient//要注册服务到nacos必须加该注解
public class CloudalibabaNacos9001Application {
public static void main(String[] args) {
SpringApplication.run(CloudalibabaNacos9001Application.class, args);
}
}
④配置nacos注册中心的地址及项目相关参数
server:
port: 9001
spring:
application:
name: nacos-provider
cloud:
discovery:
#nacos地址
server-addr: 127.0.0.1:8848
management:
endpoint:
web:
exposure:
include: '*'
最后结果:
注意:maven项目中,父子模块,除了子要parent父,父也要module子
2 Nacos测试负载均衡(自带Ribbon)
服务创建出来了,并且注册到nacos上了,那么我们应该如何调用服务呢?
通过Ribbon
2.1Ribbon简介
它是一个基于HTTP和TCP客户端负载均衡器。它虽然只是一个工具类库,它却是每一个微服务的基础设施。因为实际上,对于服务间调用、API网关请求转发都需要经过Ribbon负载均衡来实现。总体来说,Ribbon的主要作用是:从注册服务器端拿到对应服务列表后以负载均衡的方式访问对应服务。【注意nacos内部已经整合了Ribbon,因此我们只需导入nacos依赖即可】
- 通常我们需要远程访问服务,可以使用restTemplate
@Bean
@LoadBalanced
public RestTemplate restTemplate(){
return new RestTemplate();
}
//restTemplate.getForObject(arg1,arg2,arg3...);
restTemplate.getForObject(arg1,arg2,arg3…);
- 第一个参数:被调用目标的Rest接口位置【服务提供者名称+请求路径】
http://nacos-provider/zi
-
第二个参数:返回值类型【如果返回的是List集合,要使用数组类型接收】
-
第三个参数:可变参数【其他参数】
2.2 实现案例
①在之前两个服务(SpringBoot项目)中添加controller
DemoController.java:
@RestController
public class DemoController {
@Value("${server.port}")
private String serverPort;
@GetMapping("/zi")
public String getServerPort(){
return "hello nacos" + serverPort;
}
}
②创建消费者模块
-
首先导入nacos依赖,并且继承之前的父工程
-
然后编写启动类,注入RestTemplate
@SpringBootApplication
public class CloudalibabaConsumer8083Application {
@Bean
@LoadBalanced
public RestTemplate restTemplate(){
return new RestTemplate();
}
public static void main(String[] args) {
SpringApplication.run(CloudalibabaConsumer8083Application.class, args);
}
}
- 编写配置文件
server:
port: 8083
spring:
application:
name: nacos-consumer
cloud:
discovery:
server-addr: localhost:8848
# 消费者将要去访问的服务名称
service-url:
nacos-user-service: http://nacos-provider
- 编写controller
@RestController
public class DemoController {
@Autowired
private RestTemplate restTemplate;
/**
* 消费者去访问具体的服务,实现配置文件和代码的分离
*/
@Value("${service-url.nacos-user-service}")
private String serverURL;
@GetMapping("/testDiscovery")
public String getDiscovery(){
//URL + 访问路径 + (动态参数)
return restTemplate.getForObject(serverURL+"/zi", String.class);
}
}
3 分布式理论(CAP模型)
计算机专家 埃里克·布鲁尔提出分布式系统设计要考虑的三个核心要素:
- C(consistency 一致性):同一时刻的同一请求的实例返回的结果要相同
- A(availability 可用性):所有实例的读写请求在一定时间可以得到正确的响应
- P(partition tolerance 分区容错性):在网络异常(光缆断裂、设备故障、宕机)情况下,系统仍能提供正常服务
以上就是CAP原则(定理),但是三者只能满足其二,即CP或AP
服务注册与发现框架 | CAP模型 | 控制台管理 | 社区活跃度 |
---|---|---|---|
Eureka (奈飞) | AP | 支持 | 低(2.x版本闭源) |
Zookeeper (Apache) | CP | 不支持 | 中 |
Consul | CP | 支持 | 高 |
Nacos | AP/CP | 支持 | 高 |
3.1 Nacos的AP/CP模式切换
Nacos无缝支持一些主流的开源生态,同时再阿里进行Nacos设计的时候重复的考虑到了市场化的运作(市面上大多都是以单一的实现形式为主,例如:Zookeeper使用的是 CP、而 Eureka采用的是AP),在Nacos中提供了两种模式的动态切换。
- 一般来说,如果不需要储存服务器别的信息且服务实例通过nacos-client注册,并能够保持心跳上报,那么就可以选择AP模式。如Spring Cloud 和 Dubbo,都适用于AP模式,AP模式为了服务的可用性减弱了一致性,因此AP模式下只支持注册临时实例。【无配置信息,AP】
- 如果需要在服务级别编辑或者储存配置信息,那么CP是必须的,K8S服务和DNS服务则是用于CP模式。CP模式下则支持注册持久化实例,此时则是以Raft协议为集群运行模式,该模式下注册实例之前必须先注册服务,如果服务不存在,则会返回错误。【有配置信息,CP】
Nacos的AP与CP模式之间的切换:
切换命令(默认是AP):
curl -X PUT '$NACOS_SERVER:8848/nacos/v1/ns/operator/switches?entry=serverMode&value=CP'
注意:临时和持久化的区别主要在健康检查失败后的表现,持久化实例健康检查失败后会被标记成不健康,而临时实例会直接从列表中被删除。
4 Nacos作为配置中心使用
Nacos不仅可以作为注册中心,还可以作为配置中心来使用
4.1 创建项目继承父项目,导入config依赖
继承父项目:
父项目中引入了web依赖
<parent>
<groupId>com.zi</groupId>
<artifactId>SpringCloudAlibabaAll</artifactId>
<version>0.0.1-SNAPSHOT</version>
</parent>
引入config依赖:
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-nacos-config</artifactId>
</dependency>
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
</dependency>
4.2 创建两个配置文件【application.yml、bootstrap.yml】
application.yml:
spring:
profiles:
active: test# 表示测试环境
bootstrap.yml:
# nacos 配置
server:
port: 3377
spring:
application:
name: nacos-config-client
cloud:
nacos:
discovery:
server-addr: localhost:8848 # nacos服务注册中心地址
config:
server-addr: localhost:8848 # nacos配置中心地址
file-extension: yaml # 指定文件扩展名为yaml
4.3 创建controller,并配置启动类
启动类上加上注解@EnableDiscoveryClient
@SpringBootApplication
@EnableDiscoveryClient
public class CloudalibabaConfig3377Application {
public static void main(String[] args) {
SpringApplication.run(CloudalibabaConfig3377Application.class, args);
}
}
创建controller:
【添加@RefreshScope支持动态刷新】
@RestController
@RefreshScope //支持Nacos动态刷新功能
public class ConfigClientControlelr {
//从远程nacos读取配置
@Value("${config.info}")
private String configInfo;
@GetMapping("/config/info")
public String getConfigInfo(){
return configInfo;
}
}
4.4 Nacos中的配置文件
Data ID:nacos-config-client-test.yaml
【DataID需要加上文件名后缀】
config:
info: nacos-config-client-test.yaml, DEFAULT_GROUP, version = 1
界面:
Data ID规则:
4.4 微服务项目中的配置
例如:content内容管理服务包含了:content-api、content-service、content-model
,其中content-api需要使用nacos的注册和配置功能(pom中引入discovery、config),content-service中使用配置功能(config)
content-api中引入discovery、config;content-service中引入config
①配置content-service(私有)
- 添加pom依赖(nacos-config)
- 在nacos中添加content-service对应的配置文件
spring.application.name不在nacos中配置,而是要在工程的本地进行配置,因为nacos是根据dataid去寻找配置文件位置(spring.application.name是dataid的第一部分)
替换content-service之前的配置文件:
bootstrap.yml:
spring:
application:
name: content-service
cloud:
nacos:
server-addr: localhost:8848
discovery:
namespace: ${spring.profiles.active}
group: xc-plus-project
config:
namespace: ${spring.profiles.active}
group: xc-plus-project
file-extension: yaml
refresh-enabled: true
#profiles默认为dev
profiles:
active: dev
运行该项目下的单元测试类,查看是否可以正确访问数据库:
②配置content-api(私有)
- 导入依赖(nacos-config、nacos-discovery)
- nacos中新建配置
content-api-dev.yaml
- content-api本地配置:
#server:
# servlet:
# context-path: /content
# port: 63040
#微服务配置
spring:
application:
name: content-api
cloud:
nacos:
server-addr: localhost:8848
discovery:
namespace: ${spring.profiles.active}
group: xc-plus-project
config:
namespace: ${spring.profiles.active}
group: xc-plus-project
file-extension: yaml
refresh-enabled: true
extension-configs:
- data-id: content-service-${spring.profiles.active}.yaml
group: xc-plus-project
refresh: true
profiles:
active: dev
# 日志文件配置路径
logging:
config: classpath:log4j2-dev.xml
# swagger 文档配置
swagger:
title: "学成在线内容管理系统"
description: "内容系统管理系统对课程相关信息进行业务管理数据"
base-package: com.zi.content
enabled: true
version: 1.0.0
③配置公用配置(swagger、logger等)
- 新建swagger文档通用配置
-
在nacos新建swagger的配置
-
删除本地content-api接口中的swagger配置(
shared-configs引入nacos上的配置
)
最终content-api的配置文件如下:
spring:
application:
name: content-api
cloud:
nacos:
server-addr: localhost:8848
discovery:
namespace: dev
group: xc-plus-project
config:
namespace: dev
group: xc-plus-project
file-extension: yaml
refresh-enabled: true
extension-configs:
- data-id: content-service-${spring.profiles.active}.yaml
group: xc-plus-project
refresh: true
shared-configs:
- data-id: swagger-${spring.profiles.active}.yaml
group: xc-plus-common
refresh: true
- data-id: logging-${spring.profiles.active}.yaml
group: xc-plus-common
refresh: true
profiles:
active: dev
- 新建日志通用配置
- nacos上新建日志配置
- 修改本地配置文件bootstrap.yaml
- 重启服务,访问swagger,查看是否能重新访问
http://localhost:63040/content/swagger-ui.html
④nacos读取配置文件优先级
项目应用名配置文件 > 扩展配置文件 > 共享配置文件 > 本地配置文件
有时候我们在测试程序时直接在本地加一个配置进行测试,这时我们想让本地最优先,可以在nacos配置文件 中配置如下即可实现:
#配置本地优先
spring:
cloud:
config:
override-none: true
5 Nacos命名空间、分组、DataID三者之间的关系
类比于IDEA中的项目名、包名、类名
一个Service可以包含多个Cluster,Nacos默认Cluster是DEFAULT,Cluster是对指定微服务的一个虚拟划分。比如说,将一个Service部署在北京和和杭州的机房中,北京机房的Service就可以起名为(BJ),杭州机房中的Service就可以起名为(HZ),这样就可以尽量让同一个机房的微服务互相调用,提升性能。
5.1 根据DataID切换不同配置
application.yml:
spring:
profiles:
active: test # 测试环境
# active: dev # 表示开发环境
5.2 根据Group切换不同配置
# nacos 配置
server:
port: 3377
spring:
application:
name: nacos-config-client
cloud:
nacos:
discovery:
server-addr: localhost:8848 # nacos服务注册中心地址
config:
server-addr: localhost:8848 # nacos配置中心地址
file-extension: yaml # 指定文件扩展名为yaml
group: DEV_GROUP # 配置组
5.3 根据命名空间切换不同配置
注意分组与命名空间;
# nacos 配置
server:
port: 3377
spring:
application:
name: nacos-config-client
cloud:
nacos:
discovery:
server-addr: localhost:8848 # nacos服务注册中心地址
config:
server-addr: localhost:8848 # nacos配置中心地址
file-extension: yaml # 指定文件扩展名为yaml
group: DEV_GROUP # 配置组
namespace: 75bbf7ab-7499-40b0-838d-0784c89322ca # 指定命名空间
6 Nacos集群部署
6.1 Nacos集群介绍
官网地址:https://nacos.io/zh-cn/docs/cluster-mode-quick-start.html
Nacos支持三种部署模式:
1.单机模式 - 用于测试和单机使用
2.集群模式 - 用于生产环境,确保高可用
3.多集群模式 - 用于多数据中心场景
架构图:部署生产使用的集群模式
具体拆分:
默认Nacos使用嵌入数据库derby实现数据(配置信息、注册信息)的存储,因此如果启动多个默认配置下的Nacos节点,存在数据存储一致性问题,为了解决该问题,Nacos采用了集中存储方式来支持集中化部署,目前仅支持MySQL的存储。
如果要想将nacos上对应的注册信息和配置信息持久化存储到MySQL中,只需要执行nacos目录下conf下的nacos-mysql.sql
同时修改该目录下的application.properties:
修改为对应mysql数据源的配置
spring.datasource.platform=mysql
db.num=1
db.url.0=jdbc:mysql://127.0.0.1:3306/nacos_config?characterEncoding=utf8&connectTimeout=1000&socketTimeout=3000&autoReconnect=true&serverTimezone=UTC
db.user=root
db.password=root
6.2 在linux环境下搭建集群
①环境准备:需要先准备好jdk,nginx,mysql,nacos环境(3台为例);然后在nacos的配置文件中配置好mysql环境,在mysql中执行对应sql脚本文件
- 对应的mysql环境:
spring.datasource.platform=mysql
db.num=1
db.url.0=jdbc:mysql://127.0.0.1:3306/nacos_config?characterEncoding=utf8&connectTimeout=1000&socketTimeout=3000&autoReconnect=true&serverTimezone=UTC
db.user=root
db.password=123456
②修改nacos的cluster.conf(复制另外两台nacos)
# 格式: ip地址:端口号
192.168.189.129:8848
192.168.189.129:8868
192.168.189.129:8888
③修改另外两台nacos的端口
#*************** Spring Boot Related Configurations ***************#
### Default web context path:
server.servlet.contextPath=/nacos
### Default web server port:
server.port=8888/8868
我们在修改端口号时,须要有一定的偏移量,因为在Nacos2.0的时候,本身就新增了占用端口,因此需要我们避开
④配置nginx【修改nginx.conf】
worker_processes 1;
events {
worker_connections 1024;
}
stream {
upstream nacos {
server 192.168.145.13:8848;
server 192.168.145.13:8868;
server 192.168.145.13:8888;
}
server {
listen 81;
proxy_pass nacos;
}
}
⑤关闭防火墙
// 关闭防火墙服务-但是开启还会自动启动
systemctl stop firewalld
// 彻底关闭-开机不会启动防火墙
systemctl disable firewalld
⑥启动nginx
cd /usr/local/nginx/sbin
./nginx
⑦访问nacos
http://192.168.145.13:81/nacos/
在UI界面下,点击集群管理即可查看
⑧添加对应配置,查看linux中的表是否有数据
⑨在SpringBoot项目中修改配置文件,将服务注册到nacos集群上
application.yml
server:
port: 9002
spring:
application:
name: nacos-provider
cloud:
nacos:
discovery:
# 换成nginx的81端口,做集群
server-addr: http://192.168.145.13:81
# server-addr: 127.0.0.1:8848
management:
endpoint:
web:
exposure:
include: '*'
Nacos集群:
7 Nacos常见bug
1. 无法读取到@Value()注解
SpringBoot引入Nacos,并在类上使用@Value()注解读取nacos配置信息,结果无法启动SpringBoot
- 错误信息:Injection of autowired dependencies failed; nested exception is java.lang.IllegalArgumentException: Could not resolve placeholder ‘config.minioUrl’ in value “${config.minioUrl}”
原因一:
- 查看SpringBoot配置文件中的nacos配置是否有误
- 查看是否能够对应上dataId
bootstrap.yaml:
spring:
cloud:
nacos:
server-addr: localhost:8848
config:
namespace: public
file-extension: yaml
# server-addr: localhost:8848
profiles:
active: public
application:
name: Yi-music
原因二:
查看nacos上的命名空间ID是否正确,命名空间ID如果不填nacos会默认生成一串字符。
2. Nacos不断拉取配置bug
原因一:
- 这是因为nacos误以为配置文件上的信息发生了变化,所以才会从naocs服务端拉取配置,但实际上没有
- 因为使用的Nacos 1.2.1版本有一个已知的MD5问题,对同一个配置文件,Nacos客户端和Nacos服务端计算出的MD5值(校验和)不一致,会导致Nacos客户端认为该配置在Nacos服务端有更新,导致Nacos客户端不断从Nacos服务端拉取配置。
- 在Nacos控制台上,修改配置文件(比如加一些不会影响格式的空格),再保存。这时Nacos客户端会重新拉取配置,并计算MD5值,只要计算出的MD5值不以0开头,就不会触发Nacos的MD5 bug。
原因二:
- 将配置文件里的 spring.cloud.nacos.config.namespace=public 注释掉
原因:public是默认的(public并不是它相对应的唯一ID),不需要加上面这行配置,如果是自定义的命名空间才需要加上相对应的ID