Nacos原理

  1、服务注册与发现

基于1.4版本的nacos

1.1、服务注册

1、客户端初始化

读取配置获取nacos服务中心地址,命名空间,group等信息

通过http请求建立连接。

2、发生注册请求

通过/nacos/v1/ns/instance接口提交请求

3、服务端处理

根据实例类型(配置参数:ephemeral)决定存储方式:

临时实例(true):存储内存中,通过AP模式(Distro 协议)快速同步集群节点
持久化实例(false),写入数据库中,通过CP模式(Raft 协议)保证一致性。

class NacosNamingService  {


    // 初始化配置数据,并拉取服务器信息
    private void init(Properties properties) throws NacosException {
        ValidatorUtils.checkInitParam(properties);
        this.namespace = InitUtils.initNamespaceForNaming(properties);
        InitUtils.initSerialization();
        initServerAddr(properties);
        InitUtils.initWebRootContext(properties);
        initCacheDir();
        initLogName(properties);
        
        this.serverProxy = new NamingProxy(this.namespace, this.endpoint, this.serverList, properties);
        // 初始化心跳检查的任务
        this.beatReactor = new BeatReactor(this.serverProxy, initClientBeatThreadCount(properties));
        // 初始化更新服务的任务
        this.hostReactor = new HostReactor(this.serverProxy, beatReactor, this.cacheDir, isLoadCacheAtStart(properties),
                isPushEmptyProtect(properties), initPollingThreadCount(properties));
    }

    // 实例化对象
    @Override
    public void registerInstance(String serviceName, String groupName, String ip, int port, String clusterName)
            throws NacosException {
        
        Instance instance = new Instance();
        instance.setIp(ip);
        instance.setPort(port);
        instance.setWeight(1.0);
        instance.setClusterName(clusterName);
        
        registerInstance(serviceName, groupName, instance);
    }

    // 1、判断心跳时间(5s),服务器超时时间(30s)是否超过
    // 2、启动线程每过5s调用一次心跳接口
    // 3、调用服务端注册接口
    @Override
    public void registerInstance(String serviceName, String groupName, Instance instance) throws NacosException {
        NamingUtils.checkInstanceIsLegal(instance);
        String groupedServiceName = NamingUtils.getGroupedName(serviceName, groupName);
        if (instance.isEphemeral()) {
            BeatInfo beatInfo = beatReactor.buildBeatInfo(groupedServiceName, instance);
            beatReactor.addBeatInfo(groupedServiceName, beatInfo);
        }
        serverProxy.registerService(groupedServiceName, groupName, instance);
    }


}


1.2、心跳与健康检查

服务注册之后,nacos会持续监控实例健康状态:

1、客户端心跳(临时实例Ephemeral才有

主动上报:临时实例每5秒向nacos发送健康检查(HTTP POST /nacos/v1/ns/instance/beat

服务端处理:收到心跳刷新最近的活跃时间,若15秒没有收到心跳,则标记为不健康状态

30秒未收到,则剔除实例(ClientBeatCheckTask#run方法)

2、服务端健康检查

被动检查:持久化实例依赖服务端主动探测。

探测配置:通过控制台配置健康检查路径(如:/health),nacos根据配置频次判断是否存活。

服务端处理:服务器启动会创建一个健康检查任务,这个任务每隔2000-7000毫秒之间执行健康检查,主要的健康检查方式有TCP(TcpSuperSenseProcessor),HTTP(HttpHealthCheckProcessor),MYSQL(MysqlHealthCheckProcessor)。

tcp方式:根据服务ip地址和端口判断是否连接成功。(默认方式)com.alibaba.nacos.naming.healthcheck.TcpSuperSenseProcessor.TaskProcessor

http方式:根据服务ip地址和端口发送http请求,请求路径需要配置。

com.alibaba.nacos.naming.healthcheck.HttpHealthCheckProcessor#process

mysql方式:根据服务ip地址和端口发送mysql连接。

com.alibaba.nacos.naming.healthcheck.MysqlHealthCheckProcessor.MysqlCheckTask

1.3、服务发现

服务发现就是指当有服务实例注册成功之后,其它服务可以发现这些服务实例。

nacos提供2种订阅方式:

1、主动查询:指客户端主动向服务端发生查询请求获取实例,也就是拉(Pull)的方式

2、被动查询(服务订阅):客户端向服务端发送一个订阅服务的请求,当被订阅的服务有信息变更,nacos就会主动把服务实例的消息推送给客户端,也就是推(Push)的方式

nacos整合springCloud的时候默认是订阅方式。

订阅方式的实现:

第一步,客户端在启动的时候,会去构建一个叫PushReceiver的类,这个类会去创建一个UDP Socket,端口是随机的

第二步,调用NamingService#subscribe来发起订阅时,会先去服务端查询需要订阅服务的所有实例信息,之后会将所有服务实例数据存到客户端的一个内部缓存中。并且在查询的时候,会将这个UDP Socket 的端口作为一个参数传到服务端,服务端接收到这个UDP端口后,后续就通过这个端口给客户端推送服务实例数据

第三步,会为这次订阅开启一个不定时执行的任务(com.alibaba.nacos.common.notify.DefaultPublisher#openEventHandler),这个任务会去从服务端查询订阅的服务实例信息,然后更新内部缓存(com.alibaba.cloud.nacos.discovery.NacosWatch#start)
之所以不定时,是因为这个当执行异常的时候,下次执行的时间间隔就会变长,但是最多不超过60s,正常是10s,这个10s是查询服务实例是服务端返回的

那么既然有了服务变动推送的功能,为什么还要定时去查询更新服务实例信息呢?

其实很简单,那就是因为 UDP通信不稳定导致的
虽然有 Push,但是由于 UDP通信自身的不确定性,有可能会导致客户端接收变动信息失败
所以这里就加了一个定时任务,弥补这种可能性,属于一个兜底的方案。

public class NacosNamingService implements NamingService {

    private void init(Properties properties) throws NacosException {
        // 参数校验
        ValidatorUtils.checkInitParam(properties);
        // 命名空间初始
        this.namespace = InitUtils.initNamespaceForNaming(properties);
        // 序列化初始化
        InitUtils.initSerialization();
        // 服务器地址初始化
        initServerAddr(properties);
        // Web上下文初始化
        InitUtils.initWebRootContext(properties);
        // 缓存目录初始化
        initCacheDir();
        // 日志名称初始化
        initLogName(properties);
        
        // 用于与Nacos服务器通信
        this.serverProxy = new NamingProxy(this.namespace, this.endpoint, this.serverList, properties
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值