上一篇我们讲述了元数据处理器是怎么注册到JRaftProtocol的,用户提交更新请求后,又是如果把请求提交给JraftProtocol,以及JraftProtocol是如何分发请求的。
本篇,我们讲请求处理器接收到任务和是如果处理本地元数据的。
元数据更新处理器
@Component
public class InstanceMetadataProcessor extends RequestProcessor4CP {
//元数据管理器统一的入口
private final NamingMetadataManager namingMetadataManager;
private final Serializer serializer;
private final Type processType;
private final ReentrantReadWriteLock lock;
private final ReentrantReadWriteLock.ReadLock readLock;
....
@Override
public List<SnapshotOperation> loadSnapshotOperate() {
//从本地硬盘加载所有的元数据操作
//lock 用于加载时的锁定避免再加载的时候对元数据的crud
return Collections.singletonList(new
InstanceMetadataSnapshotOperation(namingMetadataManager, lock));
}
@Override
public Response onRequest(ReadRequest request) {
return null;
}
//数据更新请求处理
@Override
public Response onApply(WriteRequest request) {
MetadataOperation<InstanceMetadata> op =
serializer.deserialize(request.getData().
toByteArray(), processType);
//解决跟快照的读写并发问题
readLock.lock();
try {
switch (DataOperation.valueOf(request.getOperation())) {
case ADD:
case CHANGE:
//添加和修改都用更新操作
updateInstanceMetadata(op);
break;
case DELETE:
deleteInstanceMetadata(op);
break;
default:
//默认不做任何处理
return Response.newBuilder().setSuccess(false)
.setErrMsg("Unsupported operation " +
request.getOperation()).build();
}
return Response.newBuilder().setSuccess(true).build();
} catch (Exception e) {
....
} finally {
readLock.unlock();
}
}
private void updateInstanceMetadata(MetadataOperation<InstanceMetadata> op) {
//构建service
Service service = Service.newService(op.getNamespace(), op.getGroup(),
op.getServiceName());
//调用namingMetadataManager处理更新请求
namingMetadataManager.updateInstanceMetadata(service, op.getTag(),
op.getMetadata());
//服务变更事件 通知所有的订阅者 服务变更事件篇以及讲解过了 大家回顾一下
NotifyCenter.publishEvent(new ServiceEvent.ServiceChangedEvent(service, true));
}
private void deleteInstanceMetadata(MetadataOperation<InstanceMetadata> op) {
Service service = Service.newService(op.getNamespace(), op.getGroup(),
op.getServiceName());
//调用namingMetadataManager删除动作
namingMetadataManager.removeInstanceMetadata(service, op.getTag());
}
//常量值是: naming_instance_metadata
//作用后续结合Jraft 再讲
@Override
public String group() {
return Constants.INSTANCE_METADATA;
}
}
上面我们看到onApply方法没有做什么逻辑,直接转发给NamingMetadataManager 处理。
NamingMetadataManager管理器类似于之前的ClientManager ServerManager 和ClientServiceIndexesManager一样都是统一的管理入口类。看到这里暂停下,还记得这些类的功能和关联关系吗?
下面我们看NamingMetadataManager
元数据管理器(NamingMetadataManager )
@Component
public class NamingMetadataManager extends SmartSubscriber {
//维护过期的元数据信息 包括服务和实例的
private final Set<ExpiredMetadataInfo> expiredMetadataInfos;
//维护服务的元数据
private ConcurrentMap<Service, ServiceMetadata> serviceMetadataMap;
//维护实例的元数据
private ConcurrentMap<Service, ConcurrentMap<String, InstanceMetadata>>
instanceMetadataMap;
private static final int INITIAL_CAPACITY = 1;
public NamingMetadataManager() {
//1<<10 = 1024
serviceMetadataMap = new ConcurrentHashMap<>(1 << 10);
instanceMetadataMap = new ConcurrentHashMap<>(1 << 10);
expiredMetadataInfos = new ConcurrentHashSet<>();
//向NotifyCenter注册自己 这块等后面讲到统一事件中心再详细讲解
//此处略过
NotifyCenter.registerSubscriber(this,
NamingEventPublisherFactory.getInstance());
}
//是否包括给服务的元数据
public boolean containServiceMetadata(Service service) {
return serviceMetadataMap.containsKey(service);
}
//是否包含实例的运输局
public boolean containInstanceMetadata(Service service, String metadataId) {
return instanceMetadataMap.containsKey(service) &&
instanceMetadataMap.get(service).containsKey(metadataId);
}
//查询服务的元数据
public Optional<ServiceMetadata> getServiceMetadata(Service service) {
return Optional.ofNullable(serviceMetadataMap.get(service));
}
//查询实例的元数据
public Optional<InstanceMetadata> getInstanceMetadata(Service service, String
metadataId) {
ConcurrentMap<String, InstanceMetadata> instanceMetadataMapForService =
instanceMetadataMap.get(service);
if (null == instanceMetadataMapForService) {
return Optional.empty();
}
return Optional.ofNullable(instanceMetadataMapForService.get(metadataId));
}
//更新服务的元数据 看到这里应该明白为什么update 和 add 都是调用这个方法了
//新增server的版本号
public void updateServiceMetadata(Service service, ServiceMetadata serviceMetadata)
{
service.incrementRevision();
serviceMetadataMap.put(service, serviceMetadata);
}
//更新实例元数据
//实例元数据是再server为key的子map中 如果子map不存在就new一个
public void updateInstanceMetadata(Service service, String metadataId,
InstanceMetadata instanceMetadata) {
if (!instanceMetadataMap.containsKey(service)) {
instanceMetadataMap.putIfAbsent(service, new ConcurrentHashMap<>
(INITIAL_CAPACITY));
}
instanceMetadataMap.get(service).put(metadataId, instanceMetadata);
}
/**
* Remove service metadata.
*
* @param service service
*/
public void removeServiceMetadata(Service service) {
serviceMetadataMap.remove(service);
expiredMetadataInfos.remove(ExpiredMetadataInfo.newExpiredServiceMetadata(service));
}
//移除服务下的某个实例元数据
//如果给服务下么有么有任务实例就删除服务的元数据
public void removeInstanceMetadata(Service service, String metadataId) {
ConcurrentMap<String, InstanceMetadata> instanceMetadataMapForService = instanceMetadataMap.get(service);
instanceMetadataMapForService.remove(metadataId);
if (instanceMetadataMapForService.isEmpty()) {
serviceMetadataMap.remove(service);
}
expiredMetadataInfos.remove(ExpiredMetadataInfo.newExpiredInstanceMetadata(service, metadataId));
}
//从当前的serviceMetadataMap创建快照
//在导出快照的时候crud是不允许操作的 这个通过前面讲的读写锁来控制
public Map<Service, ServiceMetadata> getServiceMetadataSnapshot() {
ConcurrentMap<Service, ServiceMetadata> result = new ConcurrentHashMap<>
(serviceMetadataMap.size());
result.putAll(serviceMetadataMap);
return result;
}
//从当前的instanceMetadataMap创建快照
//在导出快照的时候cud是不允许操作的 这个通过前面讲的读写锁来控制
//导出时完毕不影响查询操作
public Map<Service, ConcurrentMap<String, InstanceMetadata>>
getInstanceMetadataSnapshot() {
ConcurrentMap<Service, ConcurrentMap<String, InstanceMetadata>> result = new
ConcurrentHashMap<>(
instanceMetadataMap.size());
result.putAll(instanceMetadataMap);
return result;
}
//加载服务的元数据快照
//加载时不可以做cud操作同导出快照一样都是由读写锁控制
//加载时完毕不影响查询操作
//最后清空老的数据
public void loadServiceMetadataSnapshot(ConcurrentMap<Service, ServiceMetadata>
snapshot) {
for (Service each : snapshot.keySet()) {
ServiceManager.getInstance().getSingleton(each);
}
ConcurrentMap<Service, ServiceMetadata> oldSnapshot = serviceMetadataMap;
serviceMetadataMap = snapshot;
oldSnapshot.clear();
}
//加载实例数据 同上
public void loadInstanceMetadataSnapshot(ConcurrentMap<Service,
ConcurrentMap<String, InstanceMetadata>> snapshot) {
ConcurrentMap<Service, ConcurrentMap<String, InstanceMetadata>> oldSnapshot =
instanceMetadataMap;
instanceMetadataMap = snapshot;
oldSnapshot.clear();
}
public Set<ExpiredMetadataInfo> getExpiredMetadataInfos() {
return expiredMetadataInfos;
}
//监听元数据变更事件及客户端断开事件
@Override
public List<Class<? extends Event>> subscribeTypes() {
List<Class<? extends Event>> result = new LinkedList<>();
result.add(MetadataEvent.InstanceMetadataEvent.class);
result.add(MetadataEvent.ServiceMetadataEvent.class);
result.add(ClientEvent.ClientDisconnectEvent.class);
return result;
}
//处理事件
@Override
public void onEvent(Event event) {
if (event instanceof MetadataEvent.InstanceMetadataEvent) {
handleInstanceMetadataEvent((MetadataEvent.InstanceMetadataEvent) event);
} else if (event instanceof MetadataEvent.ServiceMetadataEvent) {
handleServiceMetadataEvent((MetadataEvent.ServiceMetadataEvent) event);
} else {
handleClientDisconnectEvent((ClientEvent.ClientDisconnectEvent) event);
}
}
//客户端delete后 找到它所有发布的服务实例
//从InstancePublishInfo中metadataId, 此处回想下之前讲的的Client对象时管理什么的
//如果元数据管理器中有metadataId的数据就移除掉
private void handleClientDisconnectEvent(ClientEvent.ClientDisconnectEvent event) {
for (Service each : event.getClient().getAllPublishedService()) {
String metadataId =
event.getClient().getInstancePublishInfo(each).getMetadataId();
if (containInstanceMetadata(each, metadataId)) {
updateExpiredInfo(true,
ExpiredMetadataInfo.newExpiredInstanceMetadata(each, metadataId));
}
}
}
//只有包含才更新
//注意不包含的情况不能本地添加 因为要保证集群的数据一致性
//这里更新并不是更新元数据内容 而是根据服务的上下线 判断要不要加上元数据的过期信息
private void handleServiceMetadataEvent(MetadataEvent.ServiceMetadataEvent event) {
Service service = event.getService();
if (containServiceMetadata(service)) {
updateExpiredInfo(event.isExpired(),
ExpiredMetadataInfo.newExpiredServiceMetadata(service));
}
}
//只有包含才更新
//注意不包含的情况不能本地添加 因为要保证集群的数据一致性
//这里更新并不是更新元数据内容 而是根据服务的上下线 判断要不要加上元数据的过期信息
private void handleInstanceMetadataEvent(MetadataEvent.InstanceMetadataEvent event)
{
Service service = event.getService();
String metadataId = event.getMetadataId();
if (containInstanceMetadata(service, metadataId)) {
updateExpiredInfo(event.isExpired(),
ExpiredMetadataInfo.newExpiredInstanceMetadata(event.getService(),
event.getMetadataId()));
}
}
//服务下线或者客户端delete掉后expired 值为true
//服务上线后expired为false
private void updateExpiredInfo(boolean expired, ExpiredMetadataInfo
expiredMetadataInfo) {
if (expired) {
expiredMetadataInfos.add(expiredMetadataInfo);
} else {
expiredMetadataInfos.remove(expiredMetadataInfo);
}
}
}
NamingMetadataManager概括一下包括哪些功能呢?
服务和实例袁术的增删改查这是最基本的,还有快照的加载和导出功能。
订阅了服务和实例元数据变更事件 这些事件还记得什么时候发出的吗?
同时订阅了客户端下线的事件 以及3类事件的处理方式。
那么最后给大家看下InstanceMetadata 和 ServiceMetadate的结构是什么样子的。
元数据实体类
public class ServiceMetadata implements Serializable {
private static final long serialVersionUID = -6605609934135069566L;
/**
* Service is ephemeral or persistence.
*/
private boolean ephemeral = true;
/**
* protect threshold.
*/
private float protectThreshold = 0.0F;
/**
* Type of {@link Selector}.
*/
private Selector selector = new NoneSelector();
private Map<String, String> extendData = new ConcurrentHashMap<>(1);
private Map<String, ClusterMetadata> clusters = new ConcurrentHashMap<>(1);
}
public class InstanceMetadata implements Serializable {
private static final long serialVersionUID = -8477858617353459226L;
/**
* instance weight.
*/
private double weight = 1.0D;
/**
* If instance is enabled to accept request.
*/
private boolean enabled = true;
private Map<String, Object> extendData = new ConcurrentHashMap<>(1);
}
public class ExpiredMetadataInfo {
private final Service service;
private final String metadataId;
private final long createTime;
private ExpiredMetadataInfo(Service service, String metadataId) {
this.service = service;
this.metadataId = metadataId;
this.createTime = System.currentTimeMillis();
}
}
上面3个类的数据大部分对我们来说非常熟悉了。不用再赘述了。不懂得同学回头翻翻之前得文章看看。
这里单独说服务下面2个熟悉
// 暂时大家知道是做筛选器使用场景得后面碰到再细讲,还记得服务实例查询得过滤吗用的就这个东西
private Selector selector = new NoneSelector();
//存储服务下集群元数据 key:集群名称
private Map<String, ClusterMetadata> clusters = new ConcurrentHashMap<>(1);