Nacos
Nacos就是Alibaba推出的一款 配置中心和注册中心结合的一款工具,属于SpringCloudAlibaba技术栈下
Nacos官网地址
https://nacos.io/zh-cn/index.html
安装启动
下载
目录结构
根据目录结构可以看出Nacos本身也就是一个java程序。SpringBoot程序
启动
cmd窗口进入nacos的bin目录,因为我这里是windos
// windows 启动
# 启动命令:
startup.cmd
# 双击startup.cmd运行文件。
# 默认是集群启动,所以要指定 参数,单机启动
startup.cmd -m standalone
# Linux/Unix/Mac启动方式:
# 启动命令(standalone代表着单机模式运行,非集群模式)
sh startup.sh -m standalone
# 如果是ubuntu系统,或者运行脚本报错提示[[符号找不到,可尝试如下运行:
bash startup.sh -m standalone
控制台不要关闭,访问nacos的监控页面
默认的账号密码都是nacos,nacos
http://localhost:8848/nacos/
配置中心
命名空间(Namespace)
用于进行租户粒度的配置隔离。不同的命名空间下,可以存在相同的 Group 或 Data ID 的配置。Namespace 的常用场景之一是不同环境的配置的区分隔离,例如开发测试环境和生产环境的资源(如配置、服务)隔离等。
配置分组(Group)
Nacos 中的一组配置集,是组织配置的维度之一。通过一个有意义的字符串(如 Buy 或 Trade )对配置集进行分组,从而区分 Data ID 相同的配置集。当您在 Nacos 上创建一个配置时,如果未填写配置分组的名称,则配置分组的名称默认采用 DEFAULT_GROUP 。配置分组的常见场景:不同的应用或组件使用了相同的配置类型,如 database_url 配置和 MQ_topic 配置。
配置集 ID(Data ID)
Nacos 中的某个配置集的 ID。配置集 ID 是组织划分配置的维度之一。Data ID 通常用于组织划分系统的配置集。一个系统或者应用可以包含多个配置集,每个配置集都可以被一个有意义的名称标识。Data ID 通常采用类 Java 包(如 com.taobao.tc.refund.log.level)的命名规则保证全局唯一性。此命名规则非强制。
配置集:一组相关或者不相关的配置项的集合称为配置集。在系统中,一个配置文件通常就是一个配置集,包含了系统各个方面的配置。例如,一个配置集可能包含了数据源、线程池、日志级别等配置项。
nacos的配置中心,我们可以把Boot程序中的yaml配置写在nacos中。boot程序访问nacos中的配置
从而将公共的配置更方便的做处理
并且可以实现nacos配置改变,不重启项目的情况下,改变项目中的配置信息
新建配置
监听查询
JAVA的API操作NACOS
这里使用Nacos自带的api操作,了解这些之后,即使在SpringBoot中遇到问题也是可以使用原生api操作的
引入依赖
<?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">
<modelVersion>4.0.0</modelVersion>
<groupId>nacosBoot</groupId>
<artifactId>nacosBoot</artifactId>
<version>1.0-SNAPSHOT</version>
<!--引入父模块-->
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.3.4.RELEASE</version>
</parent>
<properties>
<nacos>2.1.1</nacos>
</properties>
<!--引入 Spring-Boot-starter依赖-->
<dependencies>
<!--从parent中引出的boot的web依赖-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<!--引入nacos客户端依赖-->
<dependency>
<groupId>com.alibaba.nacos</groupId>
<artifactId>nacos-client</artifactId>
<version>${nacos}</version>
</dependency>
</dependencies>
</project>
添加Nacos的配置文件
每次的添加和修改都会产生记录和历史数据
获取配置代码
public static void main(String[] args) {
try {
ConfigService configService = NacosFactory.createConfigService("127.0.0.1:8848");
String dataId = "JavaTestNacos";
String groupId = "JAVA_API";
// 指定 DataID,GroupID,超时时间 从而指定nacos中的配置文件
String config = configService.getConfig(dataId, groupId, 5000);
System.out.println(config);
} catch (NacosException e) {
e.printStackTrace();
}
}
监听配置代码
public static void main(String[] args) {
try {
ConfigService configService = NacosFactory.createConfigService("127.0.0.1:8848");
String dataId = "JavaTestNacos";
String groupId = "JAVA_API";
// 指定 DataID,GroupID
// 重写监听器的回调函数
configService.addListener(dataId, groupId, new Listener() {
@Override
public Executor getExecutor() {
return null;
}
// 作为监听器的回调函数
@Override
public void receiveConfigInfo(String s) {
System.out.println("时间: "+ LocalDateTime.now());
System.out.println(s);
}
});
// 避免main方法运行就结束
System.out.print("避免程序结束:");
System.in.read();
} catch (NacosException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
}
SpringBoot操作
引入依赖
<!--引入nacos客户端依赖-->
<dependency>
<groupId>com.alibaba.boot</groupId>
<artifactId>nacos-config-spring-boot-starter</artifactId>
<version>0.2.12</version>
</dependency>
方式一
配置类指定nacos的访问信息
@SpringBootApplication
@EnableNacosConfig(globalProperties = @NacosProperties(serverAddr = "127.0.0.1:8848"))
@NacosPropertySource(dataId = "SpringBootNacos",groupId = "DEFAULT_GROUP",autoRefreshed = true)
public class SpringBootNacosStudy {
public static void main(String[] args) {
SpringApplication.run(SpringBootNacosStudy.class,args);
}
}
@RestController
public class NacosProperties {
@NacosValue(value = "${name}",autoRefreshed = true)
String name;
@GetMapping("/showProperties")
public String showProperties(){
return name;
}
}
测试可以随着配置文件改变而改变
方式二
如果我们不想用配置类的注解方式我们还可以在配置文件中指定
其实底层,配置文件和注解都是进行绑定到配置类中的api操作
启动类不指定注解方式,使用配置类指定配置文件
contoller不用改变
@SpringBootApplication
public class SpringBootNacosStudy {
public static void main(String[] args) {
SpringApplication.run(SpringBootNacosStudy.class,args);
}
}
nacos.config.data-id=SpringBootNacos
nacos.config.group=DEFAULT_GROUP
nacos.config.server-addr=127.0.0.1:8848
nacos.config.auto-refresh=true
nacos.config.bootstrap.enable=true
SpringCloud操作Nacos
Nacos的Cloud和Boot对应的版本说明
引入依赖
<?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">
<modelVersion>4.0.0</modelVersion>
<groupId>nacosBoot</groupId>
<artifactId>nacosBoot</artifactId>
<version>1.0-SNAPSHOT</version>
<!--引入父模块-->
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.3.4.RELEASE</version>
</parent>
<!--引入 Spring-Boot-starter依赖-->
<dependencies>
<!--从parent中引出的boot的web依赖-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<!--引入nacos客户端依赖-->
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-nacos-config</artifactId>
<version>2.2.8.RELEASE</version>
</dependency>
</dependencies>
</project>
配置文件
SpringBoot中默认是不支持bootstrap.properties属性文件的。我们需要映入SpringCloud的依赖才可以。
Bootstrap.properties 配置文件主要是通过 SpringCloud 新增的父应用上下文通过 BootstrapApplicationListener 监听事件 来初始化 Bootstrap 应用上下文然后进行加载填充到 SpringApplication 的 Environment 中。
# 指定服务名称 同时指定DataId
spring.application.name=nacos-study
# 下面是指定 nacos的分组和地址
spring.cloud.nacos.config.server-addr=127.0.0.1:8848
spring.cloud.nacos.config.group=DEFAULT_GROUP
这里我指定了两个配置文件
bootStrap:指定 服务名和nacos相关信息
application:指定了一个age,与nacos中冲突测试会采用那边
注意:即使不编写BootStrap文件,将内容迁移application中项目也是可以正常运行
注意:即使不编写BootStrap文件,将内容迁移application中项目也是可以正常运行
注意:即使不编写BootStrap文件,将内容迁移application中项目也是可以正常运行
启动类和controller
@SpringBootApplication
public class SpringBootNacosStudy {
public static void main(String[] args) {
SpringApplication.run(SpringBootNacosStudy.class,args);
}
}
@RefreshScope
这个注解的作用不是开启Nacos的自动刷新
nacos自动刷新配置默认开启,这里表示的javaBean的属性自动刷新
// 注意这里使用的 @RefreshScope
@RestController
@RefreshScope
public class NacosProperties {
@Value("${name}")
String name;
@Value("${age}")
String age;
@GetMapping("/showProperties")
public String showProperties(){
return name+" "+age;
}
}
结果:发现最终配置类的age采用了项目中的配置
所以:项目中配置<nacos中配置
如果Nacos服务突然挂掉,不会影响服务正常运行
如果Nacos服务突然挂掉,不会影响服务正常运行
多个配置文件
# 指定服务名称 同时指定DataId
spring.application.name=nacos-study
我们可以在nacos中配置多个配置文件。
拉去顺序
拉去dataid等于 服务名称 的配置
拉去dataid等于 指定名称.properties 或 服务名称.yaml 的配置
通过 spring.cloud.nacos.config.file.extension=properties/yaml 来决定
这里也表示读取到的 nacos的文件格式解析指定为 properties 还是 yaml 创建文件编写的格式
拉去dataid等于 服务名称-${spring.profiles.active}.properties的配置
spring.profiles.active=dev 就决定获取 服务名称-dev.proerties的dataid配置文件
// 指定后缀
spring.cloud.nacos.config.file.extension=properties/yaml
// 指定文件的-环境
spring.profiles.active=dev
可以自行去测试验证
指定多个配置文件
我们上面指定的配置文件,按照优先级也只是指定一个配置文件
我们可以通过上面配置文件指定多个自定义的配置文件
一个用于可能不止需要一个配置,有可能需要多个配置
spring.cloud.nacos.config.shared-configs[0].data-id=dataid全名字
spring.cloud.nacos.config.shared-configs[1].data-id=dataid全名字
spring.cloud.nacos.config.extension-configs[0].data-id=dataid全名字
spring.cloud.nacos.config.extension-configs[1].data-id=dataid全名字
优先级
- config[1] > config[0]:值越大优先级越搞
- nacos的主配置 > extension > shared
服务注册
nacos原生操作【服务注册】
引入依赖
<!-- 需要引入的依赖-->
<dependency>
<groupId>com.alibaba.nacos</groupId>
<artifactId>nacos-client</artifactId>
<version>2.1.1</version>
</dependency>
<!--不过引入 cloud的注册中心依赖会包含nacos的原生依赖-->
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
<version>2.2.8.RELEASE</version>
</dependency>
代码操作
示例1
public class DemoOne {
public static void main(String[] args) {
try {
// 创建一个nacos服务
NamingService namingService = NamingFactory.createNamingService("127.0.0.1:8848");
// 创建一个实例 指定参数
Instance instance = new Instance();
instance.setClusterName("testClusterName");
instance.setIp("11.11.11.1");
instance.setPort(9090);
// nacos注册指定实例,指定服务名称
namingService.registerInstance("testServerName",instance);
// 防止程序运行停止
System.in.read();
} catch (NacosException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
}
}
示例2
直接写定参数来指定。不需要创建Instance实例
public class DemoTwo {
public static void main(String[] args) {
try {
// 创建一个nacos服务
NamingService namingService = NamingFactory.createNamingService("127.0.0.1:8848");
// nacos注册指定 指定
// 服务名称,ip,端口,集群名称
namingService.registerInstance("testServerName","11.11.11.2",8888,"cluster1");
NamingService namingService1 = NamingFactory.createNamingService("127.0.0.1:8848");
namingService1.registerInstance("testServerName","11.11.11.3",8888,"cluster1");
NamingService namingService2 = NamingFactory.createNamingService("127.0.0.1:8848");
namingService2.registerInstance("testServerName","11.11.11.4",8888,"cluster2");
// 防止程序运行停止
System.in.read();
} catch (NacosException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
}
}
这些服务都是上面的代码。注册到nacos中的
这些服务都是上面的代码。注册到nacos中的
这些服务都是上面的代码。注册到nacos中的
Nacos就是根据这些概念性的一层一层划分查找,一层一层找
NameSpace:命名空间,主要用来区分运行环境,开发,测试,生产
group:区分组,指定服务在那个组下,避免出现相同的服务名区分
service:服务名。多个服务在一起
cluster:将多个服务在分成一个一个的集群
-- 本服务的 ip 端口
server:
port: 8001
spring:
application:
-- 本服务的服务名
name: WXL-DEV-SERVICE-2
cloud:
nacos:
-- 指定nacos的ip端口注册到位置
server-addr: localhost:8848 # nacos的服务配置
discovery:
-- 注册到的集群名称
cluster-name: Service_cluster_2 #集群
-- 注册到的分组名称
group: Service_group_2 # 分组名
namespace: public # 命名空间
username: nacos # 用户名
password: nacos # 密码
ephemeral: false #永久实例 哪怕宕机了也不会删除实例,默认为true非永久
nacos原生操作【服务发现】
获取nacos中的服务实例
NamingService的方法
方法名 | 描述 |
---|---|
getAllInstances | 指定服务名称,等参数。获取当前所有实例 |
selectInstances | 指定服务名,等参数。获取当前所有 健康实例 |
selectOneHealthyInstance | 指定服务,等参数。获取一个随机的 健康实例 根据权重,越高出现概率越高 |
Instance类
该类就表示获取的一个服务的所有信息,在注册的时候我也同样去指定过Instance
public class Primordial {
public static void main(String[] args) throws NacosException {
// 创建nocos 服务
NamingService namingService = NamingFactory.createNamingService("127.0.0.1");
// 指定服务名称获取当前所有实例
List<Instance> testServerName = namingService.getAllInstances("testServerName");
// 指定服务名成获取当前所有 健康实例
List<Instance> instances = namingService.selectInstances("testServerName", true);
// 指定服务名获取一个随机的 健康实例 根据权重,越高出现概率越高
namingService.selectOneHealthyInstance("testServerName");
}
}
修改服务的权重
监听获取实例
public class AutoPrimordial {
public static void main(String[] args) throws NacosException, IOException {
// 创建nocos 服务
NamingService namingService = NamingFactory.createNamingService("127.0.0.1");
// 创建监听事件 这里注意导包
EventListener eventListener = new EventListener(){
// 监听的回调函数
@Override
public void onEvent(Event event) {
// 判断是否该类型
if(event instanceof NamingEvent){
// 转换 事件名称 可以获取服务更改的信息
NamingEvent event1 = (NamingEvent) event;
String serviceName = event1.getServiceName();
String clusters = event1.getClusters();
List<Instance> instances = event1.getInstances();
for (Instance instance : instances) {
System.out.println("服务名称:"+instance.getServiceName());
System.out.println("集群名称:"+instance.getClusterName());
System.out.println("实例id:"+instance.getInstanceId());
System.out.println("ip:端口:"+instance.getIp()+instance.getPort());
}
}
}
};
// 指定监听订阅的服务名 和 事件监听的回调函数
namingService.subscribe("testServerName",eventListener);
System.in.read();
}
}
可以通过修改注册进的服务实例,或者直接关闭注册进的服务。
从而动态的监听到变化,执行回调函数。
SpringCloud服务注册
这里我新建一个服务nacosProduce来模拟nacos的服务注册进去
引入依赖
<!--不过引入 cloud的注册中心依赖会包含nacos的原生依赖-->
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
<version>2.2.8.RELEASE</version>
</dependency>
spring.application.name=produce
# 这里可以看到指定了两个 nacos的ip和端口
# 带上discovery 表示专门区分 配置中心和注册中心的nacos的配置
# 不带discovery 表示 配置中心和注册中心地址一样
spring.cloud.nacos.server-addr=127.0.0.1:8848
spring.cloud.nacos.discovery.server-addr=127.0.0.1:8848
SpringCloud服务发现
因为想要测试,调用注册进去的服务是否可以访问到,我们可以在 produce服务中编写一个Contoller进行调用测试
@RestController
public class ProduceController {
@GetMapping("/showProduce")
public String showProduce(){
return "我是produce服务";
}
}
RestTemplate的服务调用
spring.application.name=consumer
server.port=2222
@RestController
public class consumerController {
@GetMapping("consumer")
public String consumer(){
RestTemplate restTemplate = new RestTemplate();
// 写死url 进行调用
// 不能走注册中心 获取ip地址 和负载均衡
ResponseEntity<String> forEntity =
restTemplate.getForEntity("http://localhost:1111/showProduce", String.class);
String body = forEntity.getBody();
return "consumer调用结果: "+body;
}
}
使用nacos服务发现
引入依赖与服务注册那边一致
引入依赖与服务注册那边一致
引入依赖与服务注册那边一致
配置文件
指定nacos的ip地址
spring.application.name=consumer
server.port=2222
# nacos的ip和端口
spring.cloud.nacos.server-addr=127.0.0.1:8848
主启动
开启服务注册中心
重新指定RestTemplate的类
LoadBalanced:当注册中心对服务名存在多个服务会进行负载均衡的调用
@SpringBootApplication
@EnableDiscoveryClient
public class Consumer {
// loadBalanced 是负载均衡 存在多个规则。
@Bean
@LoadBalanced
public RestTemplate restTemplate(){
return new RestTemplate();
}
public static void main(String[] args) {
SpringApplication.run(Consumer.class,args);
}
}
Controller
路径通过服务名获取IP地址
@RestController
public class consumerController {
@Autowired
private RestTemplate restTemplate;
@GetMapping("consumer")
public String consumer() {
// 写死url 进行调用
// 不能走注册中心 获取ip地址 和负载均衡
ResponseEntity<String> forEntity =
restTemplate.getForEntity("http://produce/showProduce", String.class);
String body = forEntity.getBody();
return "consumer调用结果: " + body;
}
}
RestTemplate的操作后面可以改下为feign调用形式
RestTemplate的操作后面可以改下为feign调用形式
RestTemplate的操作后面可以改下为feign调用形式,后面会陆续更新
临时实例和保护阙值
临时和持久实例
# 表示持久化实例
spring.cloud.nacos.discovery.ephemeral=false
- 默认情况Nacos中都是临时实例,可以指定配置,在注册的时候表示持久实例
- 客户端每隔5s向nacos发送一次心跳
- nacos如果超过15s没有收到客户端心跳,会把实例标记不健康
- 超过30s没有收到心跳,就会删除实例
- 持久化就是不会删除实例
保护阙值
在0-1范围指定一个比例
如果不健康的服务占比超过比例
那么该服务的所有实例都不可访问,触发保护。
保护阙值
在使用过程中,我们可以设置一个0-1的一个比例,表示如果服务的所有实例中,健康实例的比重低于这个比重就会触发保护,一旦触发保护,在服务消费端侧就会把所有实例拉取下来,不管是否健康,这样就起到了保护的作用,因为正常来说消费端只会拿到健康实例,但是如果健康实例占总实例比例比较小了,那么就会导致所有流量都会压到健康实例上,这样仅剩的几个健康实例也会被压垮,所以只要触发了保护,消费端就会拉取到所有实例,这样部分消费端仍然会访问到不健康的实例从而请求失败,但是也有一部分请求能访问到健康实例,达到保护的作用。