在 spring-cloud 部分组件宣布闭源后,急需一套可以代替的开源框架,而 spring-cloud-alibaba 是对 Spring Cloud 的标准实现,以微服务为核心的整体解决方案
各组件版本以官方公布的版本说明为准.
官方解读
简单来说,Nacos 致力于帮助您发现、配置和管理微服务。Nacos 提供了一组简单易用的特性集,帮助您快速实现动态服务发现、服务配置、服务元数据及流量管理。
Nacos 依赖 Java 环境来运行。如果您是从代码开始构建并运行Nacos,还需要为此配置 Maven环境,请确保是在以下版本环境中安装使用:
你可以通过源码和发行包两种方式来获取 Nacos。
git clone https://github.com/alibaba/nacos.git
cd nacos/
mvn -Prelease-nacos -Dmaven.test.skip=true clean install -U
ls -al distribution/target/
// change the $version to your actual path
cd distribution/target/nacos-server-$version/nacos/bin
您可以从 最新稳定版本 下载 nacos-server-$version.zip
包。
unzip nacos-server-$version.zip 或者 tar -xvf nacos-server-$version.tar.gz
cd nacos/bin
mysql 5.7+(包含5.7)
在 nacos
解压后的文件夹中,conf
目录下 有一个文件叫做:nacos-mysql.sql
,创建一个数据库,让后执行这个 sql
文件,结果如下图:
修改 nacos
安装文件夹下 conf
目录中的::application.properties
,改动如下:
#*************** Config Module Related Configurations ***************#
### If use MySQL as datasource:
spring.datasource.platform=mysql
### Count of DB:
db.num=1
### Connect URL of DB:
db.url.0=jdbc:mysql://127.0.0.1:3306/你的数据库名称?characterEncoding=utf8&connectTimeout=1000&socketTimeout=3000&autoReconnect=true&useUnicode=true&useSSL=false&serverTimezone=UTC
db.user.0=你的数据库用户名
db.password.0=你的数据库密码
下面开始改默认密码,创建一个 java
工程,为求方便最好是 springboot
工程,原因在于 nacos
的密码生成使用的是 spring-security
包下的 BCryptPasswordEncoder
类。
添加依赖
<dependency>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-starter-securityartifactId>
dependency>
写一个 main 方法
public static void main(String[] args) {
String passWord = new BCryptPasswordEncoder().encode("123456");
System.out.println(passWord);
}
或者使用在线生成的 如:Bcrypt生成(未试验),让后直接修改数据库,或者等服务启动成功后,再去页面修改密码即可.
启动
启动命令(standalone代表着单机模式运行,非集群模式):
sh startup.sh -m standalone
如果您使用的是ubuntu系统,或者运行脚本报错提示[[符号找不到,可尝试如下运行:
bash startup.sh -m standalone
启动命令(standalone代表着单机模式运行,非集群模式):
startup.cmd -m standalone
停止
sh shutdown.sh
shutdown.cmd
或者双击shutdown.cmd运行文件。
预备环境,已经源码下载安装过程与单机一致,单机可以不配置数据库,但是集群一定要配置。
一个 nacos 服务启动 会用到三个端口号,在集群配置初期要做好端口规划,避免端口冲突,三个端口号详情以服务本身配置端口号为 8848为例:
端口号 | 与主端口号的偏移量 | 描述 |
---|---|---|
8848 | 0 | nacos 服务本身端口号 |
9848 | 1000 | 客户端 grpc 请求服务端端口,用于客户端向服务端发送连接和请求 |
9849 | 1001 | 服务端 grpc 请求服务端端口,用于服务间同步等 |
由于我只有一台服务器,以下过程均是在一台服务器中配置,但过程类似
主端口号 | 客户端请求端口号 | 服务端请求端口号 | 描述 |
---|---|---|---|
8848 | 9848 | 9849 | 服务一 |
8858 | 9858 | 9859 | 服务二 |
8868 | 9868 | 9869 | 服务三 |
首先将安装文件复制,或者解压三份,名称推荐以nacos+主端口号组合,如下图:
nacos 服务配置修改
修改 conf
目录下的 application.properties
,三个服务都需要改成对应的端口号,如下图:
集群文件配置修改
复制 conf
目录下的 cluster.conf.example
文件,重命名为:cluster.conf
,让后将每行配置成 ip:端口
的格式,如下:
官方示例:
[root@VM-4-7-centos conf]# cat cluster.conf.example
#
# Copyright 1999-2021 Alibaba Group Holding Ltd.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
#
#it is ip
#example
192.168.16.101:8847
192.168.16.102
192.168.16.103
我的配置:
[root@VM-4-7-centos conf]# cat cluster.conf
#2022-01-27T17:23:54.963
192.168.122.1:8848
192.168.122.1:8858
192.168.122.1:8868
因为这个配置是所有的服务一致的,所以可以在 windows配置好,在上传到 Linux 上,每个服务都要有。
启动脚本修改
修改 nacos 安装目录下的 bin 目录下的 startup.sh 文件,若默认配置的内存过大,根据实际情况进行调整,下图为我修改之后的配置.
分别执行三个文件夹,bin 目录下的 startup.sh
文件进行启动即可
sh startup.sh
三个文件夹内,bin 目录下 执行 shutdown.sh
文件
sh shutdown.sh
当前 Spring Cloud Alibaba
的最新版本为 2.2.7.RELEASE
,因此 Spring Cloud
的版本选择为:Spring Cloud Hoxton.SR12
,Spring Boot 的版本选择为:2.3.12.RELEASE
父 POM
<project xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns="http://maven.apache.org/POM/4.0.0"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0modelVersion>
<modules>
<module>learn-cloud-alibaba-nacosmodule>
modules>
<parent>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-starter-parentartifactId>
<version>2.3.12.RELEASEversion>
parent>
<groupId>com.dran.cloud.learngroupId>
<artifactId>learn-cloud-alibabaartifactId>
<version>1.0-SNAPSHOTversion>
<packaging>pompackaging>
<properties>
<maven.compiler.source>8maven.compiler.source>
<maven.compiler.target>8maven.compiler.target>
<spring.cloud.version>Hoxton.SR12spring.cloud.version>
<spring.cloud.alibaba.version>2.2.7.RELEASEspring.cloud.alibaba.version>
properties>
<dependencyManagement>
<dependencies>
<dependency>
<groupId>org.springframework.cloudgroupId>
<artifactId>spring-cloud-dependenciesartifactId>
<version>${spring.cloud.version}version>
<type>pomtype>
<scope>importscope>
dependency>
<dependency>
<groupId>com.alibaba.cloudgroupId>
<artifactId>spring-cloud-alibaba-dependenciesartifactId>
<version>${spring.cloud.alibaba.version}version>
<type>pomtype>
<scope>importscope>
dependency>
dependencies>
dependencyManagement>
project>
子POM
<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>learn-cloud-alibabaartifactId>
<groupId>com.dran.cloud.learngroupId>
<version>1.0-SNAPSHOTversion>
parent>
<modelVersion>4.0.0modelVersion>
<groupId>com.dran.cloud.learn.nacosgroupId>
<artifactId>learn-cloud-alibaba-nacosartifactId>
<properties>
<maven.compiler.source>8maven.compiler.source>
<maven.compiler.target>8maven.compiler.target>
properties>
<dependencies>
<dependency>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-starter-webartifactId>
dependency>
dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-maven-pluginartifactId>
plugin>
<plugin>
<groupId>org.apache.maven.pluginsgroupId>
<artifactId>maven-compiler-pluginartifactId>
<configuration>
<source>${maven.compiler.source}source>
<target>${maven.compiler.target}target>
configuration>
plugin>
plugins>
build>
project>
添加依赖
<dependency>
<groupId>com.alibaba.cloudgroupId>
<artifactId>spring-cloud-starter-alibaba-nacos-discoveryartifactId>
dependency>
<dependency>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-starter-actuatorartifactId>
dependency>
注: spring-boot-starter-actuator 这个依赖是用来监测服务是否正常
application.yml
server:
port: 8081
spring:
application:
#服务名称,服务注册到nacos上时默认也是使用这个名称
name: cloud-nacos-servicec
cloud:
nacos:
discovery:
#nacos 地址
server-addr: https://nacos.drancode.com
#nacos开启权限验证后,配置用户名称
username: nacos
#nacos开启权限验证后,配置用户密码
password: Longtao199606.
#配置 命名空间
namespace: public
#自定义group
group:
management:
endpoints:
web:
exposure:
include: '*'
endpoint:
health:
show-details: always
服务启动后,nacos截图:
关于 @EnableDiscoveryClient
该注解在当前版本下不配置服务也能正常进行服务注册和服务发现
添加依赖
<dependency>
<groupId>com.alibaba.cloudgroupId>
<artifactId>spring-cloud-starter-alibaba-nacos-configartifactId>
dependency>
配置修改
nacos-config 使用 bootstrap.properties 或者 bootstrap.yml 来进行配置,与 application.properties 或者 application.yml 类似,具体区别可以自行考证
DataId 默认使用 spring.application.name
配置跟文件扩展名结合(配置格式默认使用 properties), GROUP 不配置默认使用 DEFAULT_GROUP。因此该配置文件对应的 Nacos Config 配置的 DataId 为 cloud-nacos-service.properties, GROUP 为 DEFAULT_GROUP
#服务名称
spring.application.name=cloud-nacos-service
#nacos服务地址
spring.cloud.nacos.config.server-addr=
#nacos用户名称
spring.cloud.nacos.config.username=
#nacos用户密码
spring.cloud.nacos.config.password=
通过代码读取配置文件
@SpringBootApplication
@EnableDiscoveryClient
public class CloudNacosApplication {
public static void main(String[] args) {
ConfigurableApplicationContext applicationContext = SpringApplication.run(CloudNacosApplication.class, args);
String userName = applicationContext.getEnvironment().getProperty("user.name");
String userAge = applicationContext.getEnvironment().getProperty("user.age");
System.err.println("user name :"+userName+"; age: "+userAge);
}
}
创建配置文件
Data Id 不带文件扩展名也可以,nacos也会匹配上,但是推荐还是带上文件扩展名
运行结果
添加依赖
<dependency>
<groupId>com.alibaba.cloudgroupId>
<artifactId>spring-cloud-starter-alibaba-nacos-configartifactId>
dependency>
配置修改
上面使用 bootstrap.properties 来进行配置,这次就用 yml 来进行配置
spring:
application:
#服务名称
name: cloud-nacos-service
cloud:
nacos:
config:
#nacos服务地址
server-addr:
#nacos用户名称
password:
#nacos用户密码
username:
#指定配置文件的扩展名
file-extension: yml
通过代码读取配置文件
@SpringBootApplication
@EnableDiscoveryClient
public class CloudNacosApplication {
public static void main(String[] args) {
ConfigurableApplicationContext applicationContext = SpringApplication.run(CloudNacosApplication.class, args);
String userName = applicationContext.getEnvironment().getProperty("user.name");
String userAge = applicationContext.getEnvironment().getProperty("user.age");
System.err.println("user name :"+userName+"; age: "+userAge);
}
}
创建配置文件
Data Id 不带文件扩展名也可以,nacos也会匹配上
运行结果
你可以通过配置 spring.cloud.nacos.config.refresh.enabled=false
来关闭动态刷新
代码修改
Nacos Config 默认支持配置的动态更新,启动 Spring Boot 应用测试的代码如下:
@SpringBootApplication
@EnableDiscoveryClient
public class CloudNacosApplication {
public static void main(String[] args) throws InterruptedException {
ConfigurableApplicationContext applicationContext = SpringApplication.run(CloudNacosApplication.class, args);
while(true) {
//当动态配置刷新时,会更新到 Enviroment中,因此这里每隔一秒中从Enviroment中获取配置
String userName = applicationContext.getEnvironment().getProperty("user.name");
String userAge = applicationContext.getEnvironment().getProperty("user.age");
System.err.println("user name :" + userName + "; age: " + userAge);
TimeUnit.SECONDS.sleep(1);
}
}
}
运行结果
Nacos Config 在加载配置的时候,不仅仅加载了以 DataId 为 ${spring.application.name}.${file-extension:properties}
为前缀的基础配置,还加载了DataId为 ${spring.application.name}-${profile}.${file-extension:properties}
的基础配置。在日常开发中如果遇到多套环境下的不同配置,可以通过Spring 提供的 ${spring.profiles.active}
这个配置项来配置。 DataId 一定要带上文件扩展名.
${spring.profiles.active} 当通过配置文件来指定时必须放在 bootstrap.properties 文件中。
spring.profiles.active=dev
新增Nacos配置文件
bootstrap.yml 修改
spring:
application:
#服务名称
name: cloud-nacos-service
cloud:
nacos:
config:
#nacos服务地址
server-addr:
#nacos用户名称
password:
#nacos用户密码
username:
#指定配置文件的扩展名
file-extension: yml
profiles:
# 不同环境的标识
active: dev
dev环境运行结果
prod环境运行结果
spring.profiles.active 改为 prod
此案例中我们通过 spring.profiles.active=
的方式写死在配置文件中,而在真正的项目实施过程中这个变量的值是需要不同环境而有不同的值。这个时候通常的做法是通过 -Dspring.profiles.active=
参数指定其配置来达到环境间灵活的切换。
Nacos 内部有 Namespace 的概念:
用于进行租户粒度的配置隔离。不同的命名空间下,可以存在相同的 Group 或 Data ID 的配置。Namespace 的常用场景之一是不同环境的配置的区分隔离,例如开发测试环境和生产环境的资源(如配置、服务)隔离等。
在没有明确指定 ${spring.cloud.nacos.config.namespace}
配置的情况下, 默认使用的是 Nacos 上 Public 这个namespae。如果需要使用自定义的命名空间,可以通过以下配置来实现:
spring.cloud.nacos.config.namespace=b3404bc0-d7dc-4855-b519-570ed34b62d7
在没有明确指定 ${spring.cloud.nacos.config.group}
配置的情况下, 默认使用的是 DEFAULT_GROUP 。如果需要自定义自己的 Group,可以通过以下配置来实现:
spring.cloud.nacos.config.group=DEVELOP_GROUP
该配置必须放在 bootstrap.properties 文件中。并且在添加配置时 Group 的值一定要和 spring.cloud.nacos.config.group
的配置值一致。
当出现配置文件名称与服务名称不一致,或者项目需要指定多个配置文件时,使用原先的默认服务名称+默认文件扩展名或者file-extension指定文件扩展名称方式无法满足,因此通过自定义 DataId 的方式来进行配置文件的指定.
自定义 DataId 的指定方式有两种, spring.cloud.nacos.config.extension-configs
和 spring.cloud.nacos.config.shared-configs
.
配置
spring.application.name=opensource-service-provider
spring.cloud.nacos.config.server-addr=127.0.0.1:8848
# config external configuration
# 1、Data Id 在默认的组 DEFAULT_GROUP,不支持配置的动态刷新
spring.cloud.nacos.config.extension-configs[0].data-id=ext-config-common01.properties
# 2、Data Id 不在默认的组,不支持动态刷新
spring.cloud.nacos.config.extension-configs[1].data-id=ext-config-common02.properties
spring.cloud.nacos.config.extension-configs[1].group=GLOBALE_GROUP
# 3、Data Id 既不在默认的组,也支持动态刷新
spring.cloud.nacos.config.extension-configs[2].data-id=ext-config-common03.properties
spring.cloud.nacos.config.extension-configs[2].group=REFRESH_GROUP
spring.cloud.nacos.config.extension-configs[2].refresh=true
可以看到:
spring.cloud.nacos.config.extension-configs[n].data-id
的配置方式来支持多个 Data Id 的配置。spring.cloud.nacos.config.extension-configs[n].group
的配置方式自定义 Data Id 所在的组,不明确配置的话,默认是 DEFAULT_GROUP。spring.cloud.nacos.config.extension-configs[n].refresh
的配置方式来控制该 Data Id 在配置变更时,是否支持应用中可动态刷新, 感知到最新的配置值。默认是不支持的。注意事项
多个 Data Id 同时配置时,他的优先级关系是 spring.cloud.nacos.config.extension-configs[n].data-id
其中 n 的值越大,优先级越高。
spring.cloud.nacos.config.extension-configs[n].data-id
的值必须带文件扩展名,文件扩展名既可支持 properties,又可以支持 yaml/yml。 此时 spring.cloud.nacos.config.file-extension
的配置对自定义扩展配置的 Data Id 文件扩展名没有影响。
配置
# 配置支持共享的 Data Id
spring.cloud.nacos.config.shared-configs[0].data-id=common.yaml
# 配置 Data Id 所在分组,缺省默认 DEFAULT_GROUP
spring.cloud.nacos.config.shared-configs[0].group=GROUP_APP1
# 配置Data Id 在配置变更时,是否动态刷新,缺省默认 false
spring.cloud.nacos.config.shared-configs[0].refresh=true
可以看到:
spring.cloud.nacos.config.shared-configs[n].data-id
来支持多个共享 Data Id 的配置。spring.cloud.nacos.config.shared-configs[n].group
来配置自定义 Data Id 所在的组,不明确配置的话,默认是 DEFAULT_GROUP。spring.cloud.nacos.config.shared-configs[n].refresh
来控制该Data Id在配置变更时,是否支持应用中动态刷新,默认false。注意事项
与 extension-configs
的注意事项相同
Spring Cloud Alibaba Nacos Config 目前提供了三种配置能力从 Nacos 拉取相关的配置。
spring.cloud.nacos.config.shared-configs[n].data-id
支持多个共享 Data Id 的配置spring.cloud.nacos.config.extension-configs[n].data-id
的方式支持多个扩展 Data Id 的配置当三种方式共同使用时,他们的一个优先级关系是:A < B < C , 优先级大的会覆盖优先级小的,并且会形成互补,互补的意思是优先级小的配置文件配置了其他种类配置文件不存在的配置,那么最终加载的配置中该配置会保留.
profile > 默认配置文件 > extension-configs(下标越大优先级越大) > shared-configs(下标越大优先级越大)
除了默认的 数组下标 的写法外,yml还支持 - 的写法,例如:
spring:
application:
name: nacos-config
cloud:
nacos:
server-addr:
username:
password:
config:
file-extension: yml
# 优先级比默认的低,如果重名不生效
shared-configs:
- dataId: order-service-shared-mysql.yml
#默认是 DEFAULT_GROUP,可不写
group: DEFAULT_GROUP
refresh: true
- dataId: order-service-shared-mq.yml
#默认是 DEFAULT_GROUP,可不写
group: DEFAULT_GROUP
refresh: true
# 优先级比默认的低,但是比 shared 高
extension-configs[0]:
dataId: order-service-extension-matedata.yml
#默认是 DEFAULT_GROUP,可不写
group: DEFAULT_GROUP
refresh: true
extension-configs[1]:
dataId: order-service-extension-param.yml
#默认是 DEFAULT_GROUP,可不写
group: DEFAULT_GROUP
refresh: true
当我们使用 如 @Value 或者 @ConfigurationProperties 等注解时,无法感知到参数值的变更,而通过 @RefreshScope 则能够进行动态刷新变量的值
示例代码
@RefreshScope
@Configuration
@ConfigurationProperties(prefix = "mysql")
public class ParamConfig {
private String name;
@Value("${mq.name}")
private String mqName;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getMqName() {
return mqName;
}
public void setMqName(String mqName) {
this.mqName = mqName;
}
}
@RestController
public class ParamController {
@Autowired
private ParamConfig paramConfig;
@GetMapping("/getParam")
public String getParam() {
return "mysqlName:" + paramConfig.getName() +"=======" + "mqName:" + paramConfig.getMqName();
}
}
使用常见的两种注入参数的方式
第一次运行,mysql.name 设置为 mqsql , mq.name 设置为 mq
运行结果:
第二次运行,mysql.name 设置为 mqsql1 , mq.name 设置为 mq1
运行结果:
日志的变化: