4、Nacos 服务注册服务端源码分析(三)

上一篇我们说到NotifyCenter,讲解了整个事件通知中心是如何通过巧妙的设计,让各个事件的生产者和消费者连接起来的。没看过的小伙伴可以点击这里进行查看。

本篇我们跟随这事件的路线,跟踪下订阅者获取到事件后,是如何进行处理的。

首先我们回顾一下,在注册的时候,服务端通过这个类,发布了几个事件,代码出处在com.alibaba.nacos.naming.remote.rpc.handler.InstanceRequestHandler#registerInstance分别如下:

private InstanceResponse registerInstance(Service service, InstanceRequest request, RequestMeta meta)
    throws NacosException {
    // 注册实例
    clientOperationService.registerInstance(service, request.getInstance(), meta.getConnectionId());
    // 发布注册实例跟踪事件
    NotifyCenter.publishEvent(new RegisterInstanceTraceEvent(System.currentTimeMillis(),
                                                             meta.getClientIp(), true, service.getNamespace(), service.getGroup(), service.getName(),
                                                             request.getInstance().getIp(), request.getInstance().getPort()));
    // 对rpcClient进行应答
    return new InstanceResponse(NamingRemoteConstants.REGISTER_INSTANCE);
}

public void registerInstance(Service service, Instance instance, String clientId) throws NacosException {
    NamingUtils.checkInstanceIsLegal(instance);
    // 获取单例的服务
    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()));
    }
    // 通过clientId获取一个客户端的处理类
    Client client = clientManager.getClient(clientId);
    if (!clientIsLegal(client, clientId)) {
        return;
    }
    InstancePublishInfo instanceInfo = getPublishInfo(instance);
    // 存放注册信息
    client.addServiceInstance(singleton, instanceInfo);
    client.setLastUpdatedTime();
    client.recalculateRevision();
    // 发布注册事件
    NotifyCenter.publishEvent(new ClientOperationEvent.ClientRegisterServiceEvent(singleton, clientId));
    // 发布内容信息事件
    NotifyCenter
        .publishEvent(new MetadataEvent.InstanceMetadataEvent(singleton, instanceInfo.getMetadataId(), false));
}

@Override
public boolean addServiceInstance(Service service, InstancePublishInfo instancePublishInfo) {
    if (null == publishers.put(service, instancePublishInfo)) {
        if (instancePublishInfo instanceof BatchInstancePublishInfo) {
            MetricsMonitor.incrementIpCountWithBatchRegister(instancePublishInfo);
        } else {
            MetricsMonitor.incrementInstanceCount();
        }
    }
    // 发布客户端变动的事件
    NotifyCenter.publishEvent(new ClientEvent.ClientChangedEvent(this));
    Loggers.SRV_LOG.info("Client change for service {}, {}", service, getClientId());
    return true;
}

光是一个注册,就发布了好几个事件。每个事件都有自己的处理逻辑。现在假如我们没有NotifyCenter进行解耦,而是一个个处理,那代码得多长,逻辑得多复杂,而且对于同一类型的事件的处理还会有这各种冗余的代码。而现在我们只需要关心我们感兴趣的事件去处理就好,假如以后新增了其他的逻辑,我们也可以增加新的事件,在这里进行publish,再写上自己的订阅类处理就好。

现在我们只拿一个事件来分析,比如NotifyCenter.publishEvent(new ClientOperationEvent.ClientRegisterServiceEvent(singleton, clientId))这个事件。拿一个事件分析是为了告诉大家如何分析,还有它的分析思路。其他的大家可以自己学着去分析。

分析事件的订阅者

既然我们已经明确了现在要分析ClientOperationEvent.ClientRegisterServiceEvent事件了。那首先我们得找到事件的订阅者。因为订阅者既然订阅了事件,那说明它对事件感兴趣,要对事件做出响应的处理。对于程序代码来说,肯定就是onEvent(Event event)的方法去处理逻辑。

我们使用IDE点击类名,查看调用,可以看到有好几处引用了。

4、Nacos 服务注册服务端源码分析(三)_第1张图片
不过仔细看就能发现,除了前面两个是处理方法外,其他的都是发布事件。而前两个都属于类ClientServiceIndexesManager。我们就继续分析ClientServiceIndexesManager

ClientServiceIndexesManager的构造方法中可以看到其在NotifyCenter中注册了自己。

Nacos中订阅者一般都是在创建的时候,也就是构造方法中注册

public ClientServiceIndexesManager() {
    // 注册自己
    NotifyCenter.registerSubscriber(this, NamingEventPublisherFactory.getInstance());
}

然后在处理事件中,仅仅处理了两类事件,其中有一个就是ClientOperationEvent

@Override
public void onEvent(Event event) {
    if (event instanceof ClientEvent.ClientDisconnectEvent) {
        handleClientDisconnect((ClientEvent.ClientDisconnectEvent) event);
    } else if (event instanceof ClientOperationEvent) {
        handleClientOperation((ClientOperationEvent) event);
    }
}

private void handleClientOperation(ClientOperationEvent event) {
    Service service = event.getService();
    String clientId = event.getClientId();
    if (event instanceof ClientOperationEvent.ClientRegisterServiceEvent) {
        // 处理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);
    }
}

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));
}

不知道大家看到这一个个事件是不是有点晕,一会clientEvent,一会又ServiceEvent。下面我就针对代码中的各个单词对应的含义进行解释一下。解释完后,大家就会对这些概念清楚很多,也能明白作者意图。

ServiceCluster,Instance,Client的解释

我们从Service开始看。Service包含了服务名,应用名,组名。

public class Service implements Serializable { 
    private static final long serialVersionUID = -3470985546826874460L;
    private String name;
    private float protectThreshold = 0.0F;
    private String appName;
    private String groupName;
    private Map<String, String> metadata = new HashMap<>();
    //...忽略其他方法
}

Cluster中包含了一个serviceName的属性。也就是Cluster是属于了一个Service的。

public class Cluster implements Serializable {
    private static final long serialVersionUID = -7196138840047197271L;
    private String serviceName;
    private String name;
    private AbstractHealthChecker healthChecker = new Tcp();
    private int defaultPort = 80;
    private int defaultCheckPort = 80;
    private boolean useIPPort4Check = true;
    private Map<String, String> metadata = new HashMap<>();
    //...其它一些get和set方法忽略
}

Instance中既有一个clusterName,又有一个serviceName。说明它是一个服务中,集群中的一个实例信息。

public class Instance implements Serializable {
    private static final long serialVersionUID = -742906310567291979L;
    private String instanceId;
    private String ip;
    private int port;
    private double weight = 1.0D;
    private boolean healthy = true;
    private boolean enabled = true;
    private boolean ephemeral = true;
    private String clusterName;
    private String serviceName;    
    private Map<String, String> metadata = new HashMap<>();
    //其它一些get和set方法忽略
    ...
}

下面是我对服务和集群的截图。

4、Nacos 服务注册服务端源码分析(三)_第2张图片
4、Nacos 服务注册服务端源码分析(三)_第3张图片

可以看到一个服务名是可以有多个集群和多个实例的。而一个集群可能有多个实例,一个实例就是一个客户端的连接。

最后我们还有个Client的概念。进入源码,Cient并不是一个实体类,而是一个接口。它定义了一系列操作的接口。

public interface Client {
    String getClientId();
    boolean isEphemeral();
    boolean addServiceInstance(Service service, InstancePublishInfo instancePublishInfo);
    InstancePublishInfo removeServiceInstance(Service service);
    // 其他的一些方法。。。
}

明白了这几个概念后,我们再看看和他们相关的类。比如ClientOperationEventInstancesChangeEvent,ServiceEvent。然后再从概念出发,当一个客户端注册到服务端,可能会引发一系列的变化,比如说新服务的注册上线,新的集群的上线,新的实例的上线等。所以可以看到注册服务所触发的一系列的事件,他们实际上都是一环扣一环,紧密相连的。

本篇就分析到这里,下一篇我们从ServiceEvent.ServiceChangedEvent继续往下分析。

你可能感兴趣的:(Nacos,源码分析,java,中间件,分布式)