4. 服务端处理客户端的服务查询请求流程
客户端查询服务时,会发送两种请求:
- 第一种:发送订阅服务请求,SubscribeServiceRequest
- 第二种:发送服务查询请求,ServiceQueryRequest
SubscribeServiceRequest 请求处理
在 Nacos 服务端,SubscribeServiceRequest 请求 SubscribeServiceRequestHandler 的 handle 方法进行处理。
@Override
@Secured(action = ActionTypes.READ)
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);
// 根据 request 的信息生成 Service 实例,用于表示服务实例。
Service service = Service.newService(namespaceId, groupName, serviceName, true);
// 根据 request 和 meta 生成 Subscriber 实例,用于表示服务订阅着。
Subscriber subscriber = new Subscriber(meta.getClientIp(), meta.getClientVersion(), app, meta.getClientIp(),
namespaceId, groupedServiceName, 0, request.getClusters());
// 根据 service 实例从 Service 存储器中读取存储的 service 详情信息,得到结果后,封装成 ServiceInfo 对象。
ServiceInfo serviceInfo = ServiceUtil.selectInstancesWithHealthyProtection(serviceStorage.getData(service),
metadataManager.getServiceMetadata(service).orElse(null), subscriber.getCluster(), false,
true, subscriber.getIp());
if (request.isSubscribe()) {
// 如果请求的 subscribe 字段为 true
// 向客户端连接对应的 Client 订阅服务
clientOperationService.subscribeService(service, subscriber, meta.getConnectionId());
// 发布订阅服务追踪事件
NotifyCenter.publishEvent(new SubscribeServiceTraceEvent(System.currentTimeMillis(),
meta.getClientIp(), service.getNamespace(), service.getGroup(), service.getName()));
} else {
// 如果 subscribe 字段为 false
// 向客户端对应的 Client 取消订阅服务
clientOperationService.unsubscribeService(service, subscriber, meta.getConnectionId());
// 发布取消订阅服务追踪事件
NotifyCenter.publishEvent(new UnsubscribeServiceTraceEvent(System.currentTimeMillis(),
meta.getClientIp(), service.getNamespace(), service.getGroup(), service.getName()));
}
// 返回订阅结果
return new SubscribeServiceResponse(ResponseCode.SUCCESS.getCode(), "success", serviceInfo);
}
SubscribeServiceRequestHandler 的 handle 方法的主要逻辑:
- 读取缓存
- 添加订阅者,如果被订阅的服务有变动,需要通知订阅者
读取缓存
// 读取缓存
ServiceInfo serviceInfo = ServiceUtil.selectInstancesWithHealthyProtection(serviceStorage.getData(service),
metadataManager.getServiceMetadata(service).orElse(null), subscriber);
- 调用 serviceStorage 的 getData 方法,得到结果
- 再调用 metadataManager 的 getServiceMetadata 方法,得到结果
- 将上两步得到的结果和订阅者封装成 ServiceInfo
调用 serviceStorage 的 getData 方法
// ServiceStorage
private final ConcurrentMap<Service, ServiceInfo> serviceDataIndexes;
public ServiceInfo getData(Service service) {
// 判断缓存中是否有数据,如果有直接取缓存数据,缓存没有就调用 getPushData(service)
return serviceDataIndexes.containsKey(service) ? serviceDataIndexes.get(service) : getPushData(service);
}
public ServiceInfo getPushData(Service service) {
// 创建空的 ServiceInfo 对象
ServiceInfo result = emptyServiceInfo(service);
if (!ServiceManager.getInstance().containSingleton(service)) {
return result;
}
Service singleton = ServiceManager.getInstance().getSingleton(service);
// 获取实例列表,hosts 属性就是对应 Instance 数据
result.setHosts(getAllInstancesFromIndex(singleton));
// 放入到缓存
serviceDataIndexes.put(singleton, result);
return result;
}
- getData 方法不止是从 serviceDataIndexes 获取数据,还会在获取数据失败后,调用 getPushData 方法将 Service 保存到 serviceDataIndexs 中。getPushData 方法会调用 getAllInstancesFromIndex 方法获取 service 对应的所有示例,一个服务可以有多个实例,每个实例有不同的 host
调用 getAllInstancesFromIndex 方法
private List<Instance> getAllInstancesFromIndex(Service service) {
Set<Instance> result = new HashSet<>();
Set<String> clusters = new HashSet<>();
// 通过 service 获取对应 service 全部的 ClientId
for (String each : serviceIndexesManager.getAllClientsRegisteredService(service)) {
// 遍历每一个 client,通过 clientId 查找注册到 client 的 Instance 信息,
Optional<InstancePublishInfo> instancePublishInfo = getInstanceInfo(each, service);
if (instancePublishInfo.isPresent()) {
// 将获取到的 InstancePublishInfo 转换成 Instance
Instance instance = parseInstance(service, instancePublishInfo.get());
// 将 Instance 存到结果集合内
result.add(instance);
// 将 Instance 对应的集群信息保存到 clusters 集合内。
clusters.add(instance.getClusterName());
}
}
// 缓存该 service 对应有哪些 集群
serviceClusterIndex.put(service, clusters);
// 返回结果
return new LinkedList<>(result);
}
- 获取 service 对应的 client id 列表
- 遍历 client id 列表,获取每一个 client id 内 serivce 对应的服务实例。
- 缓存服务和集群信息,并返回 Instance 实例结果列表
获取 service 对应的 client id 列表
调用 ClientServiceIndexesManager 的 getAllClientsRegisteredService 方法找到 service 对应的 client 列表
public Collection<String> getAllClientsRegisteredService(Service service) {
// 从注册表中获取 clientId 集合
return publisherIndexes.containsKey(service) ? publisherIndexes.get(service) : new ConcurrentHashSet<>();
}
- getAllClientsRegisteredService 会从 publisherIndexes 集合中找到 service 对应的 clientId 列表。(服务端在处理客户端的注册请求时,会将 client 和 service 的对应关系存储到 ClientServiceIndexesManager 的 publisherIndex Map 内,publisherIndex Map 的 key 是 service,value 是为 service 所注册的客户端的 ID 列表,一个服务可以被多个客户端注册)
遍历 client id 列表,获取每一个 client id 内 serivce 对应的服务实例
private Optional<InstancePublishInfo> getInstanceInfo(String clientId, Service service) {
// 通过 clientId 获取每一个对应的 client 连接对象
Client client = clientManager.getClient(clientId);
if (null == client) {
return Optional.empty();
}
// 在 client 对象中,获取实例信息
return Optional.ofNullable(client.getInstancePublishInfo(service));
}
- 调用 clientManager,获取 clientId 对应的 client
- 从 client 中查询 service 对应的服务实例并返回。
调用 metadataManager 的 getServiceMetadata 方法
// NamingMetadataManager
public Optional<ServiceMetadata> getServiceMetadata(Service service) {
return Optional.ofNullable(serviceMetadataMap.get(service));
}
- getServiceMetadata 主要是从 serviceMetadataMap 中获取对应的服务元数据。(元数据在 serviceStorage 的 getData 方法调用的getSingleton 方法中被添加到 serviceMetadataMap 中,getSingleton 方法会获取不到 Service 后,会发布一个 ServiceMetadataEvent 事件,这个事件会向 serviceMetadataMap 添加服务元数据。)
添加订阅者,如果被订阅的服务有变动,需要通知订阅者
if (request.isSubscribe()) {
// 添加订阅者,如果被订阅的服务有变动,需要通知订阅者
clientOperationService.subscribeService(service, subscriber, meta.getConnectionId());
} else {
clientOperationService.unsubscribeService(service, subscriber, meta.getConnectionId());
}
由于使用的是 grpc 调用,所以应该进入 EphemeralClientOperationServiceImpl 的 subscribeService 完成服务订阅
/**
* 订阅者列表
*/
protected final ConcurrentHashMap<Service, Subscriber> subscribers = new ConcurrentHashMap<>(16, 0.75f, 1);
@Override
public void subscribeService(Service service, Subscriber subscriber, String clientId) {
// service 是对应 stock-service
Service singleton = ServiceManager.getInstance().getSingletonIfExist(service).orElse(service);
// 这里的 clientId 是 order-service 的 Client 对象
Client client = clientManager.getClient(clientId);
if (!clientIsLegal(client, clientId)) {
return;
}
// 服务对应的 Client 对象中,添加订阅者
client.addServiceSubscriber(singleton, subscriber);
client.setLastUpdatedTime();
// 发布客户端订阅事件
NotifyCenter.publishEvent(new ClientOperationEvent.ClientSubscribeServiceEvent(singleton, clientId));
}
@Override
public boolean addServiceSubscriber(Service service, Subscriber subscriber) {
// 添加订阅者,key = stock-service 、 value = order-service
if (null == subscribers.put(service, subscriber)) {
MetricsMonitor.incrementSubscribeCount();
}
return true;
}
- 添加订阅者的目的是在服务更新的时候,向对应的 client 推送服务,所以在 subscribeService 方法中,将 client 和服务信息保存到 EphemeralClientOperationServiceImpl 的 subscribers 中,并在订阅者添加完毕后,发布 ClientOperationEvent.ClientSubscribeServiceEvent 事件
- ClientSubscribeServiceEvent 事件最终会被 ClientServiceIndexesManager 的 onEven 方法消费
ClientServiceIndexesManager 的 onEven 方法
@Override
public void onEvent(Event event) {
if (event instanceof ClientEvent.ClientDisconnectEvent) {
handleClientDisconnect((ClientEvent.ClientDisconnectEvent) event);
} else if (event instanceof ClientOperationEvent) {
handleClientOperation((ClientOperationEvent) event); // 消费 ClientOperationEvent 事件
}
}
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); // 消费 ClientOperationEvent.ClientSubscribeServiceEvent 事件
} else if (event instanceof ClientOperationEvent.ClientUnsubscribeServiceEvent) {
removeSubscriberIndexes(service, clientId);
}
}
private final ConcurrentMap<Service, Set<String>> subscriberIndexes = new ConcurrentHashMap<>();
private void addSubscriberIndexes(Service service, String clientId) {
// 将service 和 clientid 添加到 subscriberIndexes 中
subscriberIndexes.computeIfAbsent(service, key -> new ConcurrentHashSet<>());
// Fix #5404, Only first time add need notify event.
if (subscriberIndexes.get(service).add(clientId)) {
// 发布服务订阅事件
NotifyCenter.publishEvent(new ServiceEvent.ServiceSubscribedEvent(service, clientId));
}
}
- ClientServiceIndexesManager 的 onEven 方法最终会将 service 和 client 的映射关系保存到 subscriberIndexes 中。其目的是为了后续有服务变动时,即使向订阅者推送消息。
- 添加映射关系后,会发布 ServiceEvent.ServiceSubscribedEvent 事件。
ServiceQueryRequest 请求处理
在 Nacos 服务端,ServiceQueryRequest 请求 ServiceQueryRequestHandler 的 handle 方法进行处理。ServiceQueryRequestHandler 和 SubscribeServiceRequestHandler 的区别在于,ServiceQueryRequestHandler 只是查询某个服务的详细信息。而 SubscribeServiceRequestHandler 除了查询服务详细信息外,还会在订阅服务变更的消息。
@Override
@Secured(action = ActionTypes.READ)
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 result = serviceStorage.getData(service);
ServiceMetadata serviceMetadata = metadataManager.getServiceMetadata(service).orElse(null);
result = ServiceUtil.selectInstancesWithHealthyProtection(result, serviceMetadata, cluster, healthyOnly, true,
meta.getClientIp());
return QueryServiceResponse.buildSuccessResponse(result);
}
ServiceQueryRequestHandler 方法的主要逻辑:
- 从 request 解析出 namespaceid,groupname, servicename,并根据这些属性创建一个空的 Service
- 从 serviceStorage 中获取 service 对应的服务信息 ServiceInfo
- 从 matadataManager 中获取服务的元数据 ServiceMetadata
- 根据获取到的 ServiceInfo 和 ServiceMetadata 创建一个 ServiceInfo 对象。
- 将这个 ServiceInfo 对象返回给客户端。

浙公网安备 33010602011771号