目录
从控制台修改服务的元数据
从控制台修改实例的元数据
修改后的结果
向JRaftProtocol注册元数据变更处理器
服务在启动的时候通过构造函数进行初始化bean
构造函数里面除了需要自动注入的属性后 还向JRaftProtocol 注册了当前对象处理器。
实例和服务的处理器bean的实例化和注册过程是一样的。
//实例元数据处理器
@Component
public class InstanceMetadataProcessor extends RequestProcessor4CP {
//统一的元数据管理器 管理服务元数据 实例元数据 过期元数据信息
private final NamingMetadataManager namingMetadataManager;
//jraft 消息的序列号器
private final Serializer serializer;
//
private final Type processType;
//普通的lock 控制读写互斥操作使用再快照的加载和保存操作场景
//快照的场景本次不细讲本次注意讲解crud
private final ReentrantReadWriteLock lock;
//读锁 使用再对内存中的元数据crud操作
private final ReentrantReadWriteLock.ReadLock readLock;
@SuppressWarnings("unchecked")
public InstanceMetadataProcessor(NamingMetadataManager namingMetadataManager,
ProtocolManager protocolManager) {
this.namingMetadataManager = namingMetadataManager;
this.serializer = SerializeFactory.getDefault();
this.processType = TypeUtils.parameterize(MetadataOperation.class,
InstanceMetadata.class);
this.lock = new ReentrantReadWriteLock();
this.readLock = lock.readLock();
//向JRaftProtocol 协议注册 InstanceMetadataProcessor
//注册后负责处理JRaft框架底层分发的实例元数据变更任务
protocolManager.getCpProtocol().
addRequestProcessors(Collections.singletonList(this));
}
....
}
//属性字段跟InstanceMetadataProcessor 是完全一样
public class ServiceMetadataProcessor extends RequestProcessor4CP {
....
//初始化属性跟InstanceMetadataProcessor 完全一致
@SuppressWarnings("unchecked")
public ServiceMetadataProcessor(NamingMetadataManager namingMetadataManager,
ProtocolManager protocolManager,
ServiceStorage serviceStorage) {
this.namingMetadataManager = namingMetadataManager;
this.serviceStorage = serviceStorage;
this.serializer = SerializeFactory.getDefault();
this.processType = TypeUtils.parameterize(MetadataOperation.class,
ServiceMetadata.class);
this.lock = new ReentrantReadWriteLock();
this.readLock = lock.readLock();
//向JRaftProtocol 协议注册 InstanceMetadataProcessor
//注册后负责处理JRaft框架底层分发的服务的元数据变更任务
protocolManager.getCpProtocol().
addRequestProcessors(Collections.singletonList(this));
}
}
向JRaftProtocol提交元数据变更请求
服务启动后完成了服务和实例的元数据变更处理器的注册,接下来所有对元数据变更功能只需要向JRaftProtocol提交请求即可。JRaftProtocol 自动完成底层集群节点的一致性同步功能和调用处理器的onApply方法完成更新操作。应用程序只需要实现处理器的处理逻辑即可。
接下来我们看看前端的元数据更新请求是如何向JRaftProtocol提交处理的。
//服务实例控制器
public class InstanceController{
....
@CanDistro
@PutMapping
@Secured(action = ActionTypes.WRITE)
public String update(HttpServletRequest request) throws Exception {
String namespaceId = WebUtils.optional(request, CommonParams.NAMESPACE_ID,
Constants.DEFAULT_NAMESPACE_ID);
String serviceName = WebUtils.required(request, CommonParams.SERVICE_NAME);
NamingUtils.checkServiceNameFormat(serviceName);
Instance instance = HttpRequestInstanceBuilder.newBuilder()
.setDefaultInstanceEphemeral(
switchDomain.isDefaultInstanceEphemeral()).setRequest(request).build();
//调用InstanceOperatorClientImpl类的方法更新实例方法
getInstanceOperator().updateInstance(namespaceId, serviceName, instance);
return "ok";
}
....
}
//服务控制器
public class ServiceController {
....
public String update(HttpServletRequest request) throws Exception {
String namespaceId = WebUtils.optional(request, CommonParams.NAMESPACE_ID,
Constants.DEFAULT_NAMESPACE_ID);
String serviceName = WebUtils.required(request, CommonParams.SERVICE_NAME);
ServiceMetadata serviceMetadata = new ServiceMetadata();
serviceMetadata.setProtectThreshold(
NumberUtils.toFloat(WebUtils.required(request, "protectThreshold")));
serviceMetadata.setExtendData(
UtilsAndCommons.parseMetadata(
WebUtils.optional(request, "metadata", StringUtils.EMPTY)));
serviceMetadata.setSelector(parseSelector(WebUtils.optional(request,
"selector", StringUtils.EMPTY)));
com.alibaba.nacos.naming.core.v2.pojo.Service service =
com.alibaba.nacos.naming.core.v2.pojo.Service
.newService(namespaceId, NamingUtils.getGroupName(serviceName),
NamingUtils.getServiceName(serviceName));
//调用InstanceOperatorClientImpl类的方法的
getServiceOperator().update(service, serviceMetadata);
return "ok";
}
....
}
public class InstanceOperatorClientImpl implements InstanceOperator {
@Override
public void updateInstance(String namespaceId, String serviceName, Instance instance)
throws NacosException {
//更加命名空间 服务吗 临时节点标志新建服务对象
Service service = getService(namespaceId, serviceName, instance.isEphemeral());
//服务管理器如果不存在该服务就报错
if (!ServiceManager.getInstance().containSingleton(service)) {
throw new NacosException(NacosException.INVALID_PARAM,
"service not found, namespace: " + namespaceId + ", service: " +
service);
}
//生成metadataId
String metadataId = InstancePublishInfo
.genMetadataId(instance.getIp(), instance.getPort(),
instance.getClusterName());
//调用NamingMetadataOperateService的updateInstanceMetadata 方法
metadataOperateService.updateInstanceMetadata(service, metadataId,
buildMetadata(instance));
}
}
//metadataId的生成逻辑 ip:port:cluster
public class InstancePublishInfo {
public static String genMetadataId(String ip, int port, String cluster) {
return ip + InternetAddressUtil.IP_PORT_SPLITER + port +
InternetAddressUtil.IP_PORT_SPLITER + cluster;
}
}
我们看到InstanceOperatorClientImpl 的updateInstance方法只是简单了做一个服务的校验和metadataidde生成逻辑就交给NamingMetadataOperateService的updateInstanceMetadata方法处理了。
public class NamingMetadataOperateService {
/**
* Update instance metadata.
*
* @param service service of metadata
* @param metadataId instance metadataId Id
* @param instanceMetadata metadata
*/
public void updateInstanceMetadata(Service service, String metadataId,
InstanceMetadata) {
//构建MetadataOperation对象
MetadataOperation<InstanceMetadata> operation = buildMetadataOperation(service);
operation.setTag(metadataId);
operation.setMetadata(instanceMetadata);
//构建WriteRequest请求WriteRequest 是定义constency.proto文件中
WriteRequest operationLog =
WriteRequest.newBuilder().setGroup(Constants.INSTANCE_METADATA)
.setOperation(DataOperation.CHANGE.name()).
setData(ByteString.copyFrom(serializer.serialize(operation))).build();
submitMetadataOperation(operationLog);
}
private void submitMetadataOperation(WriteRequest operationLog) {
...
//向JRaftProtocol提交请求
Response response = cpProtocol.write(operationLog);
...
}
//根据service 构建 MetadataOperation 对象
private <T> MetadataOperation<T> buildMetadataOperation(Service service) {
MetadataOperation<T> result = new MetadataOperation<>();
result.setNamespace(service.getNamespace());
result.setGroup(service.getGroup());
result.setServiceName(service.getName());
return result;
}
}
public class MetadataOperation<T> implements Serializable {
private static final long serialVersionUID = -111405695252896706L;
private String namespace;
private String group;
private String serviceName;
private String tag;
private T metadata;
}
//constency.proto文件
//WriteRequest 结构体
message WriteRequest {
string group = 1;
string key = 2;
bytes data = 3;
string type = 4;
string operation = 5;
map<string, string> extendInfo = 6;
}
实际上updateInstanceMetadata这个方法并没有立即更新内存中的元数据而是构建一个包含metadata的OperationMetadata对象 , 使用protol协议构建WriteRequest请求对象提交给JRaftProtocol分发和处理。
上面我们详细解析了实例的元数据变更请求的处理过程,服务的元数据变更请求处理过程原理是一样的。这里不赘述。
下一章节我们讲讲元数据处理器收到JRaftProtocol分发的任务后是如何更新本地的元数据的。敬请期待!