实例注册——服务端处理

RequestHandler

nacos所有request处理的父类,子类需要实现handle方法

package com.alibaba.nacos.core.remote;
/**
* Nacos based request handler.
*
*
*/
@SuppressWarnings("PMD.AbstractClassShouldStartWithAbstractNamingRule")
public abstract class RequestHandler<T extends Request, S extends Response> {

@Autowired
private RequestFilters requestFilters;

/**
* Handler request.
*/
public Response handleRequest(T request, RequestMeta meta) throws NacosException {
for (AbstractRequestFilter filter : requestFilters.filters) {
try {
Response filterResult = filter.filter(request, meta, this.getClass());
if (filterResult != null && !filterResult.isSuccess()) {
return filterResult;
}
} catch (Throwable throwable) {
Loggers.REMOTE.error("filter error", throwable);
}

}
return handle(request, meta);
}

/**
* Handler request.
*/
public abstract S handle(T request, RequestMeta meta) throws NacosException;

}

InstanceRequestHandler

RequestHandler的子类

需要留意这俩集合singletonRepository、namespaceSingletonMaps,这是v2版本中注册信息的数据结构,均是ConcurrentHashMap类型的集合

核心处理方法handle,是服务端InstanceRequest处理入口,会根据请求类型分别处理注册与注销。这里只看注册逻辑

registerInstance方法三块

  1. registerInstance 向服务注册实例
  2. publishEvent 发布RegisterInstanceTraceEvent
  3. 生成InstanceResponse返回

package com.alibaba.nacos.naming.remote.rpc.handler;
/**
* Instance request handler.
* 继承自 RequestHandler ,这是nacos处理reques的基类
* @author xiweng.yy
*/
@Component
public class InstanceRequestHandler extends RequestHandler<InstanceRequest, InstanceResponse> {
/**
* rpc的操作都是针对临时服务,http是持久服务
* v2中已将临时/持久提升到服务层级,而不再是实例级别
*/
private final EphemeralClientOperationServiceImpl clientOperationService;

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

@Override
@Secured(action = ActionTypes.WRITE)
public InstanceResponse handle(InstanceRequest request, RequestMeta meta) throws NacosException {
Service service = Service
.newService(request.getNamespace(), request.getGroupName(), request.getServiceName(), true);
//根据request类型分别处理注册与注销,其他抛异常
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)
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()));
//生成InstanceResponse并返回
return new InstanceResponse(NamingRemoteConstants.REGISTER_INSTANCE);
}

private InstanceResponse deregisterInstance(Service service, InstanceRequest request, RequestMeta meta) {
//与注册基本相同,这里略过
......
}

}


1、EphemeralClientOperationServiceImpl.registerInstance

rpc的操作都是针对临时服务,http是持久服务,这里是rpc请求处理,所以是EphemeralClientOperationServiceImpl,而不是PersistentClientOperationServiceImpl

接口ClientOperationService是客户端操作服务(注册、批量注册、注销、订阅、取消订阅、实例转为发布实例),共有三个实现类,分别是临时、持久及临时与持久的组合(内部根据实例的 是否临时属性确定使用哪个)

package com.alibaba.nacos.naming.core.v2.service.impl;
/**
* Operation service for ephemeral clients and services.
*
* @author xiweng.yy
*/
@Component("ephemeralClientOperationService")
public class EphemeralClientOperationServiceImpl implements ClientOperationService {

private final ClientManager clientManager;
/**
* 直接指定 ClientManagerDelegate
* @param clientManager
*/
public EphemeralClientOperationServiceImpl(ClientManagerDelegate clientManager) {
this.clientManager = clientManager;
}

@Override
public void registerInstance(Service service, Instance instance, String clientId) throws NacosException {
NamingUtils.checkInstanceIsLegal(instance);
// 1 从缓存中取得服务
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()));
}
// 2 从缓存中取得client
Client client = clientManager.getClient(clientId);
if (!clientIsLegal(client, clientId)) {
return;
}
// 3 将实例转为服务端实例,为当前client添加服务下实例
InstancePublishInfo instanceInfo = getPublishInfo(instance);
client.addServiceInstance(singleton, instanceInfo);
client.setLastUpdatedTime();
client.recalculateRevision();
// 4 发布事件 ClientRegisterServiceEvent
NotifyCenter.publishEvent(new ClientOperationEvent.ClientRegisterServiceEvent(singleton, clientId));
// 4 发布事件 InstanceMetadataEvent
NotifyCenter
.publishEvent(new MetadataEvent.InstanceMetadataEvent(singleton, instanceInfo.getMetadataId(), false));
}

@Override
public void batchRegisterInstance(Service service, List<Instance> instances, String clientId) {
//略
}

@Override
public void deregisterInstance(Service service, Instance instance, String clientId) {
//略
}

@Override
public void subscribeService(Service service, Subscriber subscriber, String clientId) {
//略
}

@Override
public void unsubscribeService(Service service, Subscriber subscriber, String clientId) {
//略
}

private boolean clientIsLegal(Client client, String clientId) {
if (client == null) {
return false;
}
if (!client.isEphemeral()) {
return false;
}
return true;
}
}

1.1、ServiceManager.getInstance().getSingleton(service)

这是nacos v2 中注册信息的数据结构,重点关注这俩集合singletonRepository、namespaceSingletonMaps。

package com.alibaba.nacos.naming.core.v2;
/**
* Nacos service manager for v2.
* 重点关注这俩集合singletonRepository、namespaceSingletonMaps
* 这是nacos v2 中注册信息的数据结构
*/
public class ServiceManager {

private static final ServiceManager INSTANCE = new ServiceManager();
/**
* singletonRepository的类型
*/
private final ConcurrentHashMap<Service, Service> singletonRepository;
/**
* namespaceSingletonMaps的类型,key为命名空间
*/
private final ConcurrentHashMap<String, Set<Service>> namespaceSingletonMaps;

private ServiceManager() {
//初始化集合size=1024
singletonRepository = new ConcurrentHashMap<>(1 << 10);
//初始化集合size=4
namespaceSingletonMaps = new ConcurrentHashMap<>(1 << 2);
}

public static ServiceManager getInstance() {
return INSTANCE;
}

public Set<Service> getSingletons(String namespace) {
return namespaceSingletonMaps.getOrDefault(namespace, new HashSet<>(1));
}

/**
* Get singleton service. Put to manager if no singleton.
*
* @param service new service
* @return if service is exist, return exist service, otherwise return new service
*/
public Service getSingleton(Service service) {
//如果singletonRepository中不存在当前service,则存入当前service
singletonRepository.putIfAbsent(service, service);
//取出singletonRepository中所存储的service(之前有就取出来,)
Service result = singletonRepository.get(service);
//如果namespaceSingletonMaps中没有service对应命名空间则存入当前service对应的命名空间
namespaceSingletonMaps.computeIfAbsent(result.getNamespace(), (namespace) -> new ConcurrentHashSet<>());
//将当前service对应命名空间存入namespaceSingletonMaps
namespaceSingletonMaps.get(result.getNamespace()).add(result);
return result;
}
//其他省略
......
}

1.2、ClientManagerDelegate.getClient

ClientManager 的委托代理,会根据方法 getClientManagerById 的结果决定使用哪个 ClientManager。具体实现很简洁

package com.alibaba.nacos.naming.core.v2.client.manager;
/**
* Client manager delegate.
* ClientManager 的委托代理,会根据方法 getClientManagerById 的结果决定使用哪个 ClientManager
* @author xiweng.yy
*/
@DependsOn({"clientServiceIndexesManager", "namingMetadataManager"})
@Component("clientManager")
public class ClientManagerDelegate implements ClientManager {

private final ConnectionBasedClientManager connectionBasedClientManager;

private final EphemeralIpPortClientManager ephemeralIpPortClientManager;

private final PersistentIpPortClientManager persistentIpPortClientManager;

public ClientManagerDelegate(ConnectionBasedClientManager connectionBasedClientManager,
EphemeralIpPortClientManager ephemeralIpPortClientManager,
PersistentIpPortClientManager persistentIpPortClientManager) {
this.connectionBasedClientManager = connectionBasedClientManager;
this.ephemeralIpPortClientManager = ephemeralIpPortClientManager;
this.persistentIpPortClientManager = persistentIpPortClientManager;
}

@Override
public boolean clientConnected(String clientId, ClientAttributes attributes) {
return getClientManagerById(clientId).clientConnected(clientId, attributes);
}

@Override
public boolean clientConnected(Client client) {
return getClientManagerById(client.getClientId()).clientConnected(client);
}

@Override
public boolean syncClientConnected(String clientId, ClientAttributes attributes) {
return getClientManagerById(clientId).syncClientConnected(clientId, attributes);
}

@Override
public boolean clientDisconnected(String clientId) {
return getClientManagerById(clientId).clientDisconnected(clientId);
}

@Override
public Client getClient(String clientId) {
return getClientManagerById(clientId).getClient(clientId);
}

@Override
public boolean contains(String clientId) {
return connectionBasedClientManager.contains(clientId) || ephemeralIpPortClientManager.contains(clientId)
|| persistentIpPortClientManager.contains(clientId);
}

@Override
public Collection<String> allClientId() {
Collection<String> result = new HashSet<>();
result.addAll(connectionBasedClientManager.allClientId());
result.addAll(ephemeralIpPortClientManager.allClientId());
result.addAll(persistentIpPortClientManager.allClientId());
return result;
}

@Override
public boolean isResponsibleClient(Client client) {
return getClientManagerById(client.getClientId()).isResponsibleClient(client);
}

@Override
public boolean verifyClient(DistroClientVerifyInfo verifyData) {
return getClientManagerById(verifyData.getClientId()).verifyClient(verifyData);
}

/**
* 这是最关键的,决定具体的 ClientManager
* @param clientId
* @return
*/
private ClientManager getClientManagerById(String clientId) {
if (isConnectionBasedClient(clientId)) {
return connectionBasedClientManager;
}
return clientId.endsWith(ClientConstants.PERSISTENT_SUFFIX) ? persistentIpPortClientManager : ephemeralIpPortClientManager;
}

//略
}


1.3、将实例转为服务端实例,为当前client添加服务实例

//3 将实例转为服务端实例,为当前client添加服务下实例
InstancePublishInfo instanceInfo = getPublishInfo(instance);
client.addServiceInstance(singleton, instanceInfo);
client.setLastUpdatedTime();
client.recalculateRevision();

调用IpPortBasedClient.addServiceInstance方法

//这里的client对应 IpPortBasedClient
@Override
public boolean addServiceInstance(Service service, InstancePublishInfo instancePublishInfo) {
return super.addServiceInstance(service, parseToHealthCheckInstance(instancePublishInfo));
}

调用父类AbstractClient.addServiceInstance方法,这里又发布了一个事件 ClientChangedEvent

/**
* publishers 类型 ConcurrentHashMap<Service, InstancePublishInfo>
*/
protected final ConcurrentHashMap<Service, InstancePublishInfo> publishers = new ConcurrentHashMap<>(16, 0.75f, 1);

/**
* subscribers 类型 ConcurrentHashMap<Service, Subscriber>
*/
protected final ConcurrentHashMap<Service, Subscriber> subscribers = new ConcurrentHashMap<>(16, 0.75f, 1);

@Override
public boolean addServiceInstance(Service service, InstancePublishInfo instancePublishInfo) {
// 将服务与服务端实例存入map,一个服务下只有一个实例的
if (null == publishers.put(service, instancePublishInfo)) {
// 做统计处理
if (instancePublishInfo instanceof BatchInstancePublishInfo) {
MetricsMonitor.incrementIpCountWithBatchRegister(instancePublishInfo);
} else {
MetricsMonitor.incrementInstanceCount();
}
}
// 发布 ClientChangedEvent
NotifyCenter.publishEvent(new ClientEvent.ClientChangedEvent(this));
Loggers.SRV_LOG.info("Client change for service {}, {}", service, getClientId());
return true;
}



1.4、NotifyCenter.publishEvent

通过统一事件通知中心发布事件,解耦+异步。nacos里有大量的事件发布,都是通过这个工具类实现的。

这里先后发布了两个事件:    ClientRegisterServiceEvent、InstanceMetadataEvent


2、NotifyCenter.publishEvent

发布 RegisterInstanceTraceEvent 事件


3、InstanceResponse

生成InstanceResponse并返回


整体流程



nacos——02_配置中心

之前的流程少了1.3部分,补上

nacos——02_配置中心_02