Nacos注册中心
大约 24 分钟
注册中心简介
微服务调用
- 基于 REST 实现的微服务架构由于其内部都是以 HTTP 协议实现数据交互处理,所以消费端可以直接进行服务端的调用,而这样的调用过程需要明确的知道服务节点的地址以及端口,而在现实的生产环境中,由于网络环境的多变性,以及服务的可维护性,这种直一旦服务提供端节点地址发生修改后都接消费端直接调用的模式就会出现严重的问题,需要及时的更新消费端,否则将无法获取到正确的服务数据。
微服务注册中心
- 为了更加合理的实现所有微服务节点的管理,就需要提供有统一的注册中心,每一个微服务启动后都自动的向注册中心中保存各自的配置信息,而后消费端可以根据指定的微服务的名称通过注册中心来查找其可用的节点信息,从而实现正确的调用,这样即便微服务的节点数据发生了改变(地址修改、节点注销、动态扩容),只要注册中心存在那么就不会影响到服务的正确调用。
CAP 原则
- 计算机专家 Eric Brewer 于 2000 年在 ACM 分布式计算原理专题讨论会(简称 PODC、Principles Of Distributed Computing)中提出的分布式系统设计要考虑三个核心要素:
- -致性(Consistency):同一时刻的同一请求的实例返回的结果相同所有的数据要求具有强一-致性(Strong Consistency);
- 可用性(Availability):所有实例的读写请求在一定时间内可以得到正确的响应;
- 分区容错性(Partition tolerance):在网络异常(挖断光缆、设备故障宕机)的情况下,系统仍能提供正常的服务;
- 以上的三个特点就是 CAP 原则(又称 CAP 定理)但是这三个特性不可能同时满尾,所以分布式条统设计要考虑的是在满足 P(分或 AP.
SpringCloud 注册中心比较
- 注册中心的本质在于节点数据信息的存储,但是如果要想开发出可用的注册中心则必须遵从 CAP 原则,这样开发与维护的成本较高,所以在 SpringCloud 开发中一般会存在有二类注册中心:Eureka、consul、Nacos
1、
public static final String DEPT_ADD_URL =
"http://provider-dept-8001:8001/provider/dept/add";
public static final String DEPT_GET_URL =
"http://provider-dept-8001:8001/provider/dept/get/"; // id是自己变更的
public static final String DEPT_LIST_URL =
"http://provider-dept-8001:8001/provider/dept/list";
public static final String DEPT_SPLIT_URL =
"http://provider-dept-8001:8001/provider/dept/split";
2、
https://docs.spring.io/spring-cloud-consul/docs/3.0.3/reference/html/
3、
https://docs.spring.io/spring-cloud-zookeeper/docs/3.0.3/reference/html/
4、
https://spring-cloud-alibaba-group.github.io/github-pages/hoxton/en-us/index.html
Nacos 技术架构
Nacos
- Nacos 是由阿里巴巴提供的一款注册与配置中心组件,官方地址:nacos.io,除了可以方便实现微服务的注册之外还可以快速的实现动态服务发现、服务配置、服务元数据以及流量管理,这样可以帮助用户在云服务时代更好的构建、交付、管理自己的微服务平台,更快的复用和组合业务服务,更快的交付商业创新的价值,从而为用户赢得市场
Nacos 一致性实现
- Nacos 实现了 CAP 原则中的 CP 原则与 AP 原则,而在实现 Nacos 集群数据一致性(CAP)的处理上,主要采用了 Distro(阿里私有协议)以及 RAFT(分布式共识)两种算法实现,其中 Distro 算法提供了 AP 支持,而 RAFT 算法提供了 CP 支持,RAFT 算法采用类似于 Paxos 算法机制,这样可以保证集群中会存在有一个领导者以及多个跟随者,由领导者负责发出数据更新的指令,而后所有的跟随者可以实现数据的一致性处理
获取 Nacos 应用组件
Nacos 代码托管
- Nacos 是一个开源项目,如果开发者要想使用 Nacos 服务组件,可以直接通过 GITHUB 获取 Nacos 的源代码。为了方便用户使用,Nacos2.x 会自动提供源代码以及打包后的应用部署组件包。
1、
https://github.com/alibaba/nacos/releases
2、
常规命令:git clone https://github.com/alibaba/nacos.git
3、
常规命令:git clone -b 2.0.0 https://github.com/alibaba/nacos.git
4、
mvn -Prelease-nacos -Dmaven.test.skip=true clean install -U
5、
localhost:8848/nacos
Linux 部署 Nacos 服务
1、
vi /etc/sysconfig/network-scripts/ifcfg-ens33
2、
IPADDR=192.168.190.158
3、
vi /etc/hostname
nacos-server
4、
vi /etc/hosts
192.168.190.158 nacos-server
5、
tar xzvf /var/ftp/nacos-server-2.0.2.tar.gz -C /usr/local/
6、
vi /usr/local/nacos/bin/startup.sh
vi /usr/local/nacos/bin/shutdown.sh
set ff
set ff=unix
7、
bash -f /usr/local/nacos/bin/startup.sh -m standalone
8、
firewall-cmd --zone=public --add-port=8848/tcp --permanent
firewall-cmd --zone=public --add-port=7848/tcp --permanent
firewall-cmd --zone=public --add-port=9848/tcp --permanent
firewall-cmd --zone=public --add-port=9849/tcp --permanent
firewall-cmd --reload
9、
nacos-server:8848/nacos
tar xzvf /mnt/resource/nacos-server-2.3.1.tar.gz -C /usr/local/
vim /usr/local/nacos/bin/startup.sh
export JAVA_HOME=/usr/local/jdk/jdk17/
/usr/local/nacos/bin/startup.sh -m standalone
sudo vim /usr/lib/systemd/system/nacos.service
[Unit]
Description=nacos
After=network.target
[Service]
Type=forking
ExecStart=bash -f /usr/local/nacos/bin/startup.sh -m standalone
ExecReload=/usr/local/nacos/bin/shutdown.sh
ExecStop=/usr/local/nacos/bin/shutdown.sh
# Let systemd restart this service always
Restart=always
PrivateTmp=true
[Install]
WantedBy=multi-user.target
Alias=nacos.service
# 在/lib/systemd/system中创建nacos.service的软连接(主要是为了升级方便)
sudo ln -s /usr/local/nacos/nacos.service /lib/systemd/system/nacos.service
# 让服务生效
systemctl daemon-reload
systemctl enable nacos.service
# 启动nacos服务
systemctl start nacos
Nacos 整合 MySQL 存储
1、
vi /etc/sysconfig/network-scripts/ifcfg-ens3
IPADDR=192.168.190.159
vi /etc/hostname
vi /etc/hosts
reboot
2、
service mysqld start
3、
/usr/local/mysql/bin/mysql -uroot -pmysqladmin -hnacos-mysql
4、
CREATE DATABASE nacos CHARACTER SET UTF8;
5、
USE nacos;
6、
scp /usr/local/nacos/conf/nacos-mysql.sql 192.168.190.159:/usr/local/src
7、
source /usr/local/src/nacos-mysql.sql
8、
vi /usr/local/nacos/conf/application.properties
9、
spring.datasource.platform=mysql
db.num=1
db.url.0=jdbc:mysql://192.168.190.159:3306/nacos?characterEncoding=utf8&connectTimeout=1000&socketTimeout=3000&autoReconnect=true&useUnicode=true&useSSL=false&serverTimezone=UTC
db.user.0=root
db.password.0=mysqladmin
10、
/usr/local/nacos/bin/shutdown.sh
11、
bash -f /usr/local/nacos/bin/startup.sh -m standalone
12、
http://nacos-server:8848/nacos
JAVA_OPT="{$JAVA_OPT} -server -Xms2g -Xmx2g -Xmn1g -XX:MetaspaceSize=128m -XX:MaxMetaspaceSize=320m"
Nacos 领域模型
Nacos 领域模型
- 由于项目应用环境的复杂度,同时考虑到各类数据的有效存储管理,在 Nacos 中对于数据的归类存储提供了不同的模型结构,分为:命名空间(NameSpace).分组(Group)、服务(Service)、集群(Cluster)、实例(Instance)或配置数据 ID(Datald)
Nacos 领域模型
- 在一个 Nacos 注册中心中使用不同的命名空间不同应用产品的分割,每一个产品都可能会拆分为若干个不同的项目模块,这样就可以通过分组来进行管理,最终一个项目模块中会包含有若干个微服务,而一个微服务又会存在有不同的集群环境以及应用节点。
创建 Nacos 命名空间
- 在使用 Nacos 进行项目开发前最重要的是需要创建一个新的命名空间,来实现不同的产品管理,本次将直接通过 Nacos 控制台创建一个“muyan”的命名空间
7d01da8b-0d61-4e63-baeb-884897d4155c
配置数据管理
Nacos 配置数据管理
- Nacos 提供有配置中心的功能,这样开发者就可以通过 Nacos 客户端实现配置数据集的发布、获取、更新、删除等常规操作,同时相关的客户端也可以通过 Nacos 的指定领域模型(命名空间、分组、数据 ID)获取数据,或者定义一个监听程序及时获取每一次更新后的数据内容。考虑到实际开发之中配置数据保存的多样性,在 Nacos 中支持有文本 JSON、XML、YML、属性等格式的配置数据存储,以满足不同的应用开发要求。
ConfigService 类结构
- 每一个 ConfigService 接口实例都可以直接实现 Nacos 配置数据的操作,而要想获取此接口实例,则可以通过 NacosFactory 工厂类所提供的 createConfigService()方法实现在该方法调用时需要传入配置操作所需要的 Nacos 属性内容(例如:Nacos 主机名称、命名空间 ID 等)这样就可以连接到指定的服务器,并在指定命名空间中进行操作(如果不设置命名空间则使用默认命名空间)
1、
// https://mvnrepository.com/artifact/com.alibaba.nacos/nacos-client
implementation group: 'com.alibaba.nacos', name: 'nacos-client', version: '2.0.2'
// https://mvnrepository.com/artifact/com.alibaba.nacos/nacos-client
implementation group: 'com.alibaba.nacos', name: 'nacos-client', version: '2.3.1'
2、
ext.versions = [ // 定义全部的依赖库版本号
springboot : '2.2.5.RELEASE', // SpringBoot版本号
springcloud : 'Hoxton.SR3', // SpringCloud版本号
alibabacloud : '2.2.1.RELEASE', // SpringCloudAlibaba版本号
lombok : '1.18.20', // Lombok版本号
junit : '5.6.3', // 配置JUnit测试工具的版本编号
junitPlatformLauncher: '1.6.3', // JUnit测试工具运行平台版本编号
mybatisPlus : '3.4.3', // MyBatisPlus的版本号
mysql : '8.0.25', // MySQL数据库驱动版本
druid : '1.2.6', // Druid版本号
swagger : '3.0.0', // Swagger版本号
nacos : '2.0.2', // Nacos版本号
]
ext.libraries = [ // 依赖库引入配置
'spring-boot-gradle-plugin' :
"org.springframework.boot:spring-boot-gradle-plugin:${versions.springboot}",
'spring-cloud-dependencies' :
"org.springframework.cloud:spring-cloud-dependencies:${versions.springcloud}",
'spring-cloud-alibaba-dependencies':
"com.alibaba.cloud:spring-cloud-alibaba-dependencies:${versions.alibabacloud}",
// 以下的配置为与项目用例测试有关的依赖
'junit-jupiter-api' :
"org.junit.jupiter:junit-jupiter-api:${versions.junit}",
'junit-vintage-engine' :
"org.junit.vintage:junit-vintage-engine:${versions.junit}",
'junit-jupiter-engine' :
"org.junit.jupiter:junit-jupiter-engine:${versions.junit}",
'junit-platform-launcher' :
"org.junit.platform:junit-platform-launcher:${versions.junitPlatformLauncher}",
'junit-platform-engine' :
"org.junit.platform:junit-platform-engine:${versions.junitPlatformLauncher}",
'junit-jupiter-params' :
"org.junit.jupiter:junit-jupiter-params:${versions.junit}",
'junit-bom' : "org.junit:junit-bom:${versions.junit}",
'junit-platform-commons' :
"org.junit.platform:junit-platform-commons:${versions.junitPlatformLauncher}",
// 以下的配置为Lombok组件有关的依赖
'lombok' : "org.projectlombok:lombok:${versions.lombok}",
// 以下的配置为数据库开发有关的依赖
'mybatis-plus-boot-starter' : "com.baomidou:mybatis-plus-boot-starter:${versions.mybatisPlus}",
'mysql-connector-java' : "mysql:mysql-connector-java:${versions.mysql}",
'druid' : "com.alibaba:druid:${versions.druid}",
// 以下的配置为Swagger有关的依赖库
'springfox-boot-starter' : "io.springfox:springfox-boot-starter:${versions.swagger}",
// 以下的配置为Nacos有关的依赖库
'nacos-client' : "com.alibaba.nacos:nacos-client:${versions.nacos}"
]
3、
project(":nacos-example") { // Nacos核心的讲解模块
dependencies {
implementation(project(":common-api")) // 导入公共的子模块
implementation(libraries.'nacos-client') // Nacos标准模块
}
}
4、
package com.yootk.nacos;
import com.alibaba.nacos.api.NacosFactory;
import com.alibaba.nacos.api.PropertyKeyConst;
import com.alibaba.nacos.api.config.ConfigService;
import java.util.Properties;
public class PublishNacosConfig { // 发布Nacos配置项
public static final String NACOS_SERVER = "nacos-server:8848"; // Naco连接地址
// 此时的命名空间使用的是自定义命名空间(名称为“muyan”,去Nacos中查找)
public static final String NAMESPACE = "0007211f-2731-495f-bf7e-6845bda78727"; // 命名空间
public static final String GROUP = "MICROCLOUD_GROUP"; // 分组
public static final String DATA_ID = "com.yootk.nacos.microcloud.config"; // 存储的KEY
public static void main(String[] args) throws Exception { // 沐言科技:www.yootk.com
String content = "edu.yootk.com"; // 要保存的配置项内容
Properties properties = new Properties(); // 将Nacos的相关属性进行配置
// 如果要想进行属性的配置,则一定要注意属性的KEY的名称
properties.put(PropertyKeyConst.SERVER_ADDR, NACOS_SERVER); // 服务地址属性
properties.put(PropertyKeyConst.NAMESPACE, NAMESPACE); // 命名空间
// 创建完连接之后,会将此连接包装为ConfigService对象实例返回,这里面实现各种数据操作
ConfigService configService = NacosFactory.createConfigService(properties); // 创建配置实例
boolean isOk = configService.publishConfig(DATA_ID, GROUP, content); // 发布配置项
System.out.println(isOk ? "Nacos配置项发布成功!" : "Nacos配置项发布失败!");
}
}
5、
package com.yootk.nacos;
import com.alibaba.nacos.api.NacosFactory;
import com.alibaba.nacos.api.PropertyKeyConst;
import com.alibaba.nacos.api.config.ConfigService;
import com.alibaba.nacos.api.config.listener.Listener;
import java.util.Properties;
import java.util.concurrent.Executor;
import java.util.concurrent.TimeUnit;
public class NacosConfigListener { // 发布Nacos配置项
public static final String NACOS_SERVER = "nacos-server:8848"; // Naco连接地址
// 此时的命名空间使用的是自定义命名空间(名称为“muyan”,去Nacos中查找)
public static final String NAMESPACE = "0007211f-2731-495f-bf7e-6845bda78727"; // 命名空间
public static final String GROUP = "MICROCLOUD_GROUP"; // 分组
public static final String DATA_ID = "com.yootk.nacos.microcloud.config"; // 存储的KEY
public static void main(String[] args) throws Exception { // 沐言科技:www.yootk.com
Properties properties = new Properties(); // 将Nacos的相关属性进行配置
// 如果要想进行属性的配置,则一定要注意属性的KEY的名称
properties.put(PropertyKeyConst.SERVER_ADDR, NACOS_SERVER); // 服务地址属性
properties.put(PropertyKeyConst.NAMESPACE, NAMESPACE); // 命名空间
// 创建完连接之后,会将此连接包装为ConfigService对象实例返回,这里面实现各种数据操作
ConfigService configService = NacosFactory.createConfigService(properties); // 创建配置实例
String content = configService.getConfig(DATA_ID, GROUP, 50000); // 获取配置项
System.err.println("【初始化配置】" + DATA_ID + " = " + content);
// 一般来讲是在服务启动的时候进行配置数据的读取,但是也需要提供有配置数据的更新
configService.addListener(DATA_ID, GROUP, new Listener() {
@Override
public Executor getExecutor() {
return null;
}
@Override
public void receiveConfigInfo(String configInfo) { // 每当更新后可以自动的接收更新
System.err.println("【配置项更新】" + DATA_ID + " = " + configInfo);
}
});
TimeUnit.MINUTES.sleep(Long.MAX_VALUE); // 持续休眠
}
}
实例数据管理
Nacos 实例管理
- Nacos 作为注册中心,可以方便的管理微服务中的所有集群节点数据(主机地址、端口)每当有新的节点启动或者已有节点下线时都可以及时实现节点数据更新。其他的外部客户端也可以通过该注册中心的指定服务名称获取该服务下对应的所有节点数据,并且根据需要实现节点服务的调用。
1、
package com.yootk.nacos;
import com.alibaba.nacos.api.PropertyKeyConst;
import com.alibaba.nacos.api.naming.NamingFactory;
import com.alibaba.nacos.api.naming.NamingService;
import java.util.Properties;
import java.util.concurrent.TimeUnit;
public class RegisterNacosInstance { // 服务实例注册
public static final String NACOS_SERVER = "nacos-server:8848"; // Naco连接地址
// 此时的命名空间使用的是自定义命名空间(名称为“muyan”,去Nacos中查找)
public static final String NAMESPACE = "0007211f-2731-495f-bf7e-6845bda78727"; // 命名空间
public static final String GROUP = "MICROCLOUD_GROUP"; // 分组
public static final String INSTANCE_ID = "micro.provider.dept"; // 存储的KEY
public static void main(String[] args) throws Exception { // 沐言科技:www.yootk.com
Properties properties = new Properties(); // 将Nacos的相关属性进行配置
properties.put(PropertyKeyConst.SERVER_ADDR, NACOS_SERVER); // 服务地址属性
properties.put(PropertyKeyConst.NAMESPACE, NAMESPACE); // 命名空间
// 此时要发布的并不是配置项,而是服务项,所以就需要创建命名服务实例
NamingService naming = NamingFactory.createNamingService(properties);
// 准备向Nacos注册中心进行服务的注册处理
naming.registerInstance(INSTANCE_ID, GROUP, "192.168.9.19",
8888, "DeptProviderCluster");
// 当服务注册成功之后就需要持续的向Nacos发送心跳,不发送心跳表示嗝屁
TimeUnit.MINUTES.sleep(Long.MAX_VALUE); // 保证进程不关闭
}
}
2、
package com.yootk.nacos;
import com.alibaba.nacos.api.PropertyKeyConst;
import com.alibaba.nacos.api.naming.NamingFactory;
import com.alibaba.nacos.api.naming.NamingService;
import com.alibaba.nacos.api.naming.pojo.Instance;
import java.util.List;
import java.util.Properties;
import java.util.concurrent.TimeUnit;
public class NacosInstanceList { // 服务实例注册
public static final String NACOS_SERVER = "nacos-server:8848"; // Naco连接地址
// 此时的命名空间使用的是自定义命名空间(名称为“muyan”,去Nacos中查找)
public static final String NAMESPACE = "0007211f-2731-495f-bf7e-6845bda78727"; // 命名空间
public static final String GROUP = "MICROCLOUD_GROUP"; // 分组
public static final String INSTANCE_ID = "micro.provider.dept"; // 存储的KEY
public static void main(String[] args) throws Exception { // 沐言科技:www.yootk.com
Properties properties = new Properties(); // 将Nacos的相关属性进行配置
properties.put(PropertyKeyConst.SERVER_ADDR, NACOS_SERVER); // 服务地址属性
properties.put(PropertyKeyConst.NAMESPACE, NAMESPACE); // 命名空间
// 此时要发布的并不是配置项,而是服务项,所以就需要创建命名服务实例
NamingService naming = NamingFactory.createNamingService(properties);
List<Instance> instances = naming.getAllInstances(INSTANCE_ID, GROUP); // 分组很重要
for (Instance instance : instances) {
System.err.println(instance);
}
TimeUnit.MINUTES.sleep(Long.MAX_VALUE); // 保证进程不关闭
}
}
3、
package com.yootk.nacos;
import com.alibaba.nacos.api.PropertyKeyConst;
import com.alibaba.nacos.api.naming.NamingFactory;
import com.alibaba.nacos.api.naming.NamingService;
import com.alibaba.nacos.api.naming.listener.NamingEvent;
import com.alibaba.nacos.api.naming.pojo.Instance;
import java.util.List;
import java.util.Properties;
import java.util.concurrent.TimeUnit;
public class NacosInstanceListener { // 服务实例注册
public static final String NACOS_SERVER = "nacos-server:8848"; // Naco连接地址
// 此时的命名空间使用的是自定义命名空间(名称为“muyan”,去Nacos中查找)
public static final String NAMESPACE = "0007211f-2731-495f-bf7e-6845bda78727"; // 命名空间
public static final String GROUP = "MICROCLOUD_GROUP"; // 分组
public static final String INSTANCE_ID = "micro.provider.dept"; // 存储的KEY
public static void main(String[] args) throws Exception { // 沐言科技:www.yootk.com
Properties properties = new Properties(); // 将Nacos的相关属性进行配置
properties.put(PropertyKeyConst.SERVER_ADDR, NACOS_SERVER); // 服务地址属性
properties.put(PropertyKeyConst.NAMESPACE, NAMESPACE); // 命名空间
// 此时要发布的并不是配置项,而是服务项,所以就需要创建命名服务实例
NamingService naming = NamingFactory.createNamingService(properties);
naming.subscribe(INSTANCE_ID, GROUP,
event -> {
if (event instanceof NamingEvent) { // 实例更改事件
System.out.println(((NamingEvent) event).getServiceName()); // 服务名称
System.out.println(((NamingEvent) event).getInstances()); // 实例的列表
}
});
TimeUnit.MINUTES.sleep(Long.MAX_VALUE); // 保证进程不关闭
}
}
REST 访问配置
Nacos 开放平台
- 除了使用“nacos-client”依赖库实现 Nacos 数据管理之外,也可以利用 Nacos 提供的开放平台基于 REST 接口的形式实现数据的处理操作,这样就可以使用任何的异构平台实现 Nacos 数据操作,
1、
https://nacos.io/zh-cn/docs/open-api.html
2、
http://nacos-server:8848/nacos/v1/console/namespaces?customNamespaceId=&namespaceName=yootk&namespaceDesc=yootk.com
7d01da8b-0d61-4e63-baeb-884897d4155c
3、
curl -X POST 'http://nacos-server:8848/nacos/v1/cs/configs' -d 'tenant=7d01da8b-0d61-4e63-baeb-884897d4155c&dataId=dept.provider&group=MICRO_REST&content=www.yootk.com'
4、
http://nacos-server:8848/nacos/v1/ns/instance?namespaceId=7d01da8b-0d61-4e63-baeb-884897d4155c&ip=192.168.1.27&port=8090&serviceName=dept.service&groupName=MICRO_REST
curl -X POST 'http://nacos-server:8848/nacos/v1/ns/instance?namespaceId=7d01da8b-0d61-4e63-baeb-884897d4155c&ip=192.168.1.27&port=8090&serviceName=dept.service&groupName=MICRO_REST'
微服务注册
Nacos 服务注册
- SpringCloudAlibaba 体系中提供了对 Nacos 注册中心的自动配置支持,开发者只需要在微服务的提供端提供 Nacos 发现服务(spring-cloud-starter-alibaba-nacos-discovery)与配置(spring-cloud-starter-alibaba-nacos-config)依赖,而 Nacos 配置依赖启动优先级较高,所以就需要在项目中提供 bootstrap.yml 配置文件,并在此配置文件中定义 Nacos 相关连接信息,随后再编写 application.yml 配置文件定义 Nacos 发现配置
1、
// https://mvnrepository.com/artifact/com.alibaba.cloud/spring-cloud-starter-alibaba-nacos-discovery
implementation group: 'com.alibaba.cloud', name: 'spring-cloud-starter-alibaba-nacos-discovery', version: '2.2.6.RELEASE'
gradle dependencies
implementation('org.springframework.cloud:spring-cloud-starter-bootstrap')
2、
// https://mvnrepository.com/artifact/com.alibaba.cloud/spring-cloud-starter-alibaba-nacos-config
implementation group: 'com.alibaba.cloud', name: 'spring-cloud-starter-alibaba-nacos-config', version: '2.2.6.RELEASE'
3、
project(":provider-dept-8001") { // 部门微服务
dependencies {
implementation(project(":common-api")) // 导入公共的子模块
implementation(libraries.'mybatis-plus-boot-starter')
implementation(libraries.'mysql-connector-java')
implementation(libraries.'druid')
implementation(libraries.'springfox-boot-starter')
implementation('org.springframework.boot:spring-boot-starter-security')
// 以下的依赖库为Nacos注册中心所需要的依赖配置
implementation('com.alibaba.cloud:spring-cloud-starter-alibaba-nacos-discovery') {
exclude group: 'com.alibaba.nacos', module: 'nacos-client' // 移除旧版本的Nacos依赖
}
implementation('com.alibaba.cloud:spring-cloud-starter-alibaba-nacos-config') {
exclude group: 'com.alibaba.nacos', module: 'nacos-client' // 移除旧版本的Nacos依赖
}
implementation(libraries.'nacos-client') // 引入与当前的Nacos匹配的依赖库
}
}
4、
spring: # Spring配置项
cloud: # SpringCloud配置项
nacos: # Nacos注册中心的配置
config: # gRPC通讯配置
server-addr: nacos-server:8848 # Nacos地址
5、
spring:
application: # 配置应用信息
name: dept.provider # 是微服务的名称
cloud: # Cloud配置
nacos: # Nacos注册中心配置
discovery: # 发现服务
service: ${spring.application.name} # 使用微服务的名称作为注册的服务名称
server-addr: nacos-server:8848 # Nacos服务地址
6、
http://nacos-server:8848/nacos/
配置 Nacos 注册信息
1、
96c23d77-8d08-4648-b750-1217845607ee
2、
spring: # Spring配置项
cloud: # SpringCloud配置项
nacos: # Nacos注册中心的配置
config: # gRPC通讯配置
server-addr: nacos-server:8848 # Nacos地址
namespace: 96c23d77-8d08-4648-b750-1217845607ee # 命名空间ID
group: MICROCLOUD_GROUP # 一般建议大写
cluster-name: YootkCluster # 配置集群名称
3、
spring:
application: # 配置应用信息
name: dept.provider # 是微服务的名称
cloud: # Cloud配置
nacos: # Nacos注册中心配置
discovery: # 发现服务
service: ${spring.application.name} # 使用微服务的名称作为注册的服务名称
server-addr: nacos-server:8848 # Nacos服务地址
namespace: 96c23d77-8d08-4648-b750-1217845607ee # 命名空间ID
group: MICROCLOUD_GROUP # 一般建议大写
cluster-name: YootkCluster # 配置集群名称
metadata: # 根据自身的需要配置元数据
version: 1.0 # 自定义元数据项
company: 沐言科技 # 自定义元数据项
url: www.yootk.com # 自定义元数据项
author: 李兴华(爆可爱的小李老师) # 自定义元数据项
Nacos 安全注册
Nacos 安全管理
- 由于当前的 Nacos 服务中并没有启用任何的安全机制,所以任何的微服务只要填写了正确的服务地址就都可以进行服务注册,也都可以通过注册中心获取到所有的实例信息这样一来是既不安全的
1、
https://nacos.io/zh-cn/docs/auth.html
2、
vi /usr/local/nacos/conf/application.properties
3、
nacos.core.auth.enabled=true
4、
/usr/local/nacos/bin/shutdown.sh
5、
bash -f /usr/local/nacos/bin/startup.sh -m standalone
6、
spring: # Spring配置项
cloud: # SpringCloud配置项
nacos: # Nacos注册中心的配置
config: # gRPC通讯配置
server-addr: nacos-server:8848 # Nacos地址
namespace: 96c23d77-8d08-4648-b750-1217845607ee # 命名空间ID
group: MICROCLOUD_GROUP # 一般建议大写
cluster-name: YootkCluster # 配置集群名称
username: muyan # 用户名
password: yootk # 密码
7、
spring:
application: # 配置应用信息
name: dept.provider # 是微服务的名称
cloud: # Cloud配置
nacos: # Nacos注册中心配置
discovery: # 发现服务
service: ${spring.application.name} # 使用微服务的名称作为注册的服务名称
server-addr: nacos-server:8848 # Nacos服务地址
namespace: 96c23d77-8d08-4648-b750-1217845607ee # 命名空间ID
group: MICROCLOUD_GROUP # 一般建议大写
cluster-name: YootkCluster # 配置集群名称
username: muyan # 用户名
password: yootk # 密码
metadata: # 根据自身的需要配置元数据
version: 1.0 # 自定义元数据项
company: 沐言科技 # 自定义元数据项
url: www.yootk.com # 自定义元数据项
author: 李兴华(爆可爱的小李老师) # 自定义元数据项
Nacos 自动配置
1、
org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
com.alibaba.cloud.nacos.discovery.NacosDiscoveryAutoConfiguration,\
com.alibaba.cloud.nacos.ribbon.RibbonNacosAutoConfiguration,\
com.alibaba.cloud.nacos.endpoint.NacosDiscoveryEndpointAutoConfiguration,\
com.alibaba.cloud.nacos.registry.NacosServiceRegistryAutoConfiguration,\
com.alibaba.cloud.nacos.discovery.NacosDiscoveryClientConfiguration,\
com.alibaba.cloud.nacos.discovery.reactive.NacosReactiveDiscoveryClientConfiguration,\
com.alibaba.cloud.nacos.discovery.configclient.NacosConfigServerAutoConfiguration
org.springframework.cloud.bootstrap.BootstrapConfiguration=\
com.alibaba.cloud.nacos.discovery.configclient.NacosDiscoveryClientConfigServiceBootstrapConfiguration
2、
org.springframework.cloud.bootstrap.BootstrapConfiguration=\
com.alibaba.cloud.nacos.NacosConfigBootstrapConfiguration
org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
com.alibaba.cloud.nacos.NacosConfigAutoConfiguration,\
com.alibaba.cloud.nacos.endpoint.NacosConfigEndpointAutoConfiguration
org.springframework.boot.diagnostics.FailureAnalyzer=\
com.alibaba.cloud.nacos.diagnostics.analyzer.NacosConnectionFailureAnalyzer
NacosConfigBootstrapConfiguration
1、
package com.alibaba.cloud.nacos;
import java.util.Objects;
import com.alibaba.cloud.nacos.diagnostics.analyzer.NacosConnectionFailureException;
import com.alibaba.nacos.api.NacosFactory;
import com.alibaba.nacos.api.config.ConfigService;
import com.alibaba.nacos.api.exception.NacosException;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
public class NacosConfigManager {
private static final Logger log = LoggerFactory.getLogger(NacosConfigManager.class);
private static ConfigService service = null; // 最终的创建存储
private NacosConfigProperties nacosConfigProperties; // Nacos配置属性
public NacosConfigManager(NacosConfigProperties nacosConfigProperties) {
this.nacosConfigProperties = nacosConfigProperties;
// Nacos配置属性里面提供了Nacos地址、领域模型、认证信息,这些信息都需要在连接时使用
createConfigService(nacosConfigProperties); // 创建ConfigService接口方法
}
static ConfigService createConfigService(
NacosConfigProperties nacosConfigProperties) { // 直接返回了NacosClient的接口实例
if (Objects.isNull(service)) {
// 在一个Nacos客户端里面应该只保留有一个ConfigService接口实例,多个是没有任何意义的
synchronized (NacosConfigManager.class) { // 创建过程应该进行同步处理
try {
if (Objects.isNull(service)) { // 饿汉式单例设计模式
service = NacosFactory.createConfigService(
nacosConfigProperties.assembleConfigServiceProperties());// 属性类型转换
}
}
catch (NacosException e) {
log.error(e.getMessage());
throw new NacosConnectionFailureException(
nacosConfigProperties.getServerAddr(), e.getMessage(), e);
}
}
}
return service;
}
public ConfigService getConfigService() {
if (Objects.isNull(service)) {
createConfigService(this.nacosConfigProperties);
}
return service;
}
public NacosConfigProperties getNacosConfigProperties() {
return nacosConfigProperties;
}
}
2、
package com.alibaba.nacos.api;
import com.alibaba.nacos.api.config.ConfigFactory;
import com.alibaba.nacos.api.config.ConfigService;
import com.alibaba.nacos.api.exception.NacosException;
import com.alibaba.nacos.api.naming.NamingFactory;
import com.alibaba.nacos.api.naming.NamingMaintainFactory;
import com.alibaba.nacos.api.naming.NamingMaintainService;
import com.alibaba.nacos.api.naming.NamingService;
import java.util.Properties;
public class NacosFactory {
public static ConfigService createConfigService(
Properties properties) throws NacosException {// 创建具体的服务
return ConfigFactory.createConfigService(properties);
}
public static ConfigService createConfigService(String serverAddr) throws NacosException {
return ConfigFactory.createConfigService(serverAddr);
}
public static NamingService createNamingService(String serverAddr) throws NacosException {
return NamingFactory.createNamingService(serverAddr);
}
public static NamingService createNamingService(Properties properties) throws NacosException {
return NamingFactory.createNamingService(properties);
}
public static NamingMaintainService createMaintainService(String serverAddr) throws NacosException {
return NamingMaintainFactory.createMaintainService(serverAddr);
}
public static NamingMaintainService createMaintainService(Properties properties) throws NacosException {
return NamingMaintainFactory.createMaintainService(properties);
}
}
3、
package com.alibaba.nacos.api.config;
import com.alibaba.nacos.api.PropertyKeyConst;
import com.alibaba.nacos.api.exception.NacosException;
import java.lang.reflect.Constructor;
import java.util.Properties;
public class ConfigFactory {
public static ConfigService createConfigService(
Properties properties) throws NacosException { // 工厂处理方法
try {
Class<?> driverImplClass = Class.forName(
"com.alibaba.nacos.client.config.NacosConfigService"); // 反射加载
Constructor constructor = driverImplClass.getConstructor(Properties.class);
ConfigService vendorImpl = (ConfigService) constructor.newInstance(properties);
return vendorImpl;
} catch (Throwable e) {
throw new NacosException(NacosException.CLIENT_INVALID_PARAM, e);
}
}
}
4、
/*
* Copyright 1999-2018 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.
*/
package com.alibaba.nacos.client.config;
import com.alibaba.nacos.api.PropertyKeyConst;
import com.alibaba.nacos.api.common.Constants;
import com.alibaba.nacos.api.config.ConfigService;
import com.alibaba.nacos.api.config.ConfigType;
import com.alibaba.nacos.api.config.listener.Listener;
import com.alibaba.nacos.api.exception.NacosException;
import com.alibaba.nacos.client.config.filter.impl.ConfigFilterChainManager;
import com.alibaba.nacos.client.config.filter.impl.ConfigRequest;
import com.alibaba.nacos.client.config.filter.impl.ConfigResponse;
import com.alibaba.nacos.client.config.http.ServerHttpAgent;
import com.alibaba.nacos.client.config.impl.ClientWorker;
import com.alibaba.nacos.client.config.impl.LocalConfigInfoProcessor;
import com.alibaba.nacos.client.config.impl.LocalEncryptedDataKeyProcessor;
import com.alibaba.nacos.client.config.impl.ServerListManager;
import com.alibaba.nacos.client.config.utils.ContentUtils;
import com.alibaba.nacos.client.config.utils.ParamUtils;
import com.alibaba.nacos.client.utils.LogUtils;
import com.alibaba.nacos.client.utils.ParamUtil;
import com.alibaba.nacos.client.utils.ValidatorUtils;
import com.alibaba.nacos.common.utils.StringUtils;
import org.slf4j.Logger;
import java.util.Arrays;
import java.util.Properties;
/**
* Config Impl.
*
* @author Nacos
*/
@SuppressWarnings("PMD.ServiceOrDaoClassShouldEndWithImplRule")
public class NacosConfigService implements ConfigService {
private static final Logger LOGGER = LogUtils.logger(NacosConfigService.class);
private static final String UP = "UP";
private static final String DOWN = "DOWN";
/**
* will be deleted in 2.0 later versions
*/
@Deprecated
ServerHttpAgent agent = null;
/**
* long polling.
*/
private final ClientWorker worker;
private String namespace;
private final ConfigFilterChainManager configFilterChainManager;
public NacosConfigService(Properties properties) throws NacosException {
ValidatorUtils.checkInitParam(properties);
initNamespace(properties);
this.configFilterChainManager = new ConfigFilterChainManager(properties);
ServerListManager serverListManager = new ServerListManager(properties);
serverListManager.start();
this.worker = new ClientWorker(this.configFilterChainManager, serverListManager, properties);
// will be deleted in 2.0 later versions
agent = new ServerHttpAgent(serverListManager);
}
private void initNamespace(Properties properties) {
namespace = ParamUtil.parseNamespace(properties);
properties.put(PropertyKeyConst.NAMESPACE, namespace);
}
@Override
public String getConfig(String dataId, String group, long timeoutMs) throws NacosException {
return getConfigInner(namespace, dataId, group, timeoutMs);
}
@Override
public String getConfigAndSignListener(String dataId, String group, long timeoutMs, Listener listener)
throws NacosException {
String content = getConfig(dataId, group, timeoutMs);
worker.addTenantListenersWithContent(dataId, group, content, Arrays.asList(listener));
return content;
}
@Override
public void addListener(String dataId, String group, Listener listener) throws NacosException {
worker.addTenantListeners(dataId, group, Arrays.asList(listener));
}
@Override
public boolean publishConfig(String dataId, String group, String content) throws NacosException {
return publishConfig(dataId, group, content, ConfigType.getDefaultType().getType());
}
@Override
public boolean publishConfig(String dataId, String group, String content, String type) throws NacosException {
return publishConfigInner(namespace, dataId, group, null, null, null, content, type, null);
}
@Override
public boolean publishConfigCas(String dataId, String group, String content, String casMd5) throws NacosException {
return publishConfigInner(namespace, dataId, group, null, null, null, content,
ConfigType.getDefaultType().getType(), casMd5);
}
@Override
public boolean publishConfigCas(String dataId, String group, String content, String casMd5, String type)
throws NacosException {
return publishConfigInner(namespace, dataId, group, null, null, null, content, type, casMd5);
}
@Override
public boolean removeConfig(String dataId, String group) throws NacosException {
return removeConfigInner(namespace, dataId, group, null);
}
@Override
public void removeListener(String dataId, String group, Listener listener) {
worker.removeTenantListener(dataId, group, listener);
}
private String getConfigInner(String tenant, String dataId, String group, long timeoutMs) throws NacosException {
group = blank2defaultGroup(group);
ParamUtils.checkKeyParam(dataId, group);
ConfigResponse cr = new ConfigResponse();
cr.setDataId(dataId);
cr.setTenant(tenant);
cr.setGroup(group);
// use local config first
String content = LocalConfigInfoProcessor.getFailover(worker.getAgentName(), dataId, group, tenant);
if (content != null) {
LOGGER.warn("[{}] [get-config] get failover ok, dataId={}, group={}, tenant={}, config={}",
worker.getAgentName(), dataId, group, tenant, ContentUtils.truncateContent(content));
cr.setContent(content);
String encryptedDataKey = LocalEncryptedDataKeyProcessor
.getEncryptDataKeyFailover(agent.getName(), dataId, group, tenant);
cr.setEncryptedDataKey(encryptedDataKey);
configFilterChainManager.doFilter(null, cr);
content = cr.getContent();
return content;
}
try {
ConfigResponse response = worker.getServerConfig(dataId, group, tenant, timeoutMs, false);
cr.setContent(response.getContent());
cr.setEncryptedDataKey(response.getEncryptedDataKey());
configFilterChainManager.doFilter(null, cr);
content = cr.getContent();
return content;
} catch (NacosException ioe) {
if (NacosException.NO_RIGHT == ioe.getErrCode()) {
throw ioe;
}
LOGGER.warn("[{}] [get-config] get from server error, dataId={}, group={}, tenant={}, msg={}",
worker.getAgentName(), dataId, group, tenant, ioe.toString());
}
LOGGER.warn("[{}] [get-config] get snapshot ok, dataId={}, group={}, tenant={}, config={}",
worker.getAgentName(), dataId, group, tenant, ContentUtils.truncateContent(content));
content = LocalConfigInfoProcessor.getSnapshot(worker.getAgentName(), dataId, group, tenant);
cr.setContent(content);
String encryptedDataKey = LocalEncryptedDataKeyProcessor
.getEncryptDataKeyFailover(agent.getName(), dataId, group, tenant);
cr.setEncryptedDataKey(encryptedDataKey);
configFilterChainManager.doFilter(null, cr);
content = cr.getContent();
return content;
}
private String blank2defaultGroup(String group) {
return (StringUtils.isBlank(group)) ? Constants.DEFAULT_GROUP : group.trim();
}
private boolean removeConfigInner(String tenant, String dataId, String group, String tag) throws NacosException {
group = blank2defaultGroup(group);
ParamUtils.checkKeyParam(dataId, group);
return worker.removeConfig(dataId, group, tenant, tag);
}
private boolean publishConfigInner(String tenant, String dataId, String group, String tag, String appName,
String betaIps, String content, String type, String casMd5) throws NacosException {
group = blank2defaultGroup(group);
ParamUtils.checkParam(dataId, group, content);
ConfigRequest cr = new ConfigRequest();
cr.setDataId(dataId);
cr.setTenant(tenant);
cr.setGroup(group);
cr.setContent(content);
cr.setType(type);
configFilterChainManager.doFilter(cr, null);
content = cr.getContent();
String encryptedDataKey = (String) cr.getParameter("encryptedDataKey");
return worker
.publishConfig(dataId, group, tenant, appName, tag, betaIps, content, encryptedDataKey, casMd5, type);
}
@Override
public String getServerStatus() {
if (worker.isHealthServer()) {
return UP;
} else {
return DOWN;
}
}
@Override
public void shutDown() throws NacosException {
worker.shutdown();
}
}
@EnableDiscoveryClient 注解
1、
@EnableDiscoveryClient
2、
package org.springframework.cloud.client.discovery;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.LinkedHashMap;
import java.util.List;
import org.springframework.cloud.commons.util.SpringFactoryImportSelector;
import org.springframework.core.Ordered;
import org.springframework.core.annotation.AnnotationAttributes;
import org.springframework.core.annotation.Order;
import org.springframework.core.env.ConfigurableEnvironment;
import org.springframework.core.env.Environment;
import org.springframework.core.env.MapPropertySource;
import org.springframework.core.type.AnnotationMetadata;
@Order(Ordered.LOWEST_PRECEDENCE - 100)
public class EnableDiscoveryClientImportSelector
extends SpringFactoryImportSelector<EnableDiscoveryClient> {
@Override
public String[] selectImports(AnnotationMetadata metadata) {
String[] imports = super.selectImports(metadata);
// 获取拥有此注解的全部的配置属性的信息
AnnotationAttributes attributes = AnnotationAttributes.fromMap(
metadata.getAnnotationAttributes(getAnnotationClass().getName(), true));
boolean autoRegister = attributes.getBoolean("autoRegister"); // 是否为自动注册
if (autoRegister) { // 如果使用了自动注册
List<String> importsList = new ArrayList<>(Arrays.asList(imports));
importsList.add(
"org.springframework.cloud.client.serviceregistry" +
".AutoServiceRegistrationConfiguration");
imports = importsList.toArray(new String[0]);
}
else { // 不使用自动注册处理
Environment env = getEnvironment();// 获取系统环境自动配置选项
if (ConfigurableEnvironment.class.isInstance(env)) {
ConfigurableEnvironment configEnv = (ConfigurableEnvironment) env;
LinkedHashMap<String, Object> map = new LinkedHashMap<>();
map.put("spring.cloud.service-registry.auto-registration.enabled", false);
MapPropertySource propertySource = new MapPropertySource(
"springCloudDiscoveryClient", map);
configEnv.getPropertySources().addLast(propertySource);
}
}
return imports;
}
@Override
protected boolean isEnabled() {
return getEnvironment().getProperty("spring.cloud.discovery.enabled",
Boolean.class, Boolean.TRUE);
}
@Override
protected boolean hasDefaultFactory() {
return true;
}
}
NacosServiceRegistryAutoConfiguration
1、
package com.alibaba.cloud.nacos.registry;
import com.alibaba.cloud.nacos.ConditionalOnNacosDiscoveryEnabled;
import com.alibaba.cloud.nacos.NacosDiscoveryProperties;
import com.alibaba.cloud.nacos.discovery.NacosDiscoveryAutoConfiguration;
import org.springframework.boot.autoconfigure.AutoConfigureAfter;
import org.springframework.boot.autoconfigure.condition.ConditionalOnBean;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.cloud.client.serviceregistry
.AutoServiceRegistrationAutoConfiguration;
import org.springframework.cloud.client.serviceregistry.AutoServiceRegistrationConfiguration;
import org.springframework.cloud.client.serviceregistry.AutoServiceRegistrationProperties;
import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
@Configuration(proxyBeanMethods = false)
@EnableConfigurationProperties
@ConditionalOnNacosDiscoveryEnabled
// application.yml中定义的属性环境,如果没有配置此属性的内容则默认为true
@ConditionalOnProperty(value = "spring.cloud.service-registry.auto-registration.enabled",
matchIfMissing = true)
@AutoConfigureAfter({
AutoServiceRegistrationConfiguration.class, // 服务自动注册
AutoServiceRegistrationAutoConfiguration.class, // 自动服务注册
NacosDiscoveryAutoConfiguration.class }) // Nacos自动配置
public class NacosServiceRegistryAutoConfiguration {
@Bean
public NacosServiceRegistry nacosServiceRegistry( // 获取了Nacos注册对象实例
NacosDiscoveryProperties nacosDiscoveryProperties) { // Nacos配置属性
return new NacosServiceRegistry(nacosDiscoveryProperties);
}
@Bean
@ConditionalOnBean(AutoServiceRegistrationProperties.class) // 自动服务注册属性
public NacosRegistration nacosRegistration(
NacosDiscoveryProperties nacosDiscoveryProperties,
ApplicationContext context) {
return new NacosRegistration(nacosDiscoveryProperties, context); // Nacos注册实例
}
@Bean
@ConditionalOnBean(AutoServiceRegistrationProperties.class)
public NacosAutoServiceRegistration nacosAutoServiceRegistration(
NacosServiceRegistry registry,
AutoServiceRegistrationProperties autoServiceRegistrationProperties,
NacosRegistration registration) {
return new NacosAutoServiceRegistration(registry,
autoServiceRegistrationProperties, registration); // Nacos自动注册实例
}
}
2、
package com.alibaba.cloud.nacos.registry;
import java.util.List;
import com.alibaba.cloud.nacos.NacosDiscoveryProperties;
import com.alibaba.nacos.api.naming.NamingService;
import com.alibaba.nacos.api.naming.pojo.Instance;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.cloud.client.serviceregistry.Registration;
import org.springframework.cloud.client.serviceregistry.ServiceRegistry;
import org.springframework.util.StringUtils;
import static org.springframework.util.ReflectionUtils.rethrowRuntimeException;
public class NacosServiceRegistry implements ServiceRegistry<Registration> {
private static final Logger log = LoggerFactory.getLogger(NacosServiceRegistry.class);
private final NacosDiscoveryProperties nacosDiscoveryProperties;
private final NamingService namingService;
public NacosServiceRegistry(NacosDiscoveryProperties nacosDiscoveryProperties) {
this.nacosDiscoveryProperties = nacosDiscoveryProperties;
this.namingService = nacosDiscoveryProperties.namingServiceInstance();
}
@Override
public void register(Registration registration) { // 服务注册
if (StringUtils.isEmpty(registration.getServiceId())) { // 是否为空
log.warn("No service to register for nacos client...");
return;
}
String serviceId = registration.getServiceId(); // 服务ID
String group = nacosDiscoveryProperties.getGroup(); // 获取注册组
Instance instance = getNacosInstanceFromRegistration(registration); // 获取实例
try {
namingService.registerInstance(serviceId, group, instance);
log.info("nacos registry, {} {} {}:{} register finished", group, serviceId,
instance.getIp(), instance.getPort());
}
catch (Exception e) {
log.error("nacos registry, {} register failed...{},", serviceId,
registration.toString(), e);
rethrowRuntimeException(e);
}
}
private Instance getNacosInstanceFromRegistration(Registration registration) {
Instance instance = new Instance(); // 定义注册实例
instance.setIp(registration.getHost());// 获取微服务的主机地址
instance.setPort(registration.getPort());// 获取微服务的端口号
instance.setWeight(nacosDiscoveryProperties.getWeight());// 获取权重
instance.setClusterName(nacosDiscoveryProperties.getClusterName());// 获取集群名称
instance.setMetadata(registration.getMetadata());// 获取元数据
return instance;
}
}
3、
package com.alibaba.nacos.client.naming;
@SuppressWarnings("PMD.ServiceOrDaoClassShouldEndWithImplRule")
public class NacosNamingService implements NamingService {
private NamingClientProxy clientProxy;
@Override
public void registerInstance(String serviceName, String groupName,
Instance instance) throws NacosException {
NamingUtils.checkInstanceIsLegal(instance);
clientProxy.registerService(serviceName, groupName, instance);
}
}
4、
package com.alibaba.nacos.client.naming.remote.gprc;
public class NamingGrpcClientProxy extends AbstractNamingClientProxy {
private final String namespaceId;
private final String uuid;
private final Long requestTimeout;
private final RpcClient rpcClient;
private final NamingGrpcConnectionEventListener namingGrpcConnectionEventListener;
@Override
public void registerService(String serviceName, String groupName, Instance instance) throws NacosException {
NAMING_LOGGER.info("[REGISTER-SERVICE] {} registering service {} with instance {}", namespaceId, serviceName, instance);
namingGrpcConnectionEventListener.cacheInstanceForRedo(serviceName, groupName, instance); // GRPC的监听
InstanceRequest request = new InstanceRequest(namespaceId, serviceName, groupName,
NamingRemoteConstants.REGISTER_INSTANCE, instance); // 实例请求
requestToServer(request, Response.class); // 最终的服务发送
}
private <T extends Response> T requestToServer(AbstractNamingRequest request, Class<T> responseClass) throws NacosException {
try {
request.putAllHeader(getSecurityHeaders());// 获取访问头信息
request.putAllHeader(getSpasHeaders(
NamingUtils.getGroupedNameOptional(request.getServiceName(), request.getGroupName())));// 请求头信息
Response response =
requestTimeout < 0 ? rpcClient.request(request) : rpcClient.request(request, requestTimeout); // 服务端响应
if (ResponseCode.SUCCESS.getCode() != response.getResultCode()) { // 判断状态
throw new NacosException(response.getErrorCode(), response.getMessage());
}
if (responseClass.isAssignableFrom(response.getClass())) {
return (T) response;
}
NAMING_LOGGER.error("Server return unexpected response '{}', expected response should be '{}'", response.getClass().getName(), responseClass.getName());
} catch (Exception e) {
throw new NacosException(NacosException.SERVER_ERROR, "Request nacos server failed: ", e);
}
throw new NacosException(NacosException.SERVER_ERROR, "Server return invalid response");
}
}
NacosDiscoveryAutoConfiguration
1、
package com.alibaba.cloud.nacos.discovery;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import com.alibaba.cloud.nacos.NacosDiscoveryProperties;
import com.alibaba.cloud.nacos.NacosServiceInstance;
import com.alibaba.nacos.api.exception.NacosException;
import com.alibaba.nacos.api.naming.pojo.Instance;
import com.alibaba.nacos.api.naming.pojo.ListView;
import org.springframework.cloud.client.ServiceInstance;
public class NacosServiceDiscovery {
// 如果没有这些属性肯定无法找到Nacos服务地址,也无法找到正确的领域模型,最终肯定无法使用
private NacosDiscoveryProperties discoveryProperties; // Nacos属性配置
public NacosServiceDiscovery(NacosDiscoveryProperties discoveryProperties) {
this.discoveryProperties = discoveryProperties;
}
// 根据Nacos地址和领域模型获取所有的服务实例
public List<ServiceInstance> getInstances(String serviceId) throws NacosException {
String group = discoveryProperties.getGroup();// 获取组
List<Instance> instances = discoveryProperties.namingServiceInstance()
.selectInstances(serviceId, group, true); // 根据命名空间、组名称、ID获取实例集合
return hostToServiceInstanceList(instances, serviceId); // 主机转换
}
public List<String> getServices() throws NacosException { // 获取服务信息项
String group = discoveryProperties.getGroup();
ListView<String> services = discoveryProperties.namingServiceInstance()
.getServicesOfServer(1, Integer.MAX_VALUE, group);
return services.getData();
}
public static List<ServiceInstance> hostToServiceInstanceList(
List<Instance> instances, String serviceId) { // 信息的转换
List<ServiceInstance> result = new ArrayList<>(instances.size());
for (Instance instance : instances) { // 获取集合中的每一个Instance
ServiceInstance serviceInstance = hostToServiceInstance(instance, serviceId);
if (serviceInstance != null) {
result.add(serviceInstance);
}
}
return result;
}
public static ServiceInstance hostToServiceInstance(Instance instance,
String serviceId) { // 剥离
if (instance == null || !instance.isEnabled() || !instance.isHealthy()) {
return null;
}
NacosServiceInstance nacosServiceInstance = new NacosServiceInstance();
nacosServiceInstance.setHost(instance.getIp());// 获取主机地址
nacosServiceInstance.setPort(instance.getPort());// 获取端口号
nacosServiceInstance.setServiceId(serviceId); // 获取服务ID
Map<String, String> metadata = new HashMap<>();
metadata.put("nacos.instanceId", instance.getInstanceId());// 实例ID
metadata.put("nacos.weight", instance.getWeight() + ""); // 权重数据
metadata.put("nacos.healthy", instance.isHealthy() + ""); // 健康信息
metadata.put("nacos.cluster", instance.getClusterName() + ""); // 集群信息
metadata.putAll(instance.getMetadata());// 元数据信息
nacosServiceInstance.setMetadata(metadata);
if (metadata.containsKey("secure")) {
boolean secure = Boolean.parseBoolean(metadata.get("secure"));
nacosServiceInstance.setSecure(secure);
}
return nacosServiceInstance;
}
}
demo