SpringCloud Nacos作为注册中心-源码解析
写在前面
源码的解读比较枯燥,但是能了解其原理。反复解读能理解他本质的思想,可以下载源码跟着我的顺序来解读。
第一次写文章,望各位大神指点其不足,部分源码比较简单只在外面加了注释没有进行详细解读,有兴趣可以跟下去看。
源码下载
naocs客户端源码 https://github.com/spring-cloud-incubator/spring-cloud-alibaba.
naocs服务端源码 https://github.com/alibaba/nacos.
nacos 官方API 文档 https://nacos.io/en-us/docs/open-api.html.
本文分析以下几个部分
1.客户端注册服务
2.服务端注册服务处理
3.客户端服务发现和调用过程
4.服务端返回客户端请求服务
5.服务心跳 定时
服务注册
spring cloud 是怎么样调用到 nacos的注册服务方法?
1.spring boot 启动后调用到 AnnotationConfigApplicationContext 下的 AnnotationConfigApplicationContext(Class<?>… componentClasses) 构造方法,然后调用refresh方法,refresh下的 finishRefresh()方法是我们调用注册服务的一步
/**
* 这里由于他有父类,所以会先调用父类的构造方法:
* 看源码得知初始化了DefaultListableBeanFactory
*
* 然后才调用自己的构造方法:
* 1.创建一个读取注解的Bean定义读取器
* 将bean读取完后,会调用DefaultListableBeanFactory注册这个bean
* 2.创建BeanDefinition扫描器
* 可以用来扫描包或者类,继而转换为bd
*/
public AnnotationConfigApplicationContext(Class<?>... componentClasses) {
this();
register(componentClasses);
refresh();
}
public void refresh() throws BeansException, IllegalStateException {
synchronized (this.startupShutdownMonitor) {
/**
* 准备工作:
* 设置启动时间、是否激活标识位
* 初始化属性源(property source)配置
*/
prepareRefresh();
/**
* 告诉子类刷新内部bean工厂
* 拿到DefaultListableBeanFactory,供后面方法调用
*/
ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory();
/**
* 准备bean工厂
*/
prepareBeanFactory(beanFactory);
try {
/**
* 这个方法在当前版本没有实现
* 可能在spring后面的版本会去扩展
*/
postProcessBeanFactory(beanFactory);
/**
* 在上下文中调用注册为bean的工厂处理器
*
* 添加BeanPostProcessor
* 如果发现loadTimeWeaver的Bean
*/
invokeBeanFactoryPostProcessors(beanFactory);
/**
* 注册BeanPostProcessor
* 自定义以及spring内部的
*/
registerBeanPostProcessors(beanFactory);
/**
* 国际化支持,不关心
*/
initMessageSource();
/**
* 初始化事件监听多路广播器
*/
initApplicationEventMulticaster();
/**
* 这个方法在当前版本没有实现
* 可能在spring后面的版本会去扩展
*/
onRefresh();
/**
* 注册监听器
*/
registerListeners();
/**
* 实例化所有bean
*/
finishBeanFactoryInitialization(beanFactory);
/**
* 发布事件
*/
finishRefresh();
}
......
}
}
2.finishRefresh推送ServletWebServerInitializedEven事件,AbstractAutoServiceRegistration触发监听并执行服务注册
/*
* serverler执行最后的容易刷新,并推送 ServletWebServerInitializedEvent监听事件
*/
protected void finishRefresh() {
super.finishRefresh();
WebServer webServer = startWebServer();
if (webServer != null) {
publishEvent(new ServletWebServerInitializedEvent(webServer, this));
}
}
/*
* 监听器监听到 WebServerInitializedEvent 执行bind方法
*/
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();
}
/*
* 1.推送实例注册前事件
* 2.推送注册前瑟吉欧款
* 3.执行注册后事件
* 4.重置启动状态
*/
public void start() {
/*注册的开关是否打开,如果只订阅是不需要打开的,这里取的是NacosDiscoveryProperties的参数*/
if (!isEnabled()) {
if (logger.isDebugEnabled()) {
logger.debug("Discovery Lifecycle disabled. Not starting");
}
return;
}
/*容器未启动,即第一次启动的时候*/
if (!this.running.get()) {
this.context.publishEvent(
new InstancePreRegisteredEvent(this, getRegistration()));
/*注册服务,这里是调用NacosAutoServiceRegistration*/
register();
/*是否需要将管理服务的服务注册到注册中心,nacos的话是null,这里可以忽略*/
if (shouldRegisterManagement()) {
registerManagement();
}
this.context.publishEvent(
new InstanceRegisteredEvent<>(this, getConfiguration()));
/*启动状态更新*/
this.running.compareAndSet(false, true);
}
}
3.调用到NacosAutoServiceRegistration的register() 方法
客户端是怎么将服务注册到Nacos上的
承接上面的调用链>>>>>>>
1.NacosAutoServiceRegistration执行register()方法,然后调用父类 AbstractAutoServiceRegistration register()方法
protected void register() {
/*如果未开启注册到nacos,直接返回*/
if (!this.registration.getNacosDiscoveryProperties().isRegisterEnabled()) {
log.debug("Registration disabled.");
return;
}
/*参数配置的端口小于0),获取服务端口,所以不需要去主动配置这个参数,可以根据服务的端口字段获取*/
if (this.registration.getPort() < 0) {
this.registration.setPort(getPort().get());
}
/*调用父类注册方法*/
super.register();
}
protected void register() {
this.serviceRegistry.register(getRegistration());
}
2.NacosNamingService执行registerInstance方法
public void registerInstance(String serviceName, String groupName, Instance instance) throws NacosException {
/*临时实例*/
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);
}
3.NamingProxy执行registerService方法,调用reqAPI进行http请求"/nacos/v1/ns/instance"
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);
}
public String reqAPI(String api, Map<String, String> params, String body, List<String> servers, String method) throws NacosException {
//获取命名空间id
params.put(CommonParams.NAMESPACE_ID, getNamespaceId());
//注册服务不能为空
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()) {
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);
······
public String callServer(String api, Map<String, String> params, String body, String curServer, String method)