浅析Spring Cloud Alibaba Nacos 服务注册原理

前言

关于Nacos的文章,可能会分成三篇来分析,本文是第一篇

  1. 第一篇:Nacos 服务注册
  2. 第二篇:Nacos 服务发现
  3. 第三篇:Nacos 配置中心

文章脉络

在这里插入图片描述

Spring Cloud 服务注册原理

1. spring cloud common组件导入spring.factories下的配置类
  1. /META-INF/spring.factories是spring默认的配置类扫描路径(会在之后的spring文章中提及)
  2. 类似的还有spring.handles和spring.schema,用于解析集成框架的自定义标签,如 dubbo 和 mybatis 自定义标签
  3. 这里我们重点关注 AutoServiceRegistrationAutoConfiguration 配置类

2. AutoServiceRegistrationAutoConfiguration 配置类注入 AutoServiceRegistration
@Configuration(proxyBeanMethods = false)
@Import(AutoServiceRegistrationConfiguration.class)
@ConditionalOnProperty(value = "spring.cloud.service-registry.auto-registration.enabled",
		matchIfMissing = true)
public class AutoServiceRegistrationAutoConfiguration {

	@Autowired(required = false)
	private AutoServiceRegistration autoServiceRegistration;
	... ...
}
  1. AutoServiceRegistration 是一个接口,AbstractAutoServiceRegistration 是它的实现类,通过@Autowired自动注入
  2. 可以通过继承 AbstractAutoServiceRegistration 重写抽象方法来实现自定义的注册中心,Nacos 就是通过这种方式
  3. 文末还会贴出大佬实现的Redis服务注册中心的文章链接,下图可以看到AutoServiceRegistration的继承关系

在这里插入图片描述

3. 触发AutoServiceRegistration的容器刷新事件,注册ServiceRegistry
  1. 上一步也谈到了 AbstractAutoServiceRegistration是AutoServiceRegistration的实现类
  2. 实现了 ApplicationListener 接口,会在Web服务器初始化完成后触发 Spring 容器事件,执行 onApplicationEvent()
  3. 通过 @SuppressWarnings(“deprecation”) 调用被丢弃的 bind(),从而执行 start() 中的 registry()
public abstract class AbstractAutoServiceRegistration<R extends Registration>
        implements AutoServiceRegistration, ApplicationContextAware,
        ApplicationListener<WebServerInitializedEvent> {

    private final ServiceRegistry<R> serviceRegistry;
    
    @Override
    //调用 @Deprecated 修饰的方法而不触发编译器警告
    @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();
    }
}
Nacos 服务注册原理

在这里插入图片描述
上一节讲到了 AbstractAutoServiceRegistration 会调用 register() 注册 ServiceReistry

public class NacosServiceRegistry implements ServiceRegistry<Registration> {
    
    @Override
    public void register(Registration registration) {

        if (StringUtils.isEmpty(registration.getServiceId())) {
            log.warn("No service to register for nacos client...");
            return;
        }

        NamingService namingService = namingService();
        String serviceId = registration.getServiceId();
        String group = nacosDiscoveryProperties.getGroup();

        Instance instance = getNacosInstanceFromRegistration(registration);

        try {
        	//注册实例到命名服务
            namingService.registerInstance(serviceId, group, instance);
            log.info("nacos registry, {} {} {}:{} register finished", group, serviceId,
                    instance.getIp(), instance.getPort());
        }
        catch (Exception e) {
            log.error("nacos registry, {} register failed...{},", serviceId,
                    registration.toString(), e);
            rethrowRuntimeException(e);
        }
    }

	... ...
}
	@Override
    public void registerInstance(String serviceName, String groupName, Instance instance) throws NacosException {
        String groupedServiceName = NamingUtils.getGroupedName(serviceName, groupName);
        if (instance.isEphemeral()) {
            BeatInfo beatInfo = beatReactor.buildBeatInfo(groupedServiceName, instance);
            //注册服务的同时发送心跳
            beatReactor.addBeatInfo(groupedServiceName, beatInfo);
        }
        //通过serverProxy来注册服务
        serverProxy.registerService(groupedServiceName, groupName, instance);
    }

在这里插入图片描述

发送心跳
	public void addBeatInfo(String serviceName, BeatInfo beatInfo) {
        NAMING_LOGGER.info("[BEAT] adding beat: {} to beat map.", beatInfo);
        String key = buildKey(serviceName, beatInfo.getIp(), beatInfo.getPort());
        BeatInfo existBeat = null;
        //fix #1733
        if ((existBeat = dom2Beat.remove(key)) != null) {
            existBeat.setStopped(true);
        }
        dom2Beat.put(key, beatInfo);
        executorService.schedule(new BeatTask(beatInfo), beatInfo.getPeriod(), TimeUnit.MILLISECONDS);
        MetricsMonitor.getDom2BeatSizeMonitor().set(dom2Beat.size());
    }
服务注册
	public String callServer(String api, Map<String, String> params, Map<String, String> body, String curServer,
            String method) throws NacosException {
        long start = System.currentTimeMillis();
        long end = 0;
        injectSecurityInfo(params);
        Header header = builderHeader();
        
        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;
            }
            url = NamingHttpClientManager.getInstance().getPrefix() + curServer + api;
        }
        
        try {
        	//底层都是基于HTTP协议完成请求的,所以注册服务就是发送一个HTTP请求
            HttpRestResult<String> restResult = nacosRestTemplate
                    .exchangeForm(url, header, Query.newInstance().initParams(params), body, method, String.class);
            end = System.currentTimeMillis();
            
            MetricsMonitor.getNamingRequestMonitor(method, url, String.valueOf(restResult.getCode()))
                    .observe(end - start);
            
            if (restResult.ok()) {
                return restResult.getData();
            }
            if (HttpStatus.SC_NOT_MODIFIED == restResult.getCode()) {
                return StringUtils.EMPTY;
            }
            throw new NacosException(restResult.getCode(), restResult.getMessage());
        } catch (Exception e) {
            NAMING_LOGGER.error("[NA] failed to request", e);
            throw new NacosException(NacosException.SERVER_ERROR, e);
        }
    }

在这里插入图片描述
服务注册的主要代码如上图所示,可以看到是通过 HttpClient 发送请求,源码太多就不贴出来了,感兴趣的小伙伴可以去 debug 源码加深下印象

参考文章

深入理解SpringCloud之自动注册服务
Spring Boot 之 spring.factories
SpringCloudAlibaba——Nacos实现原理详解

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值