之前我们看了微服务中用nacos做注册中心,openFeing+ribbon 发起调用 ,feign 底层依赖ribbon
从注册中心吧服务列表拉取到本地, 然后在本地实现负载均衡 发起请求
我们知道nacos分client,service,服务注册就是我们服务在启动的时候吧我们的服务注册上去
心跳机制就是检测我们一个client下线了,我们的微服务怎么做到的感知 通过心跳机制让我们nacos的server知道服务还存活
服务下线之后就不会再发送心跳了。server就会认为你已经挂了,就会吧状态设置为down
Nacos 的核心功能点
注册
心跳
健康检查
服务发现
服务同步
包括集群中间数据同步
我们吧服务注册在服务上去的时候 会导入nacos的client的jar, 然后在yaml中进行配置
然后服务启动就吧自己 注册在nacos服务上去了
NacosDiscoveryAutoConfiguration 他的这个自动装配类 就@Bean注入了者三个
NacosServiceRegistry
NacosRegistration
NacosAutoServiceRegistration
然后我们看这个类 的父类
#####看这里
@Deprecated
public void bind(WebServerInitializedEvent event) {
ApplicationContext context = event.getApplicationContext();
if (context instanceof ConfigurableWebServerApplicationContext) {
if ("management".equals(((ConfigurableWebServerApplicationContext) context)
.getServerNamespace())) {
return;
}
}
this.port.compareAndSet(0, event.getWebServer().getPort());
this.start();
}
########### 然后看这个register()
register();
namingService.registerInstance(serviceId, instance);
####### 这里就实际上调用nacos的api 吧我们的实例注册上去了
public void registerService(String serviceName, String groupName, Instance instance) throws NacosException {
NAMING_LOGGER.info("[REGISTER-SERVICE] {} registering service {} with instance: {}",
namespaceId, serviceName, instance);
final Map<String, String> params = new HashMap<String, String>(9);
params.put(CommonParams.NAMESPACE_ID, namespaceId);
params.put(CommonParams.SERVICE_NAME, serviceName);
params.put(CommonParams.GROUP_NAME, groupName);
params.put(CommonParams.CLUSTER_NAME, instance.getClusterName());
params.put("ip", instance.getIp());
params.put("port", String.valueOf(instance.getPort()));
params.put("weight", String.valueOf(instance.getWeight()));
params.put("enable", String.valueOf(instance.isEnabled()));
params.put("healthy", String.valueOf(instance.isHealthy()));
params.put("ephemeral", String.valueOf(instance.isEphemeral()));
params.put("metadata", JSON.toJSONString(instance.getMetadata()));
reqAPI(UtilAndComs.NACOS_URL_INSTANCE, params, HttpMethod.POST);
}
上面说的是client客户端源码 下来我们说下服务端的源码
我们现在要看客户端传过来的Instence是怎么注册的
#####这个是服务端源码 当接受一个服务注册的请求的时候 我们服务端会怎么去做
public void registerInstance(String namespaceId, String serviceName, Instance instance) throws NacosException {
createEmptyService(namespaceId, serviceName, instance.isEphemeral());
Service service = getService(namespaceId, serviceName);
if (service == null) {
throw new NacosException(NacosException.INVALID_PARAM,
"service not found, namespace: " + namespaceId + ", service: " + serviceName);
}
addInstance(namespaceId, serviceName, instance.isEphemeral(), instance);
}
####### 看这个
createEmptyService(namespaceId, serviceName, instance.isEphemeral());
###########
这是nacos中服务的节点的信息内存结构 双层map存储注册信息
/**
* Map(namespace, Map(group::serviceName, Service)).
*/
private final Map<String, Map<String, Service>> serviceMap = new ConcurrentHashMap<>();
这里put了一个新的内存结构
public void putService(Service service) {
if (!serviceMap.containsKey(service.getNamespaceId())) {
synchronized (putServiceLock) {
if (!serviceMap.containsKey(service.getNamespaceId())) {
serviceMap.put(service.getNamespaceId(), new ConcurrentSkipListMap<>());
}
}
}
serviceMap.get(service.getNamespaceId()).put(service.getName(), service);
}
紧接着我们看这里的init 意味着初始化要做的操作
// 初始化要做检查任务 这个是做心跳检查的(客户端定时周期发心跳)
service.init(); ----->>> HealthCheckReactor.scheduleCheck(clientBeatCheckTask);
------> GlobalExecutor.scheduleNamingHealth(task, 5000, 5000, TimeUnit.MILLISECONDS)
(定时任务)
然后我们看线程的run方法 就是拿到所有的服务节点 如果超过一定时间 服务端还没有发送心跳的话 我就给设置为false
之后再看 就是怎么把我们的服务实例注册上去的
public void addInstance(String namespaceId, String serviceName, boolean ephemeral, Instance... ips)
throws NacosException {
String key = KeyBuilder.buildInstanceListKey(namespaceId, serviceName, ephemeral);
// 此时关于整体的服务信息 还没有加入节点
Service service = getService(namespaceId, serviceName);
synchronized (service) {
// 基于这个service 找到所有的 instanceList 包括此时新加入的
List<Instance> instanceList = addIpAddresses(service, ephemeral, ips);
Instances instances = new Instances();
instances.setInstanceList(instanceList);
consistencyService.put(key, instances);
}
然后看这个
#####3
这里
public void put(String key, Record value) throws NacosException {
onPut(key, value);
distroProtocol.sync(new DistroKey(key, KeyBuilder.INSTANCE_LIST_KEY_PREFIX), DataOperation.CHANGE,
globalConfig.getTaskDispatchPeriod() / 2);
}
这里的onput 实际上就是吧我们的instence实例节点 存储在了dataStore.put(key, datum);
// datumKey 操作
tasks.offer(Pair.with(datumKey, action)); // 然后吧这些instence实例封装成了Pair对象 放在了
private BlockingQueue<Pair<String, DataOperation>> tasks = new ArrayBlockingQueue<>(1024 * 1024);
阻塞队列中
这其实他的注册逻辑就完事了。我们到底是没有看到怎么insert到他之前构建的双层map中
这个可能就是阻塞队列需要下一步的消费
# 分临时节点的注册和持久节点的注册 一般都是true 为临时节点
private ConsistencyService mapConsistencyService(String key) {
return KeyBuilder.matchEphemeralKey(key) ? ephemeralConsistencyService : persistentConsistencyService;
}
阻塞队列 最终把我们客户端传过来的instence 封装成pare对象放在阻塞队列中 注册逻辑结束了