一、Nacos的安装
Nacos单例启动命令:
startup.cmd -m standalone
目前Nacos的2.X版本有些不稳定,在课程上的时候老师讲的是他尝试的时候在做集群的时候失败,但是自己在本机上尝试的时候是项目启动不起来。因此在本地尝试的时候还是采用了1.3的版本。下载地址:
https://github.com/alibaba/nacos/releases
现在完成之后可以看看配置文件是否需要修改,还是采用默认配置:
如果conf目录下没有application.properties的文件就将application.properties.example的拷贝一份为application.properties,并在里面更改自己想要的配置。
有关配置信息的详细介绍可以参考文档:Nacos系统参数介绍
个人汇总的Nacos的思维导图:https://www.processon.com/view/link/61459ef57d9c08198c57a60f
#*************** Spring Boot Related Configurations ***************#
### Default web context path:Nacos的根路径,也是就是安装路径
server.servlet.contextPath=/nacos
### Default web server port:访问端口
server.port=8847
### If user MySQL as datasource:是否启用外部存储如果想使用Mysql存储的话就把这个打开,如果不用的话则注销掉
spring.datasource.platform=mysql
### Count of DB:这是有关数据库相关的配置,nacos支持的是多数据源配置,通过后面加index实现不同数据源的配置信息eg:db.url.0
db.num=1
db.url.0=jdbc:mysql://127.0.0.1:3306/nacos_config?characterEncoding=utf8&connectTimeout=10000&socketTimeout=3000&autoReconnect=true&useUnicode=true&useSSL=false&serverTimezone=UTC
db.user=root
db.password=123456
### The ignore urls of auth, is deprecated in 1.2.0:控制台鉴权跳过的接口
nacos.security.ignore.urls=/,/error,/**/*.css,/**/*.js,/**/*.html,/**/*.map,/**/*.svg,/**/*.png,/**/*.ico,/console-fe/public/**,/v1/auth/**,/v1/console/health/**,/actuator/**,/v1/console/server/**
配置完成之后进入bin目录下启动startup.cmd,启动的时候如果是单例启动要记得添加上面说的-m standalone。如果采用的是默认配置就直接访问:http://localhost:8848/nacos,账户名与密码都是nacos
默认情况下数据的存储用的是文件也就是内部存储如果要用外部存储除过打开pring.datasource.platform以及下面有关数据库的几个配置,在1.x的版本中还需要添加plugins的目录(plugins\mysql)并且添加驱动:
二、原生Nacos的使用
通过IDEA创建Meavn项目,在项目的Pom文件中引入依赖:
<dependency>
<groupId>com.alibaba.nacos</groupId>
<artifactId>nacos-client</artifactId>
<version>1.1.1</version>
</dependency>
首先我们根据注册中心的地址以及注册中心的空间名称获取一个ConfigService的实体,通过ConfigService实体根据DataID以及分组信息就可以获取到配置信息。如果要是想要在客户端直接感知配置中心的变化则应该配置监听:
public class NacosUtils {
//声明Nacos中相关的变量
private final static String SERVER_ARR = "127.0.0.1:8847";
private static HashMap<String,String> NAME_SPACE_IDS = new HashMap();
private final static Long TIME_OUT = 5000L;
static {
NAME_SPACE_IDS.put("public","");
NAME_SPACE_IDS.put("dev","2345sate");
}
//简单查询配置中心的信息
static String getConfig(String spaceName,String groupName,String dataId) throws Exception {
//参数合法性检测
if (!checkDatas(dataId))throw new Exception("DataId为空,你查个毛线数据啊");
//获取ConfigService
ConfigService configService = getConfigService(spaceName);
//获取配置信息
return configService.getConfig(dataId, groupName, TIME_OUT);
}
static String getConfig(String groupName,String dataId) throws Exception{
return getConfig(null,groupName,dataId);
}
static String getConfig(String dataId) throws Exception{
return getConfig(null,"DEFAULT_GROUP",dataId);
}
//对配置中心的读取实现监听
static void getConfigByListener(String spaceName,String groupName,String dataId) throws Exception {
final int[] changeCount = {0};
if(!checkDatas(dataId))new Exception("DataId为空,你查个毛线数据啊");
ConfigService configService = getConfigService(spaceName);
//对configService添加监听事件
System.out.println("***程序开始对配置中心的事件进行监听***");
configService.addListener(dataId, groupName, new Listener() {
public Executor getExecutor() {
return null;
}
public void receiveConfigInfo(String s) {
changeCount[0]++;
System.out.println("~~~配置数据发生了第"+ changeCount[0] +"修改~~~");
System.out.println(s);
}
});
while (changeCount[0] <5){
Thread.sleep(100L);
}
}
static void getConfigByListener(String groupName,String dataId) throws Exception {
getConfigByListener(null,groupName,dataId);
}
static void getConfigByListener(String dataId) throws Exception {
getConfigByListener(null,"DEFAULT_GROUP",dataId);
}
//根据配置获取ConfigService
static ConfigService getConfigService(String spaceName) throws Exception {
//获取ConfigService
Properties properties = new Properties();
properties.put(PropertyKeyConst.SERVER_ADDR,SERVER_ARR);
if (spaceName!=null){
String nameSpaceId = NAME_SPACE_IDS.get(spaceName);
if (nameSpaceId==null){
throw new Exception("spaceName不合规,没有找到对应的id");
}
properties.put(PropertyKeyConst.NAMESPACE,nameSpaceId);
}
return ConfigFactory.createConfigService(properties);
}
//对传入数据的合法性进行检测
static boolean checkDatas(String dataId){
return dataId==null||dataId==""?false:true;
}
}
该方法运行的结果就是:
三、集成SpringCloud
通过集成SpringCloud我们在项目中对Nacos相关的使用就会变得简单,例如我们可以简化相关的配置等操作,首先是配置Pom文件:
<dependencies>
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-nacos-config</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-web</artifactId>
<version>5.3.9</version>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
在配置配置中心相关的信息时候要确保首先加载,所以我们将配置中心相关的配置到bootstrap.yml:
server:
port: 9001
spring:
application:
name: config1
cloud:
nacos:
config:
#127.0.0.1:8860
#此种配置方式拥有着最高的配置优先级
server-addr: 127.0.0.1:8860,127.0.0.1:8861,127.0.0.1:8862
file-extension: yaml
namespace: 2345sate
group: DEFAULT_GROUP
#进行多个数据源配置方式一:在配置的优先级方面他居于第二位
ext-config[0]:
data-id: config2.yaml
ext-config[1]:
data-id: config3.yaml
group: LML_GROUP
ext-config[2]:
data-id: config4.yaml
group: LML_GROUP
refresh: true
#进行多数据源方式配置方式二:这种范式配置方便但是只适用于他们属于同一group;
#在配置的优先级中他也是居于末尾的
#shared-dataids: config2.yaml,config3.yaml,config4.yaml
#refreshable-dataids: config3.yaml
在优先级最高的那种配置方式,他会根据
p
r
e
f
i
x
−
{prefix}-
prefix−{spring.profiles.active}.${file-extension}(spring.profiles.active
即为当前环境对应的 profile)的规则生成最终的dataId去配置中心中匹配,所以在配置的时候要注意最终生成的DataID要与配置中心中的相对应,否则的话会出现找不到的情况,就像上面的最终形成的DataId是:config1.yaml,这就要求我们的配置中心中必须要有对应的:
多数据源相关的配置在boostrap文件中的配置是较为简单的,不同的地址之间用“,”隔开,nacos集群的配置主要是通过nacos的config下面的配置文件实现的,其中主要修改端口、本机地址、集群配置等信息
127.0.0.1:8860
127.0.0.1:8861
127.0.0.1:8862
这是集群相关的配置文件,之后我们更改application.properties中的:
在本机实际操作的过程中遇到两个问题,一个是直接通过startup.cmd启动起来的服务是单例,而不是说的默认集群,在造成的现象就是明明集群中的机器单个都起来了但是在集群管理中并没有见到其他的节点。这应该是与cmd文件内容相关所有就在cmd文件中对集群模式做了一个声明:
这个问题解决之后启动时可以了但是还是会报错,每个节点都报错,是数据库层面的,这时候我就讲mysql相关的配置放开,当三个Nacos节点都连接一个库的时候可以启动成功当他们分别连接一个库的时候集群还是可以构建成功的。有关这一点个人的猜测是这不会是应为说集群启动就一定需要配置外部存储而是可能与我们的plugins插件有关,当然这也仅仅只是推测。
最终程序的测试代码为:
@RefreshScope
@RestController
public class NacosServerController {
@Resource
private ConfigurableApplicationContext configurableApplicationContext;
@Value("${student.age:没有找到对应的值}")
private String config1Name;
@Value("${config2.name:config2没有值}")
private String config2Name;
//当不加注解@RefreshScope直接通过@Value是不会感知配置中心变化的
@GetMapping("/getNameNoFresh")
public String getNameNoFresh(){
return config1Name+" "+config2Name;
}
//通过getEnvironment方式感知配置中心数据的变化
@RequestMapping("/getNameFresh")
public String getNameFresh(){
String name1 = configurableApplicationContext.getEnvironment().getProperty("config2.name");
return name1+"~~~~~~~~~~~";
}
//当加上了@RefreshScope则会自动进行刷新
}
这其中需要注意的是数据变化监听的两种方式以及,如何取到配置文件中的值
四、临时节点与永久性节点
(8条消息) Nacos临时实例和持久化实例_Jak的博客-CSDN博客_nacos临时实例和永久实例
临时性节点与永久性节点是由不同的健康检查模式决定的,节点健康检查的模式可以分为agent、服务端两种模式:
-
agent上报模式
客户端通过心跳的方式将状况上报给服务端,如果要是超过15秒没有上报状态就会被标记为不健康如果超过三十秒没有上报,这个节点就会被删除,这种检测模式的节点就是临时性节点也就是临时实例
-
服务端主动检测
nacos服务端主动探测节点的状态间隔的时间为20秒,健康检测失败后会被标记为不健康但是不会被删除,设置临时实例的方法为:
# 默认false spring: cloud: nacos: discovery: ephemeral: true
注册实例支持ephemeral字段
在Nacos的1.0.0版本中instance级别中添加了一个emphemeral字段实现临时性节点与永久性节点的设置。这个设置的出现表示在一个服务中心可能同时存在两种节点,为true的是健康检测中的agent模 式为false的是健康检测中的服务端模式。
Server运行模式的设置
当前Nacos Server 支持AP、CP、MIXED三种运行模式,CP、AP的概念缘于CAP理念有关这一块不清楚的可以看本人Dubbo相关的博客上面有介绍:
CP:一致性、分区容错性
AP:可用性、分区容错性
MIXED:这种模式的设立主要是为了能够同时支持临时实例和持久化实例,这种模式下注册实例之前必须创建服务,在服务已经存在的前提下临时实例可以在网络分区的情况下进行注册
临时实例与永久实例的区别
临时实例比较适合那些需要应对流量突增而弹性扩容的服务,当流量降下来之后这些实例自己销毁即可,便不用在去Nacos中做处理。但是持久化的时候可以实时看到健康状况便于后续做警告、扩容等处理。
CA、CP的切换:
Nacos 集群默认支持的是CAP原则中的AP原则,但是也可切换为CP原则,切换命令如下:
curl -X PUT '$NACOS_SERVER:8848/nacos/v1/ns/operator/switches?entry=serverMode&value=CP'
同时微服务的bootstrap.properties 需配置如下选项指明注册为临时/永久实例。AP模式不支持数据一致性,所以只支持服务注册的临时实例,CP模式支持服务注册的永久实例,满足配置文件的一致性:
#false为永久实例,true表示临时实例开启,注册为临时实例
spring.cloud.nacos.discovery.ephemeral=false
五、CMDB机理
如何打通CMDB,实现就近访问 - 阿里云云栖号 - 博客园 (cnblogs.com)
如何打通CMDB,实现就近访问-阿里云开发者社区 (aliyun.com)
CMDB在企业中一般是用来存放机器设备、服务、应用等相关的元数据信息,我们通过打通CMDB来实现同机房访问。至于说为什么要实现同机房访问这是由于一个请求也会应为地域距离而变得有更长延时,eg:通常来说如果请求与服务再同一个城市他们的相应一般在1ms但是如果两者相隔的是北京与上海这时候请求的延时可能就是要30ms了。
在Nacos中有关CMDB信息的获取有两个途径一个是通过本地存储的信息还有一个就是通过获取第三方系统的。至于说如何对接第三方系统Nacos中采用了SPI的策略来实现,实现完成之后再打开配置文件中相关的开关:
nacos.cmdb.loadDataAtStart=true
这时候我们就拿到第三方CMDB的信息,有了这些信息我们就可以用Selector实现同机房优先访问。CMDB实现对的就近访问实际上也是一种负载均衡策略。
这里有一个问题就是Nacos给客户端返回的是访问列表,那么也就是说当Nacos客户端连接Server的时候这个负载均衡是在客户端实现的?
六、环境隔离
https://nacos.io/zh-cn/blog/address-server.html
现实的项目场景中是有很多个不同的环境的,我们如何实现环境之间的隔离,这就是一个需要考虑的问题了,通常我们说的隔离可以有逻辑隔离以及物理隔离。
1、环境隔离带来的一些优点
当我们使用的是物理隔离的时候,一套系统就是一个独立的业务处理单元,在这个单元中包含所有的业务处理逻辑,一个用户的任何一个请求都会在这个单元中完成。这时候如果我们按照id对请求切割成了10个业务单元来进行处理,这时候就算一个处理单元中发生了异常影响的也只有1/10,这样就大大可提高了系统整体的可用性。
当一个系统出现故障这时候可以通过快速下发配置将系统请求的路由切换到另一个系统中,这时候就极大的加快了系统的恢复。在这里面起作用的不只是环境之间的隔离还有就是配置中心的配置下发。虽然能够理解但是至于说如何实现一个路由装换以及其中的设计原理还是不很能理解。
通过环境隔离功能来保证请求在指定的机器集群处理,开发和测试不需要做任何做任何配置,大大提高了研发效率。原来的话可能是需要测试的通知做大量的额host绑定配置JVM参数等。
2、初始化Nacos客户端实例的两种方式
- 直接告诉客户端nacos服务端的IP
- 告诉客户端一个endpoint,客户端通过HTTP请求到endpoint查询nacos服务端IP列表
在endpoint端配置网段和环境的映射关系,endpoint在接收到客户端的请求之后,根据客户端的来源IP所属网段,计算出该客户端所属环境,然后找到对应环境的IP列表返回给客户端。
Nacos 利用网段进行环境隔离,如果想要这么做我们要有几个前提条件:一台机器部署的应用都在一个环境内,这可以确保我们在endPoint的时候可以根据ip地址计算出这台机器对应的环境,那么就诞生出第二个必要条件我们应该要能探知到客户端的IP地址;同时我们还要保证一个进程中默认情况下只能连一个环境的Nacos,这样就不至于要连的Nacos被endPoint挡掉;最后既然我们是利用网段进行环境隔离的那么我们就必须对机器进行网段规划,否则不规则的网络使用也就谈不上利用网段惊醒一系列的治理
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-h00MqMJR-1632236494486)(F:\typroa\aimages\image-20210918165028153.png)]
3、地址服务器的实现
基于nginx实现,利用nginx的geo模块,做IP端和环境的映射,然后利用nginx返回静态文件内容。这一部分需要一定的Nginx相关知识储备,后续再做探索。
境隔离,如果想要这么做我们要有几个前提条件:一台机器部署的应用都在一个环境内,这可以确保我们在endPoint的时候可以根据ip地址计算出这台机器对应的环境,那么就诞生出第二个必要条件我们应该要能探知到客户端的IP地址;同时我们还要保证一个进程中默认情况下只能连一个环境的Nacos,这样就不至于要连的Nacos被endPoint挡掉;最后既然我们是利用网段进行环境隔离的那么我们就必须对机器进行网段规划,否则不规则的网络使用也就谈不上利用网段惊醒一系列的治理
3、地址服务器的实现
基于nginx实现,利用nginx的geo模块,做IP端和环境的映射,然后利用nginx返回静态文件内容。这一部分需要一定的Nginx相关知识储备,后续再做探索。