Nacos源码 (3) 注册中心

这篇具有很好参考价值的文章主要介绍了Nacos源码 (3) 注册中心。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

本文将从一个服务注册示例入手,通过阅读客户端、服务端源码,分析服务注册、服务发现原理。

使用的2.0.2的版本。

客户端

创建NacosNamingService对象

NacosNamingService nacosNamingService = new NacosNamingService(NACOS_HOST);

NacosNamingService提供两个构造方法:

public NacosNamingService(String serverList) throws NacosException {
    Properties properties = new Properties();
    properties.setProperty(PropertyKeyConst.SERVER_ADDR, serverList);
    init(properties);
}

public NacosNamingService(Properties properties) throws NacosException {
    init(properties);
}

第二个方法的properties的key在PropertyKeyConst常量类可以找到,如:

  • namespace
  • username
  • password
  • serverAddr
  • clusterName
  • 其他

构造方法中会初始化一些参数和组件:

  • 初始化namespace参数

  • 创建InstancesChangeNotifier对象,它实现了Subscriber接口,监听InstancesChangeEvent事件

    public class InstancesChangeNotifier extends Subscriber<InstancesChangeEvent> {
    
        // key使用serviceName + groupName + clusters组合而成
        // value是监听器集合
        private final Map<String, ConcurrentHashSet<EventListener>> listenerMap;
    
        // 锁
        private final Object lock = new Object();
    
  • 向NotifyCenter注册InstancesChangeEvent事件,注册之前创建的InstancesChangeNotifier对象监听服务实例变化

    NotifyCenter.registerToPublisher(InstancesChangeEvent.class, 16384);
    NotifyCenter.registerSubscriber(changeNotifier);
    
    // NotifyCenter维护着EventPublisher集,Subscriber会被注册到EventPublisher上
    // EventPublisher提供publish方法向Event队列推送事件
    // EventPublisher是一个Thread类,run方法从Event队列取事件通知Subscriber来处理
    
  • 创建NamingClientProxyDelegate对象,用于与服务端通信,它是一个代理,内部使用其他的NamingClientProxy实现:

    • NamingHttpClientProxy
    • NamingGrpcClientProxy - 默认使用该实现类,其中有healthCheck检测服务端是否健康,服务端直接响应成功无操作

服务注册

NacosNamingService nacosNamingService = new NacosNamingService(NACOS_HOST);
nacosNamingService.registerInstance(ORDER_SERVICE, "192.168.0.100", 9999);

提供多个重载的registerInstance方法,最终使用这个方法:

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

public void registerInstance(String serviceName, String groupName, Instance instance)
            throws NacosException {
    // 此处clientProxy是NamingClientProxyDelegate对象
    clientProxy.registerService(serviceName, groupName, instance);
}

NamingClientProxyDelegate的registerService方法会选择一个具体的NamingClientProxy对象与服务端通信,默认使用NamingGrpcClientProxy对象。

NamingGrpcClientProxy的registerService方法构建InstanceRequest请求对象,之后使用RpcClient对象发送请求并接收响应。

RpcClient内部通过GrpcConnection对象使用GRPC来访问服务端。

内部的GRPC代码是使用protoc和protobuf-maven-plugin生成的,通信细节此处不做介绍。

服务下线

nacosNamingService.deregisterInstance(ORDER_SERVICE, "192.168.0.100", 9999);

deregisterInstance服务下线:

public void deregisterInstance(String serviceName,
                               String groupName,
                               String ip,
                               int port,
                               String clusterName) throws NacosException {
    Instance instance = new Instance();
    instance.setIp(ip);
    instance.setPort(port);
    instance.setClusterName(clusterName);
    deregisterInstance(serviceName, groupName, instance);
}

public void deregisterInstance(String serviceName,
                               String groupName,
                               Instance instance) throws NacosException {
    clientProxy.deregisterService(serviceName, groupName, instance);
}

查询实例

示例代码:

NacosNamingService namingService = new NacosNamingService(NACOS_HOST);
List<Instance> instances = namingService.getAllInstances(ORDER_SERVICE, true);

System.out.printf(">> instance count=%d\n", instances.size());

for (Instance instance : instances) {
    System.out.printf(">> serviceName=%s, id=%s, cluster=%s, ip=%s, port=%s\n",
            instance.getServiceName(), instance.getInstanceId(),
            instance.getClusterName(), instance.getIp(), instance.getPort());
}

提供了几个重载的getAllInstances方法,最重要的参数就是subscribe,当为true时,会向服务端发送订阅请求,之后一直从ServiceInfoHolder中获取服务实例信息,而不再向服务端发送查询请求。

public List<Instance> getAllInstances(String serviceName, String groupName, List<String> clusters,
        boolean subscribe) throws NacosException {
    ServiceInfo serviceInfo;
    String clusterString = StringUtils.join(clusters, ",");
    if (subscribe) {
        serviceInfo = serviceInfoHolder.getServiceInfo(serviceName, groupName, clusterString);
        if (null == serviceInfo) {
            // 订阅请求
            serviceInfo = clientProxy.subscribe(serviceName, groupName, clusterString);
        }
    } else {
        // 查询请求
        serviceInfo = clientProxy.queryInstancesOfService(serviceName, groupName, clusterString, 0, false);
    }
    List<Instance> list;
    if (serviceInfo == null || CollectionUtils.isEmpty(list = serviceInfo.getHosts())) {
        return new ArrayList<Instance>();
    }
    return list;
}

服务订阅

示例代码:

NacosNamingService namingService = new NacosNamingService(NACOS_HOST);
namingService.subscribe(ORDER_SERVICE, new EventListener() {
    @Override
    public void onEvent(Event event) {
        NamingEvent e = (NamingEvent) event;
        System.out.println("serviceName=" + e.getServiceName());
        List<Instance> instances = e.getInstances();
        System.out.printf(">> instance count=%d\n", instances.size());

        for (Instance instance : instances) {
            System.out.printf(">> serviceName=%s, id=%s, cluster=%s, ip=%s, port=%s\n",
                    instance.getServiceName(), instance.getInstanceId(),
                    instance.getClusterName(), instance.getIp(), instance.getPort());
        }
    }
});

TimeUnit.SECONDS.sleep(1200);

subscribe方法:

public void subscribe(String serviceName, String groupName, List<String> clusters, EventListener listener)
        throws NacosException {
    String clusterString = StringUtils.join(clusters, ",");
    // 将listener保存到listenerMap中
    changeNotifier.registerListener(groupName, serviceName, clusterString, listener);
    // 发送订阅请求
    clientProxy.subscribe(serviceName, groupName, clusterString);
}

实例变化的方法调用栈:
Nacos源码 (3) 注册中心

当收到服务端的实例变化事件时,会触发grpc层的观察者监听:

public void onMessage(RespT message) {
  if (firstResponseReceived && !streamingResponse) {
    throw Status.INTERNAL
        .withDescription("More than one responses received for unary or client-streaming call")
        .asRuntimeException();
  }
  firstResponseReceived = true;
  // 调用观察者
  observer.onNext(message);

  if (streamingResponse && adapter.autoFlowControlEnabled) {
    // Request delivery of the next inbound message.
    adapter.request(1);
  }
}

此处的observer是在创建rpc连接的时候注册的:

private StreamObserver<Payload> bindRequestStream(
        final BiRequestStreamGrpc.BiRequestStreamStub streamStub,
        final GrpcConnection grpcConn) {

    return streamStub.requestBiStream(new StreamObserver<Payload>() {

        @Override
        public void onNext(Payload payload) {
            try {
                Object parseBody = GrpcUtils.parse(payload);
                final Request request = (Request) parseBody;
                if (request != null) {
                    try {
                        // 调用ServerRequestHandler处理请求
                        Response response = handleServerRequest(request);
                        if (response != null) {
                            response.setRequestId(request.getRequestId());
                            sendResponse(response);
                        }

// ...

NamingPushRequestHandler的处理逻辑:

public Response requestReply(Request request) {
    if (request instanceof NotifySubscriberRequest) {
        NotifySubscriberRequest notifyResponse = (NotifySubscriberRequest) request;
        serviceInfoHolder.processServiceInfo(notifyResponse.getServiceInfo());
        return new NotifySubscriberResponse();
    }
    return null;
}

serviceInfoHolder.processServiceInfo方法:

public ServiceInfo processServiceInfo(ServiceInfo serviceInfo) {
    String serviceKey = serviceInfo.getKey();
    if (serviceKey == null) {
        return null;
    }
    ServiceInfo oldService = serviceInfoMap.get(serviceInfo.getKey());
    if (isEmptyOrErrorPush(serviceInfo)) {
        //empty or error push, just ignore
        return oldService;
    }
    serviceInfoMap.put(serviceInfo.getKey(), serviceInfo);
    boolean changed = isChangedServiceInfo(oldService, serviceInfo);
    if (StringUtils.isBlank(serviceInfo.getJsonFromServer())) {
        serviceInfo.setJsonFromServer(JacksonUtils.toJson(serviceInfo));
    }
    MetricsMonitor.getServiceInfoMapSizeMonitor().set(serviceInfoMap.size());
    if (changed) {
        // 推送一个InstancesChangeEvent事件
        NotifyCenter.publishEvent(new InstancesChangeEvent(
                serviceInfo.getName(), serviceInfo.getGroupName(),
                serviceInfo.getClusters(), serviceInfo.getHosts()));
        DiskCache.write(serviceInfo, cacheDir);
    }
    return serviceInfo;
}

推送一个InstancesChangeEvent事件:

  1. NotifyCenter维护着一个EventPublisher集,当有事件时,会选择一个目标EventPublisher

  2. 通过publish方法将事件保存到一个Event队列

    public boolean publish(Event event) {
        checkIsStart();
        boolean success = this.queue.offer(event);
        if (!success) {
            // 当队列操作失败时,直接使用当前线程处理事件
            receiveEvent(event);
            return true;
        }
        return true;
    }
    
  3. EventPublisher是一个线程,在NotifyCenter初始化时启动。run方法会从Event队列取事件,使用receiveEvent(event)进行处理

  4. receiveEvent方法查找所有的Subscriber,其中就有最初创建的InstancesChangeNotifier,调用订阅者onEvent方法

服务端

服务注册

InstanceRequestHandler处理器

注册中心的rpc处理器在com.alibaba.nacos.naming.remote.rpc.handler包,处理服务注册和下线的处理器是InstanceRequestHandler类:

public class InstanceRequestHandler extends RequestHandler<InstanceRequest, InstanceResponse> {

    private final EphemeralClientOperationServiceImpl clientOperationService;

    public InstanceRequestHandler(EphemeralClientOperationServiceImpl clientOperationService) {
        this.clientOperationService = clientOperationService;
    }

    @Secured(action = ActionTypes.WRITE, parser = NamingResourceParser.class)
    public InstanceResponse handle(InstanceRequest request, RequestMeta meta) throws NacosException {
        Service service = Service
                .newService(request.getNamespace(), request.getGroupName(), request.getServiceName(), true);
        switch (request.getType()) {
            // 服务注册
            case NamingRemoteConstants.REGISTER_INSTANCE:
                return registerInstance(service, request, meta);
            // 服务下线
            case NamingRemoteConstants.DE_REGISTER_INSTANCE:
                return deregisterInstance(service, request, meta);
            default:
                throw new NacosException(NacosException.INVALID_PARAM,
                        String.format("Unsupported request type %s", request.getType()));
        }
    }

    // 服务注册
    private InstanceResponse registerInstance(Service service, InstanceRequest request, RequestMeta meta) {
        clientOperationService.registerInstance(service, request.getInstance(), meta.getConnectionId());
        return new InstanceResponse(NamingRemoteConstants.REGISTER_INSTANCE);
    }

    // 服务下线
    private InstanceResponse deregisterInstance(
            Service service, InstanceRequest request, RequestMeta meta) {
        clientOperationService.deregisterInstance(service, request.getInstance(), meta.getConnectionId());
        return new InstanceResponse(NamingRemoteConstants.DE_REGISTER_INSTANCE);
    }
}

服务注册核心流程

public void registerInstance(Service service, Instance instance, String clientId) {
    Service singleton = ServiceManager.getInstance().getSingleton(service);
    Client client = clientManager.getClient(clientId);
    InstancePublishInfo instanceInfo = getPublishInfo(instance);
    // Add a new instance for service for current client
    // 1. 给当前客户端绑定service -> instance关系
    // 2. 推送一个ClientChangedEvent事件
    client.addServiceInstance(singleton, instanceInfo);
    client.setLastUpdatedTime();

    // 推送ClientRegisterServiceEvent和InstanceMetadataEvent事件
    NotifyCenter.publishEvent(
        new ClientOperationEvent.ClientRegisterServiceEvent(singleton, clientId));
    NotifyCenter.publishEvent(
        new MetadataEvent.InstanceMetadataEvent(singleton, instanceInfo.getMetadataId(), false));
}
  1. 给当前客户端绑定service -> instance关系
  2. 推送一个ClientChangedEvent事件
  3. 推送ClientRegisterServiceEvent事件
  4. 推送InstanceMetadataEvent事件

事件处理流程

ClientChangedEvent事件:Client changed event. Happened when Client add or remove service. 会由DistroClientDataProcessor进行处理,同步客户端数据到所有服务节点

ClientRegisterServiceEvent事件:Client register service event. 由ClientServiceIndexesManager进行处理,ClientServiceIndexesManager类维护clientId与service的注册关系和订阅关系。另外该处理器会推送一个ServiceChangedEvent事件。

InstanceMetadataEvent事件:实例元数据事件。由NamingMetadataManager进行处理,NamingMetadataManager管理客户端注册的服务和实例元数据信息。InstanceMetadataEvent事件会触发该处理器的实例过期判断

ServiceChangedEvent事件:Service data changed event. 有两个处理器:

  • NamingSubscriberServiceV2Impl - 触发回调服务订阅者任务
  • DoubleWriteEventListener - 触发将服务信息同步到其他nacos节点任务

服务下线

服务下线核心流程

public void deregisterInstance(Service service, Instance instance, String clientId) {
    Service singleton = ServiceManager.getInstance().getSingleton(service);
    Client client = clientManager.getClient(clientId);
    // Remove service instance from client
    // 1. 解除当前客户端的service -> instance关系
    // 2. 推送一个ClientChangedEvent事件
    InstancePublishInfo removedInstance = client.removeServiceInstance(singleton);
    client.setLastUpdatedTime();

    // 推送ClientDeregisterServiceEvent和InstanceMetadataEvent事件
    if (null != removedInstance) {
        NotifyCenter.publishEvent(
            new ClientOperationEvent.ClientDeregisterServiceEvent(singleton, clientId));
        NotifyCenter.publishEvent(
            new MetadataEvent.InstanceMetadataEvent(singleton, removedInstance.getMetadataId(), true));
    }
}
  1. 解除当前客户端的service -> instance关系
  2. 推送一个ClientChangedEvent事件
  3. 推送ClientDeregisterServiceEvent事件
  4. 推送InstanceMetadataEvent事件

事件处理流程

基本与服务注册流程相同。

ClientChangedEvent事件:Client changed event. Happened when Client add or remove service. 会由DistroClientDataProcessor进行处理,同步客户端数据到所有服务节点

ClientDeregisterServiceEvent事件:Client deregister service event. 由ClientServiceIndexesManager进行处理,ClientServiceIndexesManager类维护clientId与service的注册关系和订阅关系。另外该处理器会推送一个ServiceChangedEvent事件。

InstanceMetadataEvent事件:实例元数据事件。由NamingMetadataManager进行处理,NamingMetadataManager管理客户端注册的服务和实例元数据信息。InstanceMetadataEvent事件会触发该处理器的实例过期判断

ServiceChangedEvent事件:Service data changed event. 有两个处理器:

  • NamingSubscriberServiceV2Impl - 触发回调服务订阅者任务
  • DoubleWriteEventListener - 触发将服务信息同步到其他nacos节点任务

服务实例心跳

  1. 客户端会周期性的发送healthCheck请求
  2. 服务端每次收到客户端请求时都会更新对应connection的活跃时间戳
  3. 服务端也会周期性的检查客户端connection的活跃时间戳和客户端IP连接数,当超过一定的时间不活跃,服务端会发一个检测请求给客户端,当连接数超过阈值时将重置多余的连接

客户端healthCheck请求

客户端会周期性发送healthCheck请求,默认每5秒执行一次,在RpcClient中:

clientEventExecutor.submit(new Runnable() {
    @Override
    public void run() {
        while (true) {
            try {
                if (isShutdown()) {
                    break;
                }
                ReconnectContext reconnectContext = reconnectionSignal
                        .poll(keepAliveTime, TimeUnit.MILLISECONDS);
                if (reconnectContext == null) {
                    //check alive time.
                    if (System.currentTimeMillis() - lastActiveTimeStamp >= keepAliveTime) {
                        boolean isHealthy = healthCheck();
                        if (!isHealthy) {
                            if (currentConnection == null) {
                                continue;
                            }
                            
                            RpcClientStatus rpcClientStatus = RpcClient.this.rpcClientStatus.get();
                            if (RpcClientStatus.SHUTDOWN.equals(rpcClientStatus)) {
                                break;
                            }
// ...

healthCheck健康检查:

private boolean healthCheck() {
    HealthCheckRequest healthCheckRequest = new HealthCheckRequest();
    try {
        Response response = this.currentConnection.request(healthCheckRequest, 3000L);
        return response != null && response.isSuccess();
    } catch (NacosException e) {
        //ignore
    }
    return false;
}

如果检查失败,将重新建立连接。

服务端记录connection活跃时间戳

服务端每次收到客户端请求时都会更新对应connection的活跃时间戳。

服务端使用GrpcRequestAcceptor作为业务层请求Acceptor入口,这个类会将GRPC的请求转为业务层请求,并转发到对应的RequestHandler处理器。

在其request方法中,会刷新对应connection的活跃时间戳:

Connection connection = connectionManager.getConnection(CONTEXT_KEY_CONN_ID.get());
RequestMeta requestMeta = new RequestMeta();
requestMeta.setClientIp(connection.getMetaInfo().getClientIp());
requestMeta.setConnectionId(CONTEXT_KEY_CONN_ID.get());
requestMeta.setClientVersion(connection.getMetaInfo().getVersion());
requestMeta.setLabels(connection.getMetaInfo().getLabels());
// 刷新connection的活跃时间戳
connectionManager.refreshActiveTime(requestMeta.getConnectionId());
Response response = requestHandler.handleRequest(request, requestMeta);
Payload payloadResponse = GrpcUtils.convert(response);
traceIfNecessary(payloadResponse, false);
responseObserver.onNext(payloadResponse);
responseObserver.onCompleted();

服务端connection活跃检查

服务端周期性检查客户端connection的活跃时间戳和客户端IP连接数,当超过一定的时间不活跃,服务端会发一个检测请求给客户端,当连接数超过阈值时将重置多余的连接。

服务端使用ConnectionManager管理连接:

Map<String, Connection> connections = new ConcurrentHashMap<String, Connection>();

在启动时,会创建周期性任务检查connections的活跃状态,默认每3秒执行一次,以下为代码片段:

// 检查长时间不活跃的连接和超过最大连接数的连接
for (Map.Entry<String, Connection> entry : entries) {
    Connection client = entry.getValue();
    String clientIp = client.getMetaInfo().getClientIp();
    AtomicInteger integer = expelForIp.get(clientIp);
    if (integer != null && integer.intValue() > 0) {
        integer.decrementAndGet();
        expelClient.add(client.getMetaInfo().getConnectionId());
        expelCount--;
    } else if (now - client.getMetaInfo().getLastActiveTime() >= KEEP_ALIVE_TIME) {
        outDatedConnections.add(client.getMetaInfo().getConnectionId());
    }
}

// ...

// 重置超过最大连接数的连接
for (String expelledClientId : expelClient) {
    try {
        Connection connection = getConnection(expelledClientId);
        if (connection != null) {
            ConnectResetRequest connectResetRequest = new ConnectResetRequest();
            connectResetRequest.setServerIp(serverIp);
            connectResetRequest.setServerPort(serverPort);
            connection.asyncRequest(connectResetRequest, null);
        }
    } catch (ConnectionAlreadyClosedException e) {
        unregister(expelledClientId);
    } catch (Exception e) {

    }
}

// ...

if (CollectionUtils.isNotEmpty(outDatedConnections)) {
    Set<String> successConnections = new HashSet<>();
    final CountDownLatch latch = new CountDownLatch(outDatedConnections.size());
    for (String outDateConnectionId : outDatedConnections) {
        try {
            Connection connection = getConnection(outDateConnectionId);
            if (connection != null) {
                // 给客户端发检测请求
                ClientDetectionRequest clientDetectionRequest = new ClientDetectionRequest();
                connection.asyncRequest(clientDetectionRequest, new RequestCallBack() {
                    @Override
                    public Executor getExecutor() {
                        return null;
                    }

                    @Override
                    public long getTimeout() {
                        return 1000L;
                    }

                    @Override
                    public void onResponse(Response response) {
                        latch.countDown();
                        if (response != null && response.isSuccess()) {
                            connection.freshActiveTime();
                            successConnections.add(outDateConnectionId);
                        }
                    }

                    @Override
                    public void onException(Throwable e) {
                        latch.countDown();
                    }
                });
            } else {
                latch.countDown();
            }

        } catch (ConnectionAlreadyClosedException e) {
            latch.countDown();
        } catch (Exception e) {
            latch.countDown();
        }
    }

    latch.await(3000L, TimeUnit.MILLISECONDS);

    // 移除失败的已断开连接
    for (String outDateConnectionId : outDatedConnections) {
        if (!successConnections.contains(outDateConnectionId)) {
            unregister(outDateConnectionId);
        }
    }
}

客户端断开连接

业务处理流程

GRPC连接层检测到连接断开之后,会触发GrpcServer的transportTerminated事件:

public void transportTerminated(Attributes transportAttrs) {
    String connectionId = null;
    try {
        connectionId = transportAttrs.get(TRANS_KEY_CONN_ID);
    } catch (Exception e) {
        // Ignore
    }
    if (StringUtils.isNotBlank(connectionId)) {
        // 使用ConnectionManager移除连接
        connectionManager.unregister(connectionId);
    }
}

ConnectionManager移除连接:

public synchronized void unregister(String connectionId) {
    // 从Connection集移除连接
    Connection remove = this.connections.remove(connectionId);
    if (remove != null) {
        String clientIp = remove.getMetaInfo().clientIp;
        AtomicInteger atomicInteger = connectionForClientIp.get(clientIp);
        // IP连接数--
        if (atomicInteger != null) {
            int count = atomicInteger.decrementAndGet();
            if (count <= 0) {
                connectionForClientIp.remove(clientIp);
            }
        }
        remove.close();
        // 通知ClientManager层断开连接
        clientConnectionEventListenerRegistry.notifyClientDisConnected(remove);
    }
}

ConnectionBasedClientManager的clientDisconnected方法:

public boolean clientDisconnected(String clientId) {
    ConnectionBasedClient client = clients.remove(clientId);
    if (null == client) {
        return true;
    }
    client.release();
    // 推送一个ClientDisconnectEvent事件
    NotifyCenter.publishEvent(new ClientEvent.ClientDisconnectEvent(client));
    return true;
}

事件处理流程

ClientDisconnectEvent事件:Client disconnect event. Happened when Client disconnect with server.

  • ClientServiceIndexesManager - 维护注册和订阅关系
  • DistroClientDataProcessor - 同步客户端数据到所有服务节点
  • NamingMetadataManager - 维护客户端注册的服务和实例元数据信息

查询实例

ServiceQueryRequestHandler处理器

ServiceQueryRequestHandler类负责客户端的服务实例查询请求:

public class ServiceQueryRequestHandler extends RequestHandler<ServiceQueryRequest, QueryServiceResponse> {
    
    private final ServiceStorage serviceStorage;
    
    private final NamingMetadataManager metadataManager;
    
    public ServiceQueryRequestHandler(ServiceStorage serviceStorage,
                                      NamingMetadataManager metadataManager) {
        this.serviceStorage = serviceStorage;
        this.metadataManager = metadataManager;
    }
    
    @Override
    @Secured(action = ActionTypes.READ, parser = NamingResourceParser.class)
    public QueryServiceResponse handle(
           ServiceQueryRequest request, RequestMeta meta) throws NacosException {

        String namespaceId = request.getNamespace();
        String groupName = request.getGroupName();
        String serviceName = request.getServiceName();
        Service service = Service.newService(namespaceId, groupName, serviceName);
        String cluster = null == request.getCluster() ? "" : request.getCluster();
        boolean healthyOnly = request.isHealthyOnly();

        // ServiceInfo封装服务基本信息和其实例集合
        ServiceInfo result = serviceStorage.getData(service);
        ServiceMetadata serviceMetadata = metadataManager.getServiceMetadata(service).orElse(null);
        result = ServiceUtil
            .selectInstancesWithHealthyProtection(result, serviceMetadata, cluster, healthyOnly, true);

        return QueryServiceResponse.buildSuccessResponse(result);
    }
}

查询服务实例:

public ServiceInfo getData(Service service) {
    // 如果缓存里面有服务信息则直接从缓存查找
    return serviceDataIndexes.containsKey(service) ? serviceDataIndexes.get(service) : getPushData(service);
}

public ServiceInfo getPushData(Service service) {
    ServiceInfo result = emptyServiceInfo(service);
    if (!ServiceManager.getInstance().containSingleton(service)) {
        return result;
    }
    // 从ClientServiceIndexesManager查找
    result.setHosts(getAllInstancesFromIndex(service));
    serviceDataIndexes.put(service, result);
    return result;
}

private List<Instance> getAllInstancesFromIndex(Service service) {
    Set<Instance> result = new HashSet<>();
    Set<String> clusters = new HashSet<>();
    // 从ClientServiceIndexesManager查找service绑定的client集
    for (String each : serviceIndexesManager.getAllClientsRegisteredService(service)) {
        // 查找该client注册的实例信息
        Optional<InstancePublishInfo> instancePublishInfo = getInstanceInfo(each, service);
        if (instancePublishInfo.isPresent()) {
            Instance instance = parseInstance(service, instancePublishInfo.get());
            result.add(instance);
            clusters.add(instance.getClusterName());
        }
    }
    // cache clusters of this service
    serviceClusterIndex.put(service, clusters);
    return new LinkedList<>(result);
}

private Optional<InstancePublishInfo> getInstanceInfo(String clientId, Service service) {
    // 获取到client对象
    Client client = clientManager.getClient(clientId);
    if (null == client) {
        return Optional.empty();
    }
    // 查找该client指定service注册的实例信息
    // AbstractClient使用Map<Service, InstancePublishInfo>结构保存
    // 前文介绍过在服务注册时会使用client.addServiceInstance方法添加注册信息
    return Optional.ofNullable(client.getInstancePublishInfo(service));
}

前文介绍过ClientServiceIndexesManager类维护clientId与service的注册关系和订阅关系。

服务订阅

SubscribeServiceRequestHandler处理器

SubscribeServiceRequestHandler类负责客户端的服务订阅请求:

public class SubscribeServiceRequestHandler extends 
             RequestHandler<SubscribeServiceRequest, SubscribeServiceResponse> {

    private final ServiceStorage serviceStorage;

    private final NamingMetadataManager metadataManager;

    private final EphemeralClientOperationServiceImpl clientOperationService;

    public SubscribeServiceRequestHandler(ServiceStorage serviceStorage,
            NamingMetadataManager metadataManager,
            EphemeralClientOperationServiceImpl clientOperationService) {
        this.serviceStorage = serviceStorage;
        this.metadataManager = metadataManager;
        this.clientOperationService = clientOperationService;
    }

    @Secured(action = ActionTypes.READ, parser = NamingResourceParser.class)
    public SubscribeServiceResponse handle(
           SubscribeServiceRequest request, RequestMeta meta) throws NacosException {

        String namespaceId = request.getNamespace();
        String serviceName = request.getServiceName();
        String groupName = request.getGroupName();
        String app = request.getHeader("app", "unknown");
        String groupedServiceName = NamingUtils.getGroupedName(serviceName, groupName);
        Service service = Service.newService(namespaceId, groupName, serviceName, true);

        // 封装Subscriber对象:客户端IP、版本、命名空间等
        Subscriber subscriber = new Subscriber(meta.getClientIp(), meta.getClientVersion(), app,
                meta.getClientIp(), namespaceId, groupedServiceName, 0, request.getClusters());

        ServiceInfo serviceInfo = handleClusterData(serviceStorage.getData(service),
                metadataManager.getServiceMetadata(service).orElse(null),
                subscriber);

        if (request.isSubscribe()) {
            // 服务订阅
            clientOperationService.subscribeService(service, subscriber, meta.getConnectionId());
        } else {
            // 取消订阅
            clientOperationService.unsubscribeService(service, subscriber, meta.getConnectionId());
        }
        return new SubscribeServiceResponse(ResponseCode.SUCCESS.getCode(), "success", serviceInfo);
    }

    private ServiceInfo handleClusterData(
            ServiceInfo data, ServiceMetadata metadata, Subscriber subscriber) {
        return StringUtils.isBlank(subscriber.getCluster()) ? data
                : ServiceUtil.selectInstancesWithHealthyProtection(data, metadata, subscriber.getCluster());
    }
}

服务订阅核心流程

public void subscribeService(Service service, Subscriber subscriber, String clientId) {
    Service singleton = ServiceManager.getInstance().getSingletonIfExist(service).orElse(service);
    Client client = clientManager.getClient(clientId);
    // 为该client绑定service -> subscriber关系
    client.addServiceSubscriber(singleton, subscriber);
    client.setLastUpdatedTime();
    // 推送一个ClientSubscribeServiceEvent事件
    NotifyCenter.publishEvent(new ClientOperationEvent.ClientSubscribeServiceEvent(singleton, clientId));
}

事件处理流程

ClientSubscribeServiceEvent事件:Client subscribe service event. 由ClientServiceIndexesManager进行处理,ClientServiceIndexesManager类维护clientId与service的注册关系和订阅关系

private void addSubscriberIndexes(Service service, String clientId) {
    subscriberIndexes.computeIfAbsent(service, (key) -> new ConcurrentHashSet<>());
    // Only first time add need notify event.
    if (subscriberIndexes.get(service).add(clientId)) {
        // 推送一个ServiceSubscribedEvent事件
        NotifyCenter.publishEvent(new ServiceEvent.ServiceSubscribedEvent(service, clientId));
    }
}

ServiceSubscribedEvent事件:Service is subscribed by one client event. NamingSubscriberServiceV2Impl进行处理。

public void onEvent(Event event) {
    if (event instanceof ServiceEvent.ServiceChangedEvent) {
        // If service changed, push to all subscribers.
        ServiceEvent.ServiceChangedEvent serviceChangedEvent = (ServiceEvent.ServiceChangedEvent) event;
        Service service = serviceChangedEvent.getService();
        delayTaskEngine.addTask(
            service, new PushDelayTask(service, PushConfig.getInstance().getPushTaskDelay()));
    } else if (event instanceof ServiceEvent.ServiceSubscribedEvent) {
        // 触发一次订阅者回调,把被订阅的服务的信息推送给订阅者
        ServiceEvent.ServiceSubscribedEvent subscribedEvent = (ServiceEvent.ServiceSubscribedEvent) event;
        Service service = subscribedEvent.getService();
        delayTaskEngine.addTask(
            service, new PushDelayTask(service, PushConfig.getInstance().getPushTaskDelay(),
                subscribedEvent.getClientId()));
    }
}

取消服务订阅

public void unsubscribeService(Service service, Subscriber subscriber, String clientId) {
    Service singleton = ServiceManager.getInstance().getSingletonIfExist(service).orElse(service);
    Client client = clientManager.getClient(clientId);
    client.removeServiceSubscriber(singleton);
    client.setLastUpdatedTime();
    NotifyCenter.publishEvent(new ClientOperationEvent.ClientUnsubscribeServiceEvent(singleton, clientId));
}

推送一个ClientUnsubscribeServiceEvent事件,还是使用ClientServiceIndexesManager来处理,移除订阅关系。文章来源地址https://www.toymoban.com/news/detail-646405.html

到了这里,关于Nacos源码 (3) 注册中心的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • Nacos:服务的注册、发现和配置中心(注册篇)

    Nacos 是阿里巴巴推出来的一个新开源项目,这是一个更易于构建云原生应用的动态服务发现、配置管理和服务管理平台。 Nacos 致力于帮助您发现、配置和管理微服务。Nacos 提供了一组简单易用的特性集,帮助您快速实现动态服务发现、服务配置、服务元数据及流量管理。 N

    2024年02月15日
    浏览(38)
  • springcloud/springboot集成NACOS 做注册和配置中心以及nacos源码分析

    Spring Cloud 是一系列框架的有序集合如服务发现注册、配置中心、消息总线、负载均衡、熔断器、数据监控等。 SpringCloud 将多个服务框架组合起来,通过Spring Boot进行再封装,屏蔽掉了复杂的配置和实现原理,最终给开发者提供了一套简单易懂、易部署和易维护的分布式系统开

    2024年02月08日
    浏览(62)
  • Ribbon和 Nacos服务注册中心

    ✅作者简介:大家好,我是Leo,热爱Java后端开发者,一个想要与大家共同进步的男人😉😉 🍎个人主页:Leo的博客 💞当前专栏: 微服务探索之旅 ✨特色专栏: MySQL学习 🥭本文内容:Ribbon和 Nacos服务注册中心 🖥️个人小站 :个人博客,欢迎大家访问 📚个人知识库: 知

    2024年02月09日
    浏览(39)
  • nacos 注册中心服务端配置详解

    Nacos是一个开源的动态服务发现、配置管理和服务管理平台。在使用Nacos作为注册中心时,需要配置Nacos Server的相关参数。 下面是Nacos Server的配置参数详解: nacos.core.default.namespace: Nacos默认的命名空间,用于隔离不同的环境或不同的应用。默认值为public。 nacos.naming.data.warmup

    2024年02月13日
    浏览(39)
  • Nacos作为服务注册中心简单示例

    主要包含两个服务: zhshl-order服务: 作为服务消费者 zhsl-stock服务: 作为服务提供者 当我们启用服务发现的时候,需要进行的操作主要有三步 0、前置条件,需要先搭建好一个nacas服务,可以是一个集群或者是单个nacos服务。可以参考https://nacos.io/zh-cn/docs/quick-start.html, 示例中使用

    2024年02月07日
    浏览(84)
  • Spring Cloud Alibaba 微服务2,注册中心演变 + Nacos注册中心与配置中心

    🏆作者简介: 哪吒 ,CSDN2022博客之星Top1、CSDN2021博客之星Top2、多届新星计划导师✌、博客专家💪 , 专注Java硬核干货分享,立志做到Java赛道全网Top N。 🏆本文收录于 Java基础教程系列(进阶篇) ,本专栏是针对大学生、初级Java工程师精心打造, 针对Java生态,逐个击破,

    2024年02月02日
    浏览(268)
  • 微服务(二)——注册中心(Eureka、Nacos)

    注册中心: 需求:当一个服务提供者 Service 部署了多个实例交给 User 远程调用时: 服务消费者 User 应该调用哪个实例,如何获取其对应地址和端口? User 如何获知实例是否健康? 注册中心作用: 帮助管理服务,并帮助服务调用者选择并调用服务 实时监测服务实例是否健康

    2024年02月12日
    浏览(39)
  • 【微服务】Nacos注册中心(3):配置管理

    给微服务配置namespace 给微服务配置namespace只能通过修改配置来实现。 重启order-service后,访问控制台,可以看到下面的结果 此时访问order-service,因为namespace不同,会导致找不到userservice,控制台会报错 Nacos的服务实例分为两种l类型: 临时实例:如果实例宕机超过一定时间,

    2024年02月05日
    浏览(47)
  • 基于Dubbo和Nacos搭建微服务的配置中心和服务注册中心

    1.添加nacos依赖 2.配置application.properties PS:nacos2.0除了需要8848的端口之外,还新增了端口+1000的新端口用于客户端和服务端GRPC通信的端口,所以如果是自己部署的nacos需要放开这俩个端口,服务才可以启动成功。 3.配置类 3.1 添加Spring Cloud原生注解 @EnableDiscoveryClient 3.2 客户端服

    2024年02月16日
    浏览(52)
  • nacos2.1.1版本,作为注册中心,配置中心如何使用,nacos服务端启动,微服务搭建,nacos集群化部署

    关于nacos 相信大多数人一样,在接触微服务的时候知道有Netflix系列和alibaba系列 但是Netflix这一套如今由于部分组件不在开源,又有alibaba这一套的出现,市面上,alibaba系列微服务架构已经是最火热的了,而nacos则是alibaba系列组件充当服务注册发现和配置中心的角色 说白了,就是将每个

    2024年02月03日
    浏览(48)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包