SpringCloud组件 & 源码剖析:Eureka服务注册方式流程全面分析

SpringCloud组件:Eureka服务注册是采用主机名还是IP地址?文章中我们讲到了服务注册的几种注册方式,那么这几种注册方式的源码是怎么实现的呢?我们带着这一个疑问来阅读本章内容能够让你更深入了解这块的知识点!!!

本章目标

分析每一种服务注册方式源码执行流程。

构建项目

本章以分析源码为主,所以不去新创建项目来讲解相关内容,我们使用SpringCloud组件:Eureka服务注册是采用主机名还是IP地址?源码作为注册服务SpringCloud组件:搭建Eureka服务注册中心源码作为服务注册中心,还是按照之前的运行流程:

  1. 启动服务注册中心
  2. 启动本章服务项目
  3. 查看服务列表,服务注册方式

配置信息获取执行流程

在开始讲解本章注册方式之前,我们需要了解整体的配置信息获取的流程信息,这样才可以分析指定的注册方式执行流程。

第一步:实例化EurekaInstanceConfigBean配置实体

在项目启动时由于依赖spring-cloud-starter-netflix-eureka-client内通过配置spring.factories文件来让项目启动时自动加载并实例化org.springframework.cloud.netflix.eureka.EurekaClientAutoConfiguration配置类,EurekaClientAutoConfiguration内会自动实例化EurekaInstanceConfigBean并且自动绑定eureka.instance开头的配置信息(具体为什么会自动映射可以去了解下@ConfigurationProperties注解作用),部分源码如下所示:

......
public class EurekaClientAutoConfiguration {
    //省略部分源码
    @Bean
    @ConditionalOnMissingBean(value = EurekaInstanceConfig.class, search        = SearchStrategy.CURRENT)
    public EurekaInstanceConfigBean eurekaInstanceConfigBean(InetUtils inetUtils,                                                       ManagementMetadataProvider managementMetadataProvider) {
      //省略部分源码
      // 传递
      EurekaInstanceConfigBean instance = new EurekaInstanceConfigBean(inetUtils);
      // 省略部分源码
    }
    //省略部分源码
}

EurekaClientAutoConfiguration#eurekaInstanceConfigBean方法只有满足@ConditionalOnMissingBean(value = EurekaInstanceConfig.class, search = SearchStrategy.CURRENT)表达式后才会去实例化,并且把实例化对象放入到IOC容器内容,BeanIdeurekaInstanceConfigBean,也就是方法的名称。 在EurekaClientAutoConfiguration#eurekaInstanceConfigBean方法中有这么一行代码我们可以进行下一步的分析

// 通过有参构造函数实例化EurekaInstanceConfigBean配置实体
EurekaInstanceConfigBean instance = new EurekaInstanceConfigBean(inetUtils);

通过调用EurekaInstanceConfigBean(InetUtils inetUtils)构造函数来进行实例化EurekaInstanceConfigBean对象,在这个构造函数内也有一些实例化的工作,源码如下:

public EurekaInstanceConfigBean(InetUtils inetUtils) {
    this.inetUtils = inetUtils;
    this.hostInfo = this.inetUtils.findFirstNonLoopbackHostInfo();
    this.ipAddress = this.hostInfo.getIpAddress();
    this.hostname = this.hostInfo.getHostname();
}
第二步:InetUtils#findFirstNonLoopbackHostInfo获取主机基本信息

在构造函数EurekaInstanceConfigBean(InetUtils inetUtils)源码实现内hostInfo主机信息通过了InetUtils#findFirstNonLoopbackHostInfo方法来进行实例化,我们来看看这个方法的具体实现逻辑,它会自动读取系统网卡列表然再进行循环遍历查询正在UP状态的网卡信息,如果没有查询到网卡信息,则使用默认的HostNameIpAddress配置信息,源码如下所示:

public HostInfo findFirstNonLoopbackHostInfo() {
    InetAddress address = findFirstNonLoopbackAddress();
    if (address != null) {
        return convertAddress(address);
    }
    HostInfo hostInfo = new HostInfo();
    hostInfo.setHostname(this.properties.getDefaultHostname());
    hostInfo.setIpAddress(this.properties.getDefaultIpAddress());
    return hostInfo;
}

public InetAddress findFirstNonLoopbackAddress() {
    InetAddress result = null;
    try {
        int lowest = Integer.MAX_VALUE;
        for (Enumeration<NetworkInterface> nics = NetworkInterface
                .getNetworkInterfaces(); nics.hasMoreElements();) {
            NetworkInterface ifc = nics.nextElement();
            if (ifc.isUp()) {
                log.trace("Testing interface: " + ifc.getDisplayName());
                if (ifc.getIndex() < lowest || result == null) {
                    lowest = ifc.getIndex();
                }
                else if (result != null) {
                    continue;
                }

                // @formatter:off
                if (!ignoreInterface(ifc.getDisplayName())) {
                    for (Enumeration<InetAddress> addrs = ifc
                            .getInetAddresses(); addrs.hasMoreElements();) {
                        InetAddress address = addrs.nextElement();
                        if (address instanceof Inet4Address
                                && !address.isLoopbackAddress()
                                && isPreferredAddress(address)) {
                            log.trace("Found non-loopback interface: "
                                    + ifc.getDisplayName());
                            result = address;
                        }
                    }
                }
                // @formatter:on
            }
        }
    }
    catch (IOException ex) {
        log.error("Cannot get first non-loopback address", ex);
    }

    if (result != null) {
        return result;
    }

    try {
        return InetAddress.getLocalHost();
    }
    catch (UnknownHostException e) {
        log.warn("Unable to retrieve localhost");
    }

    return null;
}

默认的HostNameIpAddress属性配置信息在InetUtilsProperties配置实体类内,如果不进行设置则直接使用默认值,如果你想更换默认值,那么你可以在application.yml配置文件内通过设置spring.cloud.inetutils.defaultHostnamespring.cloud.inetutils.defaultIpAddress进行修改默认值,源码如下所示:

public class InetUtilsProperties {
    public static final String PREFIX = "spring.cloud.inetutils";

    /**
     * The default hostname. Used in case of errors.
     */
    private String defaultHostname = "localhost";

    /**
     * The default ipaddress. Used in case of errors.
     */
    private String defaultIpAddress = "127.0.0.1";
}
第三步:EurekaInstanceConfigBean#getHostName方法实现

getHostName是一个Override的方法,继承于com.netflix.appinfo.EurekaInstanceConfig接口,该方法有个boolean类型的参数refresh来判断是否需要刷新重新获取主机网络基本信息,当传递refresh=false并且在application.yml配置文件内并没有进行手动设置eureka.instance.hostname以及eureka.instance.ip-address参数则会根据eureka.instance.prefer-ip-address设置的值进行返回信息,源码如下所示:

@Override
public String getHostName(boolean refresh) {
    if (refresh && !this.hostInfo.override) {
        this.ipAddress = this.hostInfo.getIpAddress();
        this.hostname = this.hostInfo.getHostname();
    }
    return this.preferIpAddress ? this.ipAddress : this.hostname;
}

默认注册方式源码分析

由于在实例化EurekaInstanceConfigBean配置实体类时,构造函数进行了获取第一个非回环主机信息,默认的hostName以及ipAddress参数则是会直接使用InetUtils#findFirstNonLoopbackHostInfo方法返回的相对应的值。

IP优先注册方式源码分析

EurekaInstanceConfigBean#getHostName方法直接调用本类重载方法getHostName(boolean refresh)并且传递参数为false,根据第三步源码我们就可以看到:

return this.preferIpAddress ? this.ipAddress : this.hostname;

如果eureka.instance.prefer-ip-address参数设置了true就会返回eureka.instance.ip-address的值,这样我们就可以从中明白为什么主动设置eureka.instance.ip-address参数后需要同时设置eureka.instance.prefer-ip-address参数才可以生效。

指定IP、HostName源码分析

我们通过application.yml配置文件进行设置eureka.instance.hostname以及eureka.instance.ip-address后会直接替换原默认值,在EurekaInstanceConfigBean#getHostName中也是返回的this.hostnamethis.ipAddress所以在这里设置后会直接生效作为返回的配置值。

总结

我们通过源码进行分析服务注册方式执行流程,这样在以后进行配置eureka.instance.hostnameeureka.instance.prefer.ip-addresseureka.instance.ip-address三个配置信息时就可以根据优先级顺序达到预期的效果,避免没有必要的错误出现。

源码位置

与恒宇少年面对面

如果你喜欢恒宇少年的相关文章,那么就去微信公众号(恒宇少年)关注我吧!!! 当然你也可以去 SpringCloud码云源码 项目底部扫描微信公众号二维码关注我,感谢阅读!!!

学习目录推荐

开源信息

这段时间一直在编写开源的相关框架,致力于公司使用的框架升级以及开源计划,将公司使用到的工具以及插件进行升级重构并且开源。

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

发表于

我来说两句

0 条评论
登录 后参与评论

相关文章

来自专栏张戈的专栏

分享最近对网站外链跳转页面代码的一些改善

很久之前在博客分享了几篇关于外链跳转的代码或教程。如果没看过的可以先了解下: 分享两种外链跳转方法,可避免权重流失。 分享一个 WordPress 外链跳转...

1K8
来自专栏Java成长之路

jsonp详解

说到AJAX就会不可避免的面临两个问题,第一个是AJAX以何种格式来交换数据?第二个是跨域的需求如何解决?这两个问题目前都有不同的解决方案,比如数据可以用自定义...

2864
来自专栏Java架构沉思录

AJAX 原理与 CORS 跨域

https://segmentfault.com/a/1190000011549088

2411
来自专栏Young Dreamer

web worker 扫盲篇

什么是woker 官方的解释是这样的: worker是一个对象,通过构造函数Worker创建,参数就是一个js文件的路径;文件中的js代码将运行在主线程之外的w...

3668
来自专栏名山丶深处

springboot集成schedule(深度理解)

7155
来自专栏fixzd

redis系列:通过队列案例学习list命令

这一篇文章将讲述Redis中的list类型命令,同样也是通过demo来讲述,其他部分这里就不在赘述了。

3271
来自专栏Python中文社区

Python爬虫—破解JS加密的Cookie

專 欄 ❈Jerry,Python中文社区专栏作者。 blog:https://my.oschina.net/jhao104/blog github:htt...

6708
来自专栏安恒网络空间安全讲武堂

CSP总结及CTF实例分析

本文作者:HeartSky 最近各大比赛中 CSP 绕过的题目突然多了起来,自己也尝试着总结下 What is CSP? > The new Content-S...

5576
来自专栏北京马哥教育

linux服务器下LNMP安装与配置方法

云豆贴心提醒,本文阅读时间6分钟 一、准备 1.准备php函数的rpm包 2.准备lnmp其他的源代码包 3.安装php-5.2.14源代码包所需要的函数支...

3859
来自专栏安恒网络空间安全讲武堂

Python编写渗透工具学习笔记二 | 0x02利用FTP与web批量抓肉鸡

0x02利用FTP与web批量抓肉鸡 脚本要实现的目标和思路: 先尝试匿名登录ftp,当匿名登录失败时再尝试用用户/密码爆破登录,登录成功后,脚本会搜索ftp中...

1.7K7

扫码关注云+社区

领取腾讯云代金券