一、介绍: What is Nacos
Nacos /nɑ:kəʊs/ 是 Dynamic Naming and Configuration Service的首字母简称,一个更易于构建云原生应用的动态服务发现、配置管理和服务管理平台。
1、架构:
二、运行
1)、单机版:
1、nacos-server二进制包: 图解Nacos单机| ProcessOn免费在线作图,在线流程图,在线思维导图
(1)、下载nacos-server-2.0.4.tar.gz
(2)、解压:tar -zxvf nacos-server-2.0.4.tar.gz
(3)、运行(单机):sh bin/startup.sh -m standalone
(4)、关闭:sh bin/startup.sh shutdown.sh
其他:默认集群模式,可见startup.sh默认MODE是cluster;默认端口8848,可见conf/application.properties
2、源码运行
(1)、下载nacos-2.0.4.zip
(2)、解压并用idea工具编译
(3)、运行nacos-console
(4)、运行参数增加:VM options:-Dnacos.standalone=true -Dnacos.home=nacos-home-standalone
其他:默认集群模式,可见startup.sh默认MODE是cluster;默认端口8848,可见conf/application.properties
2)、集群
1、多服务器部署:简易图解: 图解Nacos集群| ProcessOn免费在线作图,在线流程图,在线思维导图
(1)、准备三台服务器
192.168.226.128
192.168.226.129
192.168.226.130
(2)、nacos集群配置:修改3台服务器配置文件 conf/cluster.conf.example 并重名名为cluster.conf文件
#it is ip
#example
192.168.226.128:8848
192.168.226.129:8848
192.168.226.130:8848
(3)、初始化nacos mysql脚本:设置在130服务
mysql -uroot -p
create database nacos;
use nacos;
source /root/nacos/conf/nacos-mysql.sql;
show tables;
(4)、derby切换成mysql数据库:
4.1、打开nacos/conf/application.properties配置mysql数据库地址和账号
4.2、重启nacos
依次启动3台nacos:sh /bin/startup.sh
4.3、访问nacos管理界面
2、单服务器集群部署: 图解Nacos集群-source版| ProcessOn免费在线作图,在线流程图,在线思维导图
nacos2.x版本采用grpc进行集群通信,grpc默认nacos server端口+1000作为通信端口(默认8848+1000=9848),所以服务部署避免grpc端口冲突。
(1)、idea打开nacos源码
(2)、新增conf/cluster.conf文件:可以从distribution/conf/cluster.conf.example拷贝重命名文件修改
(3)、将derby修改成mysql数据库
spring.datasource.platform=mysql
db.num=1
db.url.0=jdbc:mysql://192.168.226.130:3306/nacos?characterEncoding=utf8&connectTimeout=1000&socketTimeout=3000&autoReconnect=true&useUnicode=true&useSSL=false&serverTimezone=UTC
db.user.0=root
db.password.0=
(4)、配置启动参数:分别配置8841、8843、8845
(5)、启动服务
三、服务注册和发现
1)、http接口
1、注册:
curl -X POST 'http://serverIp:8848/nacos/v1/ns/instance?serviceName=client.server.name&ip=clinetIp&port=client端口'
例如:curl -X POST 'http://192.168.226.130:8848/nacos/v1/ns/instance?serviceName=lis.routine&ip=172.17.17.15&port=20010'
2、发现:
curl -X GET 'http://serverIp:8848/nacos/v1/ns/instance/list?serviceName=client.server.name'
例如:curl -X GET 'http://192.168.226.130:8848/nacos/v1/ns/instance/list?serviceName=lis.rounte'
2)、java SDK: Java SDK (nacos.io)
1、应用maven包
<dependency>
<groupId>com.alibaba.nacos</groupId>
<artifactId>nacos-client</artifactId>
<version>2.0.4</version>
</dependency>
2、模拟http注册和发现
public static void main(String[] args) throws NacosException, IOException {
Properties properties = new Properties();
properties.setProperty("serverAddr","127.0.0.1:8848");
NamingService namingService = NamingFactory.createNamingService(properties);
//服务注册
namingService.registerInstance("lis.user","172.17.17.114",20100,"DEV1");
//服务发现
System.out.println(namingService.getAllInstances("lis.user"));
// 进程不停止,nacos client会不断地向nacos server发送心跳 时间间隔
System.in.read();
}
3)、spring cloud:
1、添加pom依赖
<!--spring bott依赖-->
<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-discovery</artifactId>
</dependency>
2、application配置
server:
port: 9091
spring:
application:
# 服务名称
name: order
cloud:
nacos:
# nacos 注册地址
server-addr: localhost:8848
3、启动两个服务
四、原理
1)、Nacos领域模型
namespace:命名空间(相当于租户概念),隔离资源空间
group:数据分组
service:服务
cluster:服务下的集群
instance:具体提供服务的实例
2)、nacos元数据
描述信息,版本号、容灾策略、负载策略、鉴权配置、自定义标签等配置信息
spring:
cloud:
nacos:
discovery:
metadata:
version: 1.0.0
date: 2023-08-09
四、源码
4.1、概述
4.2、源码流程解析
1)、N1 Nacos Server 启动
源码流程图:N1 Nacos Server 启动 流程图模板_ProcessOn思维导图、流程图
1、Spring Boot 启动流程
2、通过Spring Boot 扩展机制:bean的扩展机制beanPostPrecessor拦截bean ->BaseRpcServer#start
3、nacos启动:9848作为grpc服务端和客户端通信;9849作为grpc服务端注册中心数据同步
4、nacos服务启动提供api
2)、S1 服务注册
源码流程图:S1 服务注册 流程图模板_ProcessOn思维导图、流程图
1、SpringApplication.run 项目启动:Spring Boot发布ServletWebServerInitializedEvent事件
2、Spring Cloud监听器AbstractAutoServiceRegistration 监听感兴趣事件WebServerInitializedEvent,并回调AbstractAutoServiceRegistration#onApplicationEvent
/*
* 1、Spring Cloud监听器AbstractAutoServiceRegistration 监听感兴趣事件WebServerInitializedEvent,回调AbstractAutoServiceRegistration#onApplicationEvent
*/
public abstract class AbstractAutoServiceRegistration<R extends Registration> implements AutoServiceRegistration, ApplicationContextAware, ApplicationListener<WebServerInitializedEvent> {
public void onApplicationEvent(WebServerInitializedEvent event) {
this.bind(event);
}
}
/*
* 2、bind
*/
@Deprecated
public void bind(WebServerInitializedEvent event) {
ApplicationContext context = event.getApplicationContext();
if (!(context instanceof ConfigurableWebServerApplicationContext) || !"management".equals(((ConfigurableWebServerApplicationContext)context).getServerNamespace())) {
this.port.compareAndSet(0, event.getWebServer().getPort());
this.start();
}
}
/*
* 3、start
*/
public void start() {
if (!this.isEnabled()) {
if (logger.isDebugEnabled()) {
logger.debug("Discovery Lifecycle disabled. Not starting");
}
} else {
if (!this.running.get()) {
this.context.publishEvent(new InstancePreRegisteredEvent(this, this.getRegistration()));
this.register();
if (this.shouldRegisterManagement()) {
this.registerManagement();
}
this.context.publishEvent(new InstanceRegisteredEvent(this, this.getConfiguration()));
this.running.compareAndSet(false, true);
}
}
}
/*
* 4、register
*/
protected void register() {
this.serviceRegistry.register(this.getRegistration());
}
3、Spring Cloud 注册接口:org.springframework.cloud.client.serviceregistry#register,其他组件实现注册方法,具体结构如下图
public interface ServiceRegistry<R extends Registration> {
void register(R registration);
}
4、Nacos实现注册
Nacos注册服务默认临时节点,通过grpc发送InstanceRequest调用Nacos服务注册(1.4.X版本没有grpc功能)
/*
* 1、Nacos注册:NacosServiceRegistry#register -> namingService.registerInstance(serviceId, group, instance)
*/
public void register(Registration registration) {
if (StringUtils.isEmpty(registration.getServiceId())) {
log.warn("No service to register for nacos client...");
} else {
NamingService namingService = this.namingService();
String serviceId = registration.getServiceId();
String group = this.nacosDiscoveryProperties.getGroup();
Instance instance = this.getNacosInstanceFromRegistration(registration);
try {
namingService.registerInstance(serviceId, group, instance);
log.info("nacos registry, {} {} {}:{} register finished", new Object[]{group, serviceId, instance.getIp(), instance.getPort()});
} catch (Exception var7) {
if (this.nacosDiscoveryProperties.isFailFast()) {
log.error("nacos registry, {} register failed...{},", new Object[]{serviceId, registration.toString(), var7});
ReflectionUtils.rethrowRuntimeException(var7);
} else {
log.warn("Failfast is false. {} register failed...{},", new Object[]{serviceId, registration.toString(), var7});
}
}
}
}
/*
* 2、NacosNamingService#registerInstance
*/
public void registerInstance(String serviceName, String groupName, Instance instance) throws NacosException {
NamingUtils.checkInstanceIsLegal(instance);
this.clientProxy.registerService(serviceName, groupName, instance);
}
/*
* 3、NamingClientProxyDelegate#registerInstance -> 临时节点(注册进程停了移除) -> grpcClientProxy
*/
public void registerService(String serviceName, String groupName, Instance instance) throws NacosException {
this.getExecuteClientProxy(instance).registerService(serviceName, groupName, instance);
}
private NamingClientProxy getExecuteClientProxy(Instance instance) {
return (NamingClientProxy)(instance.isEphemeral() ? this.grpcClientProxy : this.httpClientProxy);
}
/*
* 4、NamingGrpcClientProxy#registerInstance
*/
public void registerService(String serviceName, String groupName, Instance instance) throws NacosException {
LogUtils.NAMING_LOGGER.info("[REGISTER-SERVICE] {} registering service {} with instance {}", new Object[]{this.namespaceId, serviceName, instance});
this.redoService.cacheInstanceForRedo(serviceName, groupName, instance);
this.doRegisterService(serviceName, groupName, instance);
}
5、Nacos服务端接收客户端InstanceRequest注册请求
3)、S2 服务心跳:客户端创建rpcClient连接会发送心跳检查
源码流程图:S2 定时心跳客户端检测 流程图模板_ProcessOn思维导图、流程图
1、客户端每5s发送心跳检查
4)、N2 定时心跳客户端检测
源码流程图:N2 定时心跳客户端检测 流程图模板_ProcessOn思维导图、流程图
4.1、临时实例:
1、1.x版本 15s没收到健康状态healthy设置为true,30s后还没接收到客户端心跳使用HTTP DELETE方式调用/v1/ns/instance地址,注册表表剔除该实例(该接口也是在InstanceController类中);
2、 2.x版本 20s添加到过期集合-探活失败移除实例
4.1、持久实例:
1、注册中心探测机制:探测周期2s~5s随机,检测异常健康状态设置true,不会剔除实例。采用tcp(ping)、http、mysql探测
2、使用场景:非弹性扩容实例,可检测不健康实例;另外起到保护阈值防止雪崩作用(分流作用)
5)、 C1 服务发现与订阅
源码流程图:https://www.processon.com/preview/64f1906d8abe6e4c2dd893bb
服务发现:客户端 向 服务端pull查询接口
服务订阅:服务端 向 客户端push更新数据
6)、 C2 服务列表本地缓存
源码流程图:C2 服务列表本地缓存 流程图模板_ProcessOn思维导图、流程图
1、服务发现和订阅 获取客户端会优先从本地缓存获取
2、服务发现 本地缓存未获取从Nacos服务获取客户端后,更新到本地缓存
2.1、异步任务获取后更新到本地缓存
2.1、异步任务未获取到从Nacos服务端获取到更新到本地缓存
3、订阅 服务更新后 发布ServiceChangedEvent事件,用于推送消息给对应的订阅者
五、CAP
1)、AP:Distro同步数据架构
2)、CP:raft架构
Raft Consensus Algorithm
Raft (thesecretlivesofdata.com)
六、删除服务
1、临时实例无需删除,不健康实例自动删除
2、持久实例先通过OpenAPI注销实例