不要问我阅读spring源码有什么用,问就是没有用,只是让我自己使用spring的过程中自信点!
相关文章
说明
1. 这个文只是说下注册的流程,nacos-config 在这里是不说的,后面会单独说
大致流程图
上面的图,大致把整个流程分为了三部分
1. spring整合nacos
2. nacos客户端的处理
3. nacos服务端的处理
spring整合nacos
1. 一般spring整合第三方组件,最少会有两个包,一个为 spring-boot/cloud-XXX-starter , 一个为服务核心包,就是一个启动包,一个核心包
2. spring整合nacos服务与发现的 启动包为 spring-cloud-Alibaba-nacoc-discovery
3. 这个包的作用就会注册当前jar的spring.factories中的类到springioc容器,这个jar的注册原理在这不多说了,后面会写一篇<手写spring-boot-XXX-starter及其原理>
//一起要从NacosServiceRegistryAutoConfiguration说起
//NacosServiceRegistryAutoConfiguration是在 spring-cloud-Alibaba-nacoc-discovery 下面的spring.factories中注册的
//com.alibaba.cloud.nacos.registry.NacosServiceRegistryAutoConfiguration.nacosServiceRegistry(NacosDiscoveryProperties)
//创建 nacosServiceRegistry
@Bean
public NacosServiceRegistry nacosServiceRegistry(
NacosDiscoveryProperties nacosDiscoveryProperties) {
return new NacosServiceRegistry(nacosDiscoveryProperties);
}
nacosServiceRegistry包含的属性
NacosDiscoveryProperties
NamingService
//这个方法看的出只是给nacosServiceRegistry的两个属性分别赋值
//NacosDiscoveryProperties这个是配置参数类,不看了,应该是@Bean来的
//主要看下NamingService 这个是是nacos组件的类
public NacosServiceRegistry(NacosDiscoveryProperties nacosDiscoveryProperties) {
this.nacosDiscoveryProperties = nacosDiscoveryProperties;
this.namingService = nacosDiscoveryProperties.namingServiceInstance();
}
public NamingService namingServiceInstance() {
//假如有直接返回
if (null != namingService) {
return namingService;
}
try {
//没有就去创建
namingService = NacosFactory.createNamingService(getNacosProperties());
}
catch (Exception e) {
log.error("create naming service error!properties={},e=,", this, e);
return null;
}
return namingService;
}
//getNacosProperties这个方法,里面就是获取一些服务信息
//继续
public static NamingService createNamingService(Properties properties) throws NacosException {
return NamingFactory.createNamingService(properties);
}
//
public static NamingService createNamingService(Properties properties) throws NacosException {
try {
//会发现是通过反射创建的
//获取clss 对象
Class<?> driverImplClass = Class.forName("com.alibaba.nacos.client.naming.NacosNamingService");
Constructor constructor = driverImplClass.getConstructor(Properties.class);
//调用有参构造方法
NamingService vendorImpl = (NamingService)constructor.newInstance(properties);
return vendorImpl;
} catch (Throwable e) {
throw new NacosException(NacosException.CLIENT_INVALID_PARAM, e);
}
}
//com.alibaba.nacos.client.naming.NacosNamingService.NacosNamingService(Properties)
public NacosNamingService(Properties properties) {
init(properties);
}
//初始化
private void init(Properties properties) {
//命名空间 public
//下面的几行代码从名字看是做一些初始化的工作,具体的我也没咋看过
namespace = InitUtils.initNamespaceForNaming(properties);
initServerAddr(properties);
InitUtils.initWebRootContext();
initCacheDir();
initLogName(properties);
eventDispatcher = new EventDispatcher();
//这里里面有两个定时任务 好像是针对nacos自身服务的,没注意看
serverProxy = new NamingProxy(namespace, endpoint, serverList, properties);
//从名字大致能猜的出来,心跳 里面有一个定时线程池,就是处理 发送心跳的任务的
//initClientBeatThreadCount方法 是根据机器选择线程数的
beatReactor = new BeatReactor(serverProxy, initClientBeatThreadCount(properties));
//这个是处理 服务 服务发生改变的任务
hostReactor = new HostReactor(eventDispatcher, serverProxy, cacheDir, isLoadCacheAtStart(properties),
initPollingThreadCount(properties));
}
nacos客户端
//nacos客户端启动的源头是spring的监听器
//NacosAutoServiceRegistration是在NacosServiceRegistryAutoConfiguration中@bean创建
@Bean
@ConditionalOnBean(AutoServiceRegistrationProperties.class)
public NacosAutoServiceRegistration nacosAutoServiceRegistration(
NacosServiceRegistry registry,
AutoServiceRegistrationProperties autoServiceRegistrationProperties,
NacosRegistration registration) {
return new NacosAutoServiceRegistration(registry,
autoServiceRegistrationProperties, registration);
}
NacosAutoServiceRegistration(nacos提供)的父类AbstractAutoServiceRegistration(spring-cloud提供)实现了
ApplicationListener<WebServerInitializedEvent> 监听的是Web初始化事件
//监听器的原理这里不多说了,后续有时间会单独写一篇介绍spring监听器原理的文章
//这里只需要知道当监听的事件触发会调用实现类的onApplicationEvent方法即可
@Override
@SuppressWarnings("deprecation")
public void onApplicationEvent(WebServerInitializedEvent event) {
//监听事件的执行入口
bind(event);
}
@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();
}
public void start() {
if (!isEnabled()) {
if (logger.isDebugEnabled()) {
logger.debug("Discovery Lifecycle disabled. Not starting");
}
return;
}
// only initialize if nonSecurePort is greater than 0 and it isn't already running
// because of containerPortInitializer below
if (!this.running.get()) {
this.context.publishEvent(
new InstancePreRegisteredEvent(this, getRegistration()));
//注册开始
//这些代码还都是在spring-cloud中执行的,都是spring提供的一些公用接口
register();
if (shouldRegisterManagement()) {
registerManagement();
}
this.context.publishEvent(
new InstanceRegisteredEvent<>(this, getConfiguration()));
this.running.compareAndSet(false, true);
}
}
@Override
//当前放在所在位置 NacosAutoServiceRegistration 类
protected void register() {
if (!this.registration.getNacosDiscoveryProperties().isRegisterEnabled()) {
log.debug("Registration disabled.");
return;
}
if (this.registration.getPort() < 0) {
this.registration.setPort(getPort().get());
}
//调用父类的 register 方法
super.register();
}
//当前放在所在位置 AbstractAutoServiceRegistration
protected void register() {
//调用 serviceRegistry 属性的方法
//上述代码总体来干的事:
//真正的注册代码是在AbstractAutoServiceRegistration的serviceRegistry属性的register开始的
//nacos的NacosServiceRegistry类实现了ServiceRegistry接口
//NacosAutoServiceRegistration继承了AbstractAutoServiceRegistration
//所以说当前实现类NacosAutoServiceRegistration有一个属性serviceRegistry,这个属性的实现类是nacos提供的NacosServiceRegistry
//总体来说就是spring提供了一套公用代码和接口,第三方组件实现他们就可以了
this.serviceRegistry.register(getRegistration());
}
//nacos代码开始
//当前类就是上面讲到的spring整合nacos 的过程中 @bean 注册的
@Override
public void register(Registration registration) {
//获取serviceId 就是你的服务名
//所以说假如的你微服务没有设置 spring.application.name 那么你的服务是不能注册到nacos
if (StringUtils.isEmpty(registration.getServiceId())) {
log.warn("No service to register for nacos client...");
return;
}
String serviceId = registration.getServiceId();
String group = nacosDiscoveryProperties.getGroup();
//封装下实例信息,例如 ip ,端口 之类的
Instance instance = getNacosInstanceFromRegistration(registration);
try {
//调用 namingService 的注册方法
//上面已经讲过 namingService 是通过反射创建的,里面搞了些初始化的信息
//例如 发送心跳的线程池就是在init创建的 并且保存在 其属性 BeatReactor...中
namingService.registerInstance(serviceId, group, instance);
log.info("nacos registry, {} {} {}:{} register finished", group, serviceId,
instance.getIp(), instance.getPort());
}
}
@Override
public void registerInstance(String serviceName, String groupName, Instance instance) throws NacosException {
//是否临时实例,一般情况都是true
if (instance.isEphemeral()) {
//包装了 发送心跳需要的信息
BeatInfo beatInfo = new BeatInfo();
beatInfo.setServiceName(NamingUtils.getGroupedName(serviceName, groupName));
beatInfo.setIp(instance.getIp());
beatInfo.setPort(instance.getPort());
beatInfo.setCluster(instance.getClusterName());
beatInfo.setWeight(instance.getWeight());
beatInfo.setMetadata(instance.getMetadata());
beatInfo.setScheduled(false);
beatInfo.setPeriod(instance.getInstanceHeartBeatInterval());
//发送心跳的任务创建 , 我们先看下这个方法
beatReactor.addBeatInfo(NamingUtils.getGroupedName(serviceName, groupName), beatInfo);
}
//主线 注册代码
serverProxy.registerService(NamingUtils.getGroupedName(serviceName, groupName), groupName, instance);
}
发送心跳任务
public void addBeatInfo(String serviceName, BeatInfo beatInfo) {
NAMING_LOGGER.info("[BEAT] adding beat: {} to beat map.", beatInfo);
//获取key 假如服务名为 service-ribbon-test01
//则key为 DEFAULT_GROUP@@service-ribbon-test01#10.40.40.109#7001
String key = buildKey(serviceName, beatInfo.getIp(), beatInfo.getPort());
BeatInfo existBeat = null;
//fix #1733
if ((existBeat = dom2Beat.remove(key)) != null) {
existBeat.setStopped(true);
}
//放入 dom2Beat 说实话不知道干啥,也没有仔细的看过
dom2Beat.put(key, beatInfo);
//创建了一个任务 BeatTask , BeatTask是实现了Runnable的
//默认是五秒之后执行,这个方法只执行一次,至于为啥能一直发送心跳,是在 BeatTask中处理的
executorService.schedule(new BeatTask(beatInfo), beatInfo.getPeriod(), TimeUnit.MILLISECONDS);
MetricsMonitor.getDom2BeatSizeMonitor().set(dom2Beat.size());
}
//BeatTask
class BeatTask implements Runnable {
BeatInfo beatInfo;
public BeatTask(BeatInfo beatInfo) {
this.beatInfo = beatInfo;
}
@Override
//只要看下这个方法
public void run() {
if (beatInfo.isStopped()) {
return;
}
//记录下间隔时间
long nextTime = beatInfo.getPeriod();
try {
//发送心跳的方法 , 里面就是封装的HTTP请求,不具体看了
JSONObject result = serverProxy.sendBeat(beatInfo, BeatReactor.this.lightBeatEnabled);
long interval = result.getIntValue("clientBeatInterval");
boolean lightBeatEnabled = false;
if (result.containsKey(CommonParams.LIGHT_BEAT_ENABLED)) {
lightBeatEnabled = result.getBooleanValue(CommonParams.LIGHT_BEAT_ENABLED);
}
BeatReactor.this.lightBeatEnabled = lightBeatEnabled;
if (interval > 0) {
nextTime = interval;
}
int code = NamingResponseCode.OK;
if (result.containsKey(CommonParams.CODE)) {
code = result.getIntValue(CommonParams.CODE);
}
//这个里面是再次 注册
if (code == NamingResponseCode.RESOURCE_NOT_FOUND) {
Instance instance = new Instance();
instance.setPort(beatInfo.getPort());
instance.setIp(beatInfo.getIp());
instance.setWeight(beatInfo.getWeight());
instance.setMetadata(beatInfo.getMetadata());
instance.setClusterName(beatInfo.getCluster());
instance.setServiceName(beatInfo.getServiceName());
instance.setInstanceId(instance.getInstanceId());
instance.setEphemeral(true);
try {
serverProxy.registerService(beatInfo.getServiceName(),
NamingUtils.getGroupName(beatInfo.getServiceName()), instance);
} catch (Exception ignore) {
}
}
} catch (NacosException ne) {
NAMING_LOGGER.error("[CLIENT-BEAT] failed to send beat: {}, code: {}, msg: {}",
JSON.toJSONString(beatInfo), ne.getErrCode(), ne.getErrMsg());
}
//发送心跳 成功了之后 等一个固定时间再次创建 beattask 任务
executorService.schedule(new BeatTask(beatInfo), nextTime, TimeUnit.MILLISECONDS);
}
}
注册
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);
}
//五个参数
//api 请求地址 /nacos/v1/ns/instance
//params 请求参数
//body
//servers nacos 的地址,之所以是集合,是因为nacos 的集群模式
//method 请求类型 post
public String reqAPI(String api, Map<String, String> params, String body, List<String> servers, String method) throws NacosException {
params.put(CommonParams.NAMESPACE_ID, getNamespaceId());
//判断下 nacos服务端 列表 是否存在
if (CollectionUtils.isEmpty(servers) && StringUtils.isEmpty(nacosDomain)) {
throw new NacosException(NacosException.INVALID_PARAM, "no server available");
}
NacosException exception = new NacosException();
if (servers != null && !servers.isEmpty()) {
//获取 要注册的 nacos服务端的 地址
//规则是随机的
Random random = new Random(System.currentTimeMillis());
int index = random.nextInt(servers.size());
for (int i = 0; i < servers.size(); i++) {
String server = servers.get(index);
try {
//这里
return callServer(api, params, body, server, method);
} catch (NacosException e) {
exception = e;
if (NAMING_LOGGER.isDebugEnabled()) {
NAMING_LOGGER.debug("request {} failed.", server, e);
}
}
index = (index + 1) % servers.size();
}
}
............................................................................
}
public String callServer(String api, Map<String, String> params, String body, String curServer, String method)
throws NacosException {
//记录下时间
long start = System.currentTimeMillis();
long end = 0;
injectSecurityInfo(params);
List<String> headers = builderHeaders();
String url;
if (curServer.startsWith(UtilAndComs.HTTPS) || curServer.startsWith(UtilAndComs.HTTP)) {
//请求地址
url = curServer + api;
} else {
if (!curServer.contains(UtilAndComs.SERVER_ADDR_IP_SPLITER)) {
curServer = curServer + UtilAndComs.SERVER_ADDR_IP_SPLITER + serverPort;
}
//拼接请求地址
//没有http或者https 前缀的
url = HttpClient.getPrefix() + curServer + api;
}
//发起http请求 注册结束
HttpClient.HttpResult result = HttpClient.request(url, headers, params, body, UtilAndComs.ENCODING, method);
end = System.currentTimeMillis();
MetricsMonitor.getNamingRequestMonitor(method, url, String.valueOf(result.code))
.observe(end - start);
if (HttpURLConnection.HTTP_OK == result.code) {
return result.content;
}
if (HttpURLConnection.HTTP_NOT_MODIFIED == result.code) {
return StringUtils.EMPTY;
}
throw new NacosException(result.code, result.content);
}
nacos服务端
//上面将nacos客户端的时候我们已经知道了注册的地址为 /nacos/v1/ns/instance
//代码模块为 nacos-naming 代码如下:
@CanDistro
@PostMapping
@Secured(parser = NamingResourceParser.class, action = ActionTypes.WRITE)
public String register(HttpServletRequest request) throws Exception {
//获取服务名称
final String serviceName = WebUtils.required(request, CommonParams.SERVICE_NAME);
final String namespaceId = WebUtils
.optional(request, CommonParams.NAMESPACE_ID, Constants.DEFAULT_NAMESPACE_ID);
//解析请求参数
final Instance instance = parseInstance(request);
//主线逻辑 注册
serviceManager.registerInstance(namespaceId, serviceName, instance);
return "ok";
}
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);
}
创建服务容器
public void createEmptyService(String namespaceId, String serviceName, boolean local) throws NacosException {
createServiceIfAbsent(namespaceId, serviceName, local, null);
}
public void createServiceIfAbsent(String namespaceId, String serviceName, boolean local, Cluster cluster)
throws NacosException {
// 这里,主要方法
Service service = getService(namespaceId, serviceName);
if (service == null) {
//key 为 public 一般情况下,我们的使用的时候 key 都是 public
//如果为空 就去构建这样一个容器
//容器结构在文章开始的那张图里面
Loggers.SRV_LOG.info("creating empty service {}:{}", namespaceId, serviceName);
service = new Service();
service.setName(serviceName);
service.setNamespaceId(namespaceId);
service.setGroupName(NamingUtils.getGroupName(serviceName));
// now validate the service. if failed, exception will be thrown
//这个是设置下最后修改时间,一般情况下 就是心跳的时间
service.setLastModifiedMillis(System.currentTimeMillis());
service.recalculateChecksum();
if (cluster != null) {
cluster.setService(service);
service.getClusterMap().put(cluster.getName(), cluster);
}
service.validate();
putServiceAndInit(service);
if (!local) {
addOrReplaceService(service);
}
}
}
//把创建service 放入 map 容器中
private void putServiceAndInit(Service service) throws NacosException {
//这个放入map中 key 为 public namespaceId
putService(service);
//初始化 这个其实还挺重要的
service.init();
//下面两行代码的意思是把你刚刚创建的容器 分别放入ephemeralConsistencyService,persistentConsistencyService 中的 listeners属性中
//listeners 属性结构为 Map<String, ConcurrentLinkedQueue<RecordListener>> listeners
//key 为:
//com.alibaba.nacos.naming.iplist.ephemeral+NamespaceId##gorup@@serviceName
//com.alibaba.nacos.naming.iplist.+NamespaceId##gorup@@serviceName
//他们的实现类分别为 DistroConsistencyServiceImpl RaftConsistencyServiceImpl
consistencyService
.listen(KeyBuilder.buildInstanceListKey(service.getNamespaceId(), service.getName(), true), service);
consistencyService
.listen(KeyBuilder.buildInstanceListKey(service.getNamespaceId(), service.getName(), false), service);
Loggers.SRV_LOG.info("[NEW-SERVICE] {}", service.toJson());
}
public void init() {
//创建 检查实例 是否健康的 任务
HealthCheckReactor.scheduleCheck(clientBeatCheckTask);
for (Map.Entry<String, Cluster> entry : clusterMap.entrySet()) {
entry.getValue().setService(this);
entry.getValue().init();
}
}
//
public Service getService(String namespaceId, String serviceName) {
// serviceMap 的数据结构 Map<String, Map<String, Service>> serviceMap
// namespaceId 为 public
//第一个服务注册,肯定为 null
if (serviceMap.get(namespaceId) == null) {
return null;
}
return chooseServiceMap(namespaceId).get(serviceName);
}
检查实例是否健康
//这里是接上面的 init 方法
public void init() {
//创建检查实例是否健康的任务
HealthCheckReactor.scheduleCheck(clientBeatCheckTask);
for (Map.Entry<String, Cluster> entry : clusterMap.entrySet()) {
entry.getValue().setService(this);
entry.getValue().init();
}
}
public static void scheduleCheck(ClientBeatCheckTask task) {
///key value
futureMap.putIfAbsent(task.taskKey(), GlobalExecutor.scheduleNamingHealth(task, 5000, 5000, TimeUnit.MILLISECONDS));
}
//我们先看下这个任务(ClientBeatCheckTask 实现了 Runnable)的java doc
//从名字能看出 是 客户端的心跳检查,,,还有个心跳检查任务 HealthCheckTask
//Check and update statues of ephemeral instances, remove them if they have been expired.
//直接在有道 翻译 就能大致了解它的作用:
//检查和更新临时实例的状态,如果它们已经过期,则删除它们
//看下它的 run 方法
@Override
public void run() {
try {
if (!getDistroMapper().responsible(service.getName())) {
return;
}
if (!getSwitchDomain().isHealthCheckEnabled()) {
return;
}
//这个地方是获取所有的实例
//为啥要加一个boolean参数呢,是因为,我们的实例 分两种 临时的,非临时的,是放在不同的容器总的
//非别是 ephemeralInstances persistentInstances
List<Instance> instances = service.allIPs(true);
// first set health status of instances:
//循环获取到的实例 , 这个for 中的操作是 是标记为不可用
for (Instance instance : instances) {
//判断当前时间减去实例最后更新的时间 是否大于默认的 超时时间 默认时间为 15 秒
if (System.currentTimeMillis() - instance.getLastBeat() > instance.getInstanceHeartBeatTimeOut()) {
if (!instance.isMarked()) {
//假如还是健康
if (instance.isHealthy()) {
//设置为不健康的
instance.setHealthy(false);
//打印一波日志
Loggers.EVT_LOG
.info("{POS} {IP-DISABLED} valid: {}:{}@{}@{}, region: {}, msg: client timeout after {}, last beat: {}",
instance.getIp(), instance.getPort(), instance.getClusterName(),
service.getName(), UtilsAndCommons.LOCALHOST_SITE,
instance.getInstanceHeartBeatTimeOut(), instance.getLastBeat());
getPushService().serviceChanged(service);
//发布事件
ApplicationUtils.publishEvent(new InstanceHeartbeatTimeoutEvent(this, instance));
}
}
}
}
if (!getGlobalConfig().isExpireInstance()) {
return;
}
// then remove obsolete instances:
//这个for 中的操作是 是删除服务
for (Instance instance : instances) {
if (instance.isMarked()) {
continue;
}
//判断当前时间减去实例最后更新的时间 是否大于默认的 超时时间 默认时间为 30 秒
if (System.currentTimeMillis() - instance.getLastBeat() > instance.getIpDeleteTimeout()) {
// delete instance
Loggers.SRV_LOG.info("[AUTO-DELETE-IP] service: {}, ip: {}", service.getName(),
JacksonUtils.toJson(instance));
//删除实例, 里面就是封装的 http 请求,去通知nacos 集群中别的nacos服务
deleteIp(instance);
}
}
} catch (Exception e) {
Loggers.SRV_LOG.warn("Exception while processing client beat time out.", e);
}
}
注册主线
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);
}
public void addInstance(String namespaceId, String serviceName, boolean ephemeral, Instance... ips)
throws NacosException {
String key = KeyBuilder.buildInstanceListKey(namespaceId, serviceName, ephemeral);
//再去获取一次 service
Service service = getService(namespaceId, serviceName);
//加锁
synchronized (service) {
//这个方法很重要,先看下
//这个方法就是获取当前 服务名的 所有实例
List<Instance> instanceList = addIpAddresses(service, ephemeral, ips);
Instances instances = new Instances();
instances.setInstanceList(instanceList);
consistencyService.put(key, instances);
}
}
拷贝数据
/**
*这个方法得作用从原doc来解释就是 比较并且获取最新的实例集合
*
* 我自己得理解就是:
* 先拿到这个service中所有得实例(这首先你要明白储存实例容器的结构是怎么样的)
* 在加上这次请求的实例(也有可能是删除,请求参数action 就是标识为删除或者添加,就是注册服务或者删除服务)
* 复制这份数据返回
*/
public List<Instance> updateIpAddresses(Service service, String action, boolean ephemeral, Instance... ips)
throws NacosException {
//第一次注册为空
//com.alibaba.nacos.naming.iplist.ephemeral+NamespaceId##gorup@@serviceName
//com.alibaba.nacos.naming.iplist.+NamespaceId##gorup@@serviceName
//这个数据在什么地方放入consistencyService后面会将的
Datum datum = consistencyService
.get(KeyBuilder.buildInstanceListKey(service.getNamespaceId(), service.getName(), ephemeral));
//第一次注册为空
List<Instance> currentIPs = service.allIPs(ephemeral);
//创建一个map 长度为currentIPs得长度,主要是为了节约内存
Map<String, Instance> currentInstances = new HashMap<>(currentIPs.size());
//这个set使用来存放实例id得
Set<String> currentInstanceIds = Sets.newHashSet();
//假如存在实例,循环
for (Instance instance : currentIPs) {
//拼接key ip:port value 为实例对象
currentInstances.put(instance.toIpAddr(), instance);
//放入 currentInstanceIds
currentInstanceIds.add(instance.getInstanceId());
}
//这是基本就是我们需要返回数据的集合了
Map<String, Instance> instanceMap;
//判断这个datum中是否存在数据,(其实我不明白为什么不从serviceMap中取值)
if (datum != null) {
//假如有数据,搞一份到instanceMap
instanceMap = setValid(((Instances) datum.value).getInstanceList(), currentInstances);
} else {
//假如不存在就创建一个空的map
instanceMap = new HashMap<>(ips.length);
}
//循环这次请求的 Instance
for (Instance instance : ips) {
//假如service.ClusterMap中不存在这个instance.getClusterName()key(这个service要不就是注册的时候创建的,要不就是getServer获取的)
//ClusterMap 是在 service 是实例化的时候创建的
if (!service.getClusterMap().containsKey(instance.getClusterName())) {
//假如不存在,创建Cluster , key 为集群名称
Cluster cluster = new Cluster(instance.getClusterName(), service);
//这个初始化里面是一个任务,具体干什么的我还不知道
cluster.init();
//把cluster放入service.ClusterMap
service.getClusterMap().put(instance.getClusterName(), cluster);
Loggers.SRV_LOG
.warn("cluster: {} not found, ip: {}, will create new cluster with default configuration.",
instance.getClusterName(), instance.toJson());
}
//这个地方判断下是不是删除实例
if (UtilsAndCommons.UPDATE_INSTANCE_ACTION_REMOVE.equals(action)) {
//删除实例 说明以前存在,说明instanceMap中存在,删除掉
instanceMap.remove(instance.getDatumKey());
} else {
//添加到instanceMap
instance.setInstanceId(instance.generateInstanceId(currentInstanceIds));
instanceMap.put(instance.getDatumKey(), instance);
}
}
//假如这个时候是注册的,并且到现在还是为空的,直接报错了
if (instanceMap.size() <= 0 && UtilsAndCommons.UPDATE_INSTANCE_ACTION_ADD.equals(action)) {
throw new IllegalArgumentException(
"ip list can not be empty, service: " + service.getName() + ", ip list: " + JacksonUtils
.toJson(instanceMap.values()));
}
//返回 实例 集合
return new ArrayList<>(instanceMap.values());
}
注册主线
public void addInstance(String namespaceId, String serviceName, boolean ephemeral, Instance... ips)
throws NacosException {
String key = KeyBuilder.buildInstanceListKey(namespaceId, serviceName, ephemeral);
//再去获取一次 service
Service service = getService(namespaceId, serviceName);
//加锁
synchronized (service) {
//这个方法很重要,先看下
//这个方法就是获取当前 服务名的 所有实例
List<Instance> instanceList = addIpAddresses(service, ephemeral, ips);
//这两行代码就是复制下获取 实例集合 。 我没看明白为啥要复制
Instances instances = new Instances();
instances.setInstanceList(instanceList);
//这里 主线
consistencyService.put(key, instances);
}
}
@Override
public void put(String key, Record value) throws NacosException {
//mapConsistencyService是根据 是否 是临时实例 获取不同的类型
//我们这里看 为临时实例 DistroConsistencyServiceImpl
mapConsistencyService(key).put(key, value);
}
//这个方法很重要
public void put(String key, Record value) throws NacosException {
//刷新数据到内存
onPut(key, value);
//同步注册数据到nacos集群中的别的nacos服务
taskDispatcher.addTask(key);
}
刷新注册数据到内存
public void onPut(String key, Record value) {
//这个value 是 实例集合
if (KeyBuilder.matchEphemeralInstanceListKey(key)) {
Datum<Instances> datum = new Datum<>();
datum.value = (Instances) value;
datum.key = key;
datum.timestamp.incrementAndGet();
//把实例集合组装放入 dataStore
dataStore.put(key, datum);
}
if (!listeners.containsKey(key)) {
return;
}
//notifier是个实现了 runbable 的任务
//这个方法的作用是把当前时间放入notifier的一个队列属性中
//然后执行这个任务,从队列中依次拿事件处理
notifier.addTask(key, ApplyAction.CHANGE);
}
public void addTask(String datumKey, ApplyAction action) {
//如果这个key存在,直接结束,因为下面处理任务的时候,会把这个key删除
if (services.containsKey(datumKey) && action == ApplyAction.CHANGE) {
return;
}
if (action == ApplyAction.CHANGE) {
//把这次事件的名称存下
services.put(datumKey, StringUtils.EMPTY);
}
//放入队列中
tasks.offer(Pair.with(datumKey, action));
}
//主要看下 notifier 任务的 run 方法,干了什么
public void run() {
Loggers.DISTRO.info("distro notifier started");
//这里有一个死循环
for (; ; ) {
try {
//一直从这个队列中拿数据
//这个数据就是我们 上面说到的 放进去的事件
Pair<String, ApplyAction> pair = tasks.take();
//处理
handle(pair);
} catch (Throwable e) {
Loggers.DISTRO.error("[NACOS-DISTRO] Error while handling notifying task", e);
}
}
}
private void handle(Pair<String, ApplyAction> pair) {
try {
String datumKey = pair.getValue0();
ApplyAction action = pair.getValue1();
//删除 services 中的 key
services.remove(datumKey);
int count = 0;
if (!listeners.containsKey(datumKey)) {
return;
}
//这个 listeners 的数据放入的过程,上面有讲
for (RecordListener listener : listeners.get(datumKey)) {
count++;
try {
//假如是添加
if (action == ApplyAction.CHANGE) {
//注册的处理
listener.onChange(datumKey, dataStore.get(datumKey).value);
continue;
}
//假如是删除
if (action == ApplyAction.DELETE) {
listener.onDelete(datumKey);
continue;
}
} catch (Throwable e) {
Loggers.DISTRO.error("[NACOS-DISTRO] error while notifying listener of key: {}", datumKey, e);
}
}
if (Loggers.DISTRO.isDebugEnabled()) {
Loggers.DISTRO
.debug("[NACOS-DISTRO] datum change notified, key: {}, listener count: {}, action: {}",
datumKey, count, action.name());
}
} catch (Throwable e) {
Loggers.DISTRO.error("[NACOS-DISTRO] Error while handling notifying task", e);
}
}
@Override
public void onChange(String key, Instances value) throws Exception {
Loggers.SRV_LOG.info("[NACOS-RAFT] datum is changed, key: {}, value: {}", key, value);
for (Instance instance : value.getInstanceList()) {
if (instance == null) {
// Reject this abnormal instance list:
throw new RuntimeException("got null instance " + key);
}
if (instance.getWeight() > 10000.0D) {
instance.setWeight(10000.0D);
}
if (instance.getWeight() < 0.01D && instance.getWeight() > 0.0D) {
instance.setWeight(0.01D);
}
}
//这里
updateIPs(value.getInstanceList(), KeyBuilder.matchEphemeralInstanceListKey(key));
recalculateChecksum();
}
public void updateIPs(Collection<Instance> instances, boolean ephemeral) {
Map<String, List<Instance>> ipMap = new HashMap<>(clusterMap.size());
for (String clusterName : clusterMap.keySet()) {
ipMap.put(clusterName, new ArrayList<>());
}
for (Instance instance : instances) {
try {
if (instance == null) {
Loggers.SRV_LOG.error("[NACOS-DOM] received malformed ip: null");
continue;
}
if (StringUtils.isEmpty(instance.getClusterName())) {
instance.setClusterName(UtilsAndCommons.DEFAULT_CLUSTER_NAME);
}
if (!clusterMap.containsKey(instance.getClusterName())) {
Loggers.SRV_LOG
.warn("cluster: {} not found, ip: {}, will create new cluster with default configuration.",
instance.getClusterName(), instance.toJson());
Cluster cluster = new Cluster(instance.getClusterName(), this);
cluster.init();
getClusterMap().put(instance.getClusterName(), cluster);
}
List<Instance> clusterIPs = ipMap.get(instance.getClusterName());
if (clusterIPs == null) {
clusterIPs = new LinkedList<>();
ipMap.put(instance.getClusterName(), clusterIPs);
}
clusterIPs.add(instance);
} catch (Exception e) {
Loggers.SRV_LOG.error("[NACOS-DOM] failed to process ip: " + instance, e);
}
}
for (Map.Entry<String, List<Instance>> entry : ipMap.entrySet()) {
//make every ip mine
//这个地方 把新的实例 放入 clusterMap 中
//updateIps 这个方法中还有些代码 不看了
List<Instance> entryIPs = entry.getValue();
clusterMap.get(entry.getKey()).updateIps(entryIPs, ephemeral);
}
setLastModifiedMillis(System.currentTimeMillis());
//发布
getPushService().serviceChanged(this);
StringBuilder stringBuilder = new StringBuilder();
for (Instance instance : allIPs()) {
stringBuilder.append(instance.toIpAddr()).append("_").append(instance.isHealthy()).append(",");
}
Loggers.EVT_LOG.info("[IP-UPDATED] namespace: {}, service: {}, ips: {}", getNamespaceId(), getName(),
stringBuilder.toString());
}
同步注册数据到nacos集群中的别的nacos服务
//com.alibaba.nacos.naming.consistency.ephemeral.distro.DistroConsistencyServiceImpl.put(String, Record)
public void put(String key, Record value) throws NacosException {
onPut(key, value);
//同步到nacos集群中别的服务上
//具体代码不看了
taskDispatcher.addTask(key);
}