Nacos源码解读01——临时实例服务注册

本文详细阐述了Nacos2.0的架构设计,涉及GRPC通信机制、临时节点与持久化节点的区别,以及SpringBoot中的服务注册流程,重点介绍了Client模型,包括如何使用GRPC协议进行临时实例注册和持久化节点的HTTP请求。
摘要由CSDN通过智能技术生成

Nacos 2.0 架构设计及新模型

参考 https://zhuanlan.zhihu.com/p/344572647

GRPC详解

参考 https://blog.csdn.net/weixin_42937773/article/details/128925911?spm=1001.2014.3001.5502

临时节点和持久化节点的区别

参考https://zhuanlan.zhihu.com/p/396489239

使用GRPC注册临时实例流程图请添加图片描述

SpringBoot自动注入

在这里插入图片描述
注入对应服务注册的Bean
在这里插入图片描述
在这里插入图片描述

监听Tomcat启动事件

NacosAutoServiceRegistration 继承了AbstractAutoServiceRegistration 而 AbstractAutoServiceRegistration实现了ApplicationListener接口当tomcat启动之后会发送WebServerInitializedEvent事件 AbstractAutoServiceRegistration监听了WebServerInitializedEvent事件进行后续的注册操作
在这里插入图片描述

开始注册服务

在这里插入图片描述
在这里插入图片描述
构建出来的 Instance信息
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

临时实例使用GRPC协议注册

这里判断是不是临时节点 临时节点默认为true 会走NamingGrpcClientProxy的注册方法采用grpc协议,nacos2.x版本中临时节点默认都是此协议进行通信。持久化节点使用HTTP请求进行通信
在这里插入图片描述
在这里插入图片描述

缓存实例信息

在这里插入图片描述

开始注册服务信息

在这里插入图片描述

发送注册请求

在这里插入图片描述

将服务信息改为已注册

在这里插入图片描述

服务端接收注册请求

在这里插入图片描述
InstanceRequestHandler 继承自RequestHandler所以当RequestHandler执行handler方法的时候会执行InstanceRequestHandler 的handler方法进行逻辑处理
在这里插入图片描述
clientOperationService是一个临时实例还是持久化实例的代理类,去管理不同实例的注册行为
在这里插入图片描述

Client模型

Nacos2.x以后新增Client模型**。**一个客户端gRPC长连接对应一个Client,每个Client有自己唯一的id(clientId)。Client负责管理一个客户端的服务实例注册Publish和服务订阅Subscribe。我们可以看一下这个模型其实就是一个接口

public interface Client {
    // 客户端id/gRPC的connectionId
    String getClientId();

    // 是否临时客户端
    boolean isEphemeral();
    // 客户端更新时间
    void setLastUpdatedTime();
    long getLastUpdatedTime();

    // 服务实例注册/注销/查询
    boolean addServiceInstance(Service service, InstancePublishInfo instancePublishInfo);
    InstancePublishInfo removeServiceInstance(Service service);
    InstancePublishInfo getInstancePublishInfo(Service service);
    Collection<Service> getAllPublishedService();

    // 服务订阅/取消订阅/查询订阅
    boolean addServiceSubscriber(Service service, Subscriber subscriber);
    boolean removeServiceSubscriber(Service service);
    Subscriber getSubscriber(Service service);
    Collection<Service> getAllSubscribeService();
    // 生成同步给其他节点的client数据
    ClientSyncData generateSyncData();
    // 是否过期
    boolean isExpire(long currentTime);
    // 释放资源
    void release();
}

服务信息Service与Instance创建

    @Override
    public void registerInstance(Service service, Instance instance, String clientId) {
        //缓存服务、缓存命名空间与服务的关系
        Service singleton = ServiceManager.getInstance().getSingleton(service);
        if (!singleton.isEphemeral()) {
            throw new NacosRuntimeException(NacosException.INVALID_PARAM,
                    String.format("Current service %s is persistent service, can't register ephemeral instance.",
                            singleton.getGroupedServiceName()));
        }
        //连接Id作为客户端Id,获取客户端
        Client client = clientManager.getClient(clientId);
        //检查客户端是否合法:客户端是否存在、客户端是否瞬时
        if (!clientIsLegal(client, clientId)) {
            return;
        }
        // 生成服务端存储的instance信息,并记录到Client
        InstancePublishInfo instanceInfo = getPublishInfo(instance);
       //在客户端添加服务与实例的关系信息
        client.addServiceInstance(singleton, instanceInfo);
        client.setLastUpdatedTime();
          // 发布注册服务事件,源码解读见下文:服务变更事件处理
        NotifyCenter.publishEvent(new ClientOperationEvent.ClientRegisterServiceEvent(singleton, clientId));
          // 发布元数据事件,源码解读见下文:管理元数据源码
        NotifyCenter
                .publishEvent(new MetadataEvent.InstanceMetadataEvent(singleton, instanceInfo.getMetadataId(), false));
    }

ServiceManager

Service的容器是ServiceManager,但是在com.alibaba.nacos.naming.core.v2包下,容器中Service都是单例。

public class ServiceManager {
    
    private static final ServiceManager INSTANCE = new ServiceManager();
    //单例Service,可以查看Service的equals和hasCode方法
    private final ConcurrentHashMap<Service, Service> singletonRepository;
    //namespace下的所有service
    private final ConcurrentHashMap<String, Set<Service>> namespaceSingletonMaps;
    .....
}

所以从这个位置可以看出,当调用这个注册方法的时候ServiceManager负责管理Service单例

//通过Map储存单例的Service
public Service getSingleton(Service service) {
    singletonRepository.putIfAbsent(service, service);
    Service result = singletonRepository.get(service);
    namespaceSingletonMaps.computeIfAbsent(result.getNamespace(), (namespace) -> new ConcurrentHashSet<>());
    namespaceSingletonMaps.get(result.getNamespace()).add(result);
    return result;
}

ClientManager

ClientManager这是一个接口这里我们要看它对应的一个实现类ConnectionBasedClientManager,这个实现类负责管理长连接clientId与Client模型的映射关系

// 根据clientId查询Client
public Client getClient(String clientId) {
    return clients.get(clientId);
}
@Override
public boolean addServiceInstance(Service service, InstancePublishInfo instancePublishInfo) {
    if (null == publishers.put(service, instancePublishInfo)) {
        MetricsMonitor.incrementInstanceCount();
    }
    NotifyCenter.publishEvent(new ClientEvent.ClientChangedEvent(this));
    Loggers.SRV_LOG.info("Client change for service {}, {}", service, getClientId());
    return true;
}

添加服务实例信息

    @Override
    public boolean addServiceInstance(Service service, InstancePublishInfo instancePublishInfo) {
        return super.addServiceInstance(service, parseToHealthCheckInstance(instancePublishInfo));
    }

Client实例AbstractClient负责存储当前客户端的服务注册表,即Service与Instance的关系。注意对于单个客户端来说,同一个服务只能注册一个实例。

    @Override
    public boolean addServiceInstance(Service service, InstancePublishInfo instancePublishInfo) {
        //缓存服务与实例映射关系到客户端的容器、一个客户端对应一个连接
        if (null == publishers.put(service, instancePublishInfo)) {
            MetricsMonitor.incrementInstanceCount();
        }
        NotifyCenter.publishEvent(new ClientEvent.ClientChangedEvent(this));
        Loggers.SRV_LOG.info("Client change for service {}, {}", service, getClientId());
        return true;
    }

可以从下图中看到 我把同一个服务 开了两个不同端口的实例 他每一个客户端id 都不相同 所以Clinet里面publishers这个map是一个service 对应一个实例
在这里插入图片描述

在这里插入图片描述
在这里插入图片描述

服务信息映射事件处理

在上面的流程中,可以看到调用通知中心派发了2个事件:

new ClientOperationEvent.ClientRegisterServiceEvent(singleton, clientId)

new MetadataEvent.InstanceMetadataEvent(singleton, instanceInfo.getMetadataId()

这里的目的是为了过滤目标服务得到最终Instance列表建立Service与Client的关系,建立Service与Client的关系就是为了加速查询。

ClientServiceIndexesManager类服务处理这个类的监听业务,ClientServiceIndexesManager维护了两个索引:

Service与发布clientId
Service与订阅clientId

private final ConcurrentMap<Service, Set<String>> publisherIndexes = new ConcurrentHashMap<>();
    
private final ConcurrentMap<Service, Set<String>> subscriberIndexes = new ConcurrentHashMap<>();

private void handleClientOperation(ClientOperationEvent event) {
    Service service = event.getService();
    String clientId = event.getClientId();
    if (event instanceof ClientOperationEvent.ClientRegisterServiceEvent) {
        addPublisherIndexes(service, clientId);
    } else if (event instanceof ClientOperationEvent.ClientDeregisterServiceEvent) {
        removePublisherIndexes(service, clientId);
    } else if (event instanceof ClientOperationEvent.ClientSubscribeServiceEvent) {
        addSubscriberIndexes(service, clientId);
    } else if (event instanceof ClientOperationEvent.ClientUnsubscribeServiceEvent) {
        removeSubscriberIndexes(service, clientId);
    }
}

//建立Service与发布Client的关系
private void addPublisherIndexes(Service service, String clientId) {
    publisherIndexes.computeIfAbsent(service, (key) -> new ConcurrentHashSet<>());
    publisherIndexes.get(service).add(clientId);
    NotifyCenter.publishEvent(new ServiceEvent.ServiceChangedEvent(service, true));
}

从ClientServiceIndexesManager类的源代码中可以看到,该类注册订阅了4个事件:

客户端注册服务事件、客户端取消注册服务事件、客户端订阅服务事件、客户端取消订阅服务事件

@Override
public List<Class<? extends Event>> subscribeTypes() {
    List<Class<? extends Event>> result = new LinkedList<>();
    result.add(ClientOperationEvent.ClientRegisterServiceEvent.class);
    result.add(ClientOperationEvent.ClientDeregisterServiceEvent.class);
    result.add(ClientOperationEvent.ClientSubscribeServiceEvent.class);
    result.add(ClientOperationEvent.ClientUnsubscribeServiceEvent.class);
    result.add(ClientEvent.ClientDisconnectEvent.class);
    return result;
}

这个索引关系建立以后,还会触发ServiceChangedEvent,代表服务注册表变更。对于注册表变更紧接着还要做两个事情:

1.通知订阅客户端

2.Nacos集群数据同步。

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值