接上篇我们继续讲解 服务端变更事件 ServiceEvent.ServiceChangedEvent
ServiceChangedEvent
public class NamingSubscriberServiceV2Impl extends SmartSubscriber implements NamingSubscriberService {
private static final int PARALLEL_SIZE = 100;
//客户端管理器回顾一下前面讲到的 ClientManagerDelegate 和
//EphemeralIpPortClientManager 管理所有的客户端及客户端的管理功能
private final ClientManager clientManager;
//ClientServiceIndexesManager类前一篇有讲过 管理所有的服务订阅者和发布者信息
private final ClientServiceIndexesManager indexesManager;
//这是Nacos推送机制 主要有udp 和 rpc长连接 这块后面讲解
private final PushDelayTaskExecuteEngine delayTaskEngine;
...
@Override
public Collection<Subscriber> getSubscribers(String namespaceId, String serviceName) {
//从serviceName中获取groupName
String serviceNameWithoutGroup = NamingUtils.getServiceName(serviceName);
String groupName = NamingUtils.getGroupName(serviceName);
//根据命名空间 组名 不在组名的服务名构建service
Service service = Service.newService(namespaceId, groupName,
serviceNameWithoutGroup);
//找到服务的所有订阅者
return getSubscribers(service);
}
//indexesManager.getAllClientsSubscribeService 查询服务的所有订阅客户端
//还记得客户端id格式吗? ip:port#(是否是临时节点的标志true|false)
@Override
public Collection<Subscriber> getSubscribers(Service service) {
Collection<Subscriber> result = new HashSet<>();
for (String each : indexesManager.getAllClientsSubscribeService(service)) {
result.add(clientManager.getClient(each).getSubscriber(service));
}
return result;
}
//根据服务名和组名模糊查找所有匹配条件的service 列表下的订阅者
@Override
public Collection<Subscriber> getFuzzySubscribers(Service service) {
return getFuzzySubscribers(service.getNamespace(),
service.getGroupedServiceName());
}
//模糊匹配所有只要包含 服务名称 和 包含组名的所有的serveice 列表
//返回所有满足匹配条件的service 列表的 所有订阅者 列表
@Override
public Collection<Subscriber> getFuzzySubscribers(String namespaceId, String
serviceName) {
Collection<Subscriber> result = new HashSet<>();
Stream<Service> serviceStream = getServiceStream();
String serviceNamePattern = NamingUtils.getServiceName(serviceName);
String groupNamePattern = NamingUtils.getGroupName(serviceName);
serviceStream.filter(service -> service.getNamespace().equals(namespaceId) &&
service.getName().contains(serviceNamePattern) &&
service.getGroup().contains(groupNamePattern))
.forEach(service -> result.addAll(getSubscribers(service)));
return result;
}
//订阅两类事件
//1、服务变更 和 服务订阅事件
@Override
public List<Class<? extends Event>> subscribeTypes() {
List<Class<? extends Event>> result = new LinkedList<>();
result.add(ServiceEvent.ServiceChangedEvent.class);
result.add(ServiceEvent.ServiceSubscribedEvent.class);
return result;
}
@Override
public void onEvent(Event event) {
if (!upgradeJudgement.isUseGrpcFeatures()) {
return;
}
if (event instanceof ServiceEvent.ServiceChangedEvent) {
// 服务变更会推送给所有的订阅者
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()));
}
}
//如果所有的服务大于100 就用并发流否则使用单流
//PARALLEL_SIZE :100
private Stream<Service> getServiceStream() {
Collection<Service> services = indexesManager.getSubscribedService();
return services.size() > PARALLEL_SIZE ? services.parallelStream() :
services.stream();
}
}
通过上面的代码我们看可以看出 ServiceChangedEvent 事件和 ServiceSubscribedEvent 事件唯一的区别是前者通知所有的订阅者 后者只推送给其中一个订阅者
我们再看看 客户端推送任务类 PushDelayTask 的结构
//延时推送订阅者任务
public class PushDelayTask extends AbstractDelayTask {
// 服务信息
private final Service service;
//是否推送所有订阅者
private boolean pushToAll;
//推送的client 列表
private Set<String> targetClients;
// 没有targetClients参数 代表推送所有的客户端
public PushDelayTask(Service service, long delay) {
this.service = service;
pushToAll = true;
targetClients = null;
setTaskInterval(delay);
setLastProcessTime(System.currentTimeMillis());
}
//推送到指定的客户端
public PushDelayTask(Service service, long delay, String targetClient) {
this.service = service;
this.pushToAll = false;
this.targetClients = new HashSet<>(1);
this.targetClients.add(targetClient);
setTaskInterval(delay);
setLastProcessTime(System.currentTimeMillis());
}
//任务的合并 如果新老任务有一个是推送所有订阅者 那就推送所有订阅之
//如果新老任务都是指定推送,那就叠加
//设置较小的处理时间为新的处理时间
@Override
public void merge(AbstractDelayTask task) {
if (!(task instanceof PushDelayTask)) {
return;
}
PushDelayTask oldTask = (PushDelayTask) task;
if (isPushToAll() || oldTask.isPushToAll()) {
pushToAll = true;
targetClients = null;
} else {
targetClients.addAll(oldTask.getTargetClients());
}
setLastProcessTime(Math.min(getLastProcessTime(), task.getLastProcessTime()));
}
}
//父类
public abstract class AbstractDelayTask implements NacosTask {
//任务处理间隔 多久处理一次 单位毫秒
private long taskInterval;
//任务最近处理 单位毫秒 对象创建时 lastProcessTime 为当前时间
private long lastProcessTime;
// 默认间隔时间1秒钟
protected static final long INTERVAL = 1000L;
}
核心类回顾
代码有一个非常核心的类就是前篇详细讲过的ClientServiceIndexesManager这个类
这个类可以说维护了服务注册表和服务订阅表提供了服务订阅和取消订阅事件的订阅表数据变更,服务上线和服务下线的注册表数据变更 已经查询服务的订阅者列表和服务的发布者列表
同样ClientManagerDelegate 及 EphemeralIpPortClientManager 也是非常核心的类
因为ClientManagerDelegate 管理了三种类型的客户端分别是临时客户端、持久化的客户端、ConnectionBasedClientManager,后面两种后面用到的场景我们再细讲这里略过。
目前为止我们需要用的EphemeralIpPortClientManager。
我们再次回顾一下客户端管理的核心功能:
创建客户端、下线客户端、根据clentid查询客户端、查询当前所有的客户端、查询当前机器负责的客户端 内部维护了一个客户端列表。
同样作为客户端实体对象 IpPortBasedClient 内部通过心跳检查任务(针对临时节点)和健康检查(针对持久节点)维护了客户端的生命周期。
而且IpPortBasedClient 还维护了该客户端的服务注册表和 服务订阅表。