概述
前言
学习源码之前,小伙伴们首先应该创建好服务提供方,方便后续断点阅读。
如果小伙伴们不知道如何动手,请参考一下这两篇文章:
《聊聊服务治理》 、《创建服务提供者》
我们先看一下这个服务发现的核心注解:
点进去:
我们可以看到这里有一个autoRegister注解,它的默认值是true。
上面注释中解释:如果为true,ServiceRegistry会自动注入local server即当前instance。看到这里,感觉线索还不是很明显,我们再找找其他内容。
果然,看到上面Import注解中:
点进去看看:
发现里面果然是有东西的。
提到这里,我给大家分享一个小插曲哈,其实我个人学习技术的过程中还是比较虐心的,我每次学习一个新技术的时候,我一般不会从一些博客或者网上找一些example来看,我通常会去官方网站里去找一些开头,比如我们这里@EnableDiscoveryClient它就是一个开头,当我找到这样的开头以后,我就不会再去看一些示例了,我会尽量尝试自己摸索,我自己把整个链路给摸索清楚,这个过程大家听了是不是感觉比较虐心哈,这里我并不是说让大家也这么虐心,其实我这么做就是太闲了哈哈哈开个玩笑。
其实大家可以尝试部分自己摸索的形式,比如说很多人用了好几年的Spring,那是不是很多人都知道怎么用但是压根没有研究过源码,那你怎么知道它底层怎么运行的?很多小伙伴可能会去网上搜一些面试宝典,面试宝典那些问题啊,你也只能忽悠住那些不研究源码的面试官。
所以小编通过这种方式也教大家虐心一把。
好,言归正传,我们先进入这个类里,我们看一下接下来该怎么办。
咱先从这个selectImports方法里,找到第一行打个断点。
接下来我们先启动注册中心:
然后找到服务eureka-client的启动类,debug方式运行:
我们启动后就会发现,很快就走到断点里面来了:
我们先看一下这个入参metadata的内容,其实这里面主要的内容就是这个annotations,点进去我们可以看到两个东西:
一个是SpringBootApplication,另一个是EnableDiscoveryClint。这两个就是我们在启动类里挂的两个注解。
我们再放行断点继续往下走:
我们可以看到这个AnnotationAttributes 里面获取到了@EnableDiscoveryClient这个注解里的内容。
获取到这个注解后,下面的attributes.getBoolean()
是获取autoRegister这个属性的值。
继续往下走:
这里因为autoRegister是true,就进入到了if里面,这里我们又可以看到一个importsList添加了AutoServiceRegistrationConfiguration
,通过这个名字我们就能断定这是一个配置类,虽然没有见过,但是它肯定是有作用的。
在继续往下走:
就直接到了return了,这个方法已经走完了。
那我们就进入到刚刚看到的配置类里面找找线索吧。
可以看到这个类上面有个注解EnableConfigurationProperties
,要读取一些配置属性。
那我们点进AutoServiceRegistrationProperties
这个类里看看。
我们可以看到,上面的注解里读取了一个自动注册spring.cloud.service-registry.auto-registration
,然后我们再看下面都有哪些属性:
从这里,我们就可以猜想到,这个类肯定是在某些配置项加载的流程中应用到。那我们就来找找这个类都有被哪里引用:
我们可以看到有好几个类都有引用,那我们就先看看这个AbstractAutoServiceRegistration,因为我们很多方法它的抽象层都会定义在Abstract类里,所以我一般都会先去Abstract类里找找有没有什么线索。
那我们点进这个类,看看是哪个方法引用到的:
我们这里可以看到,有个初始化的方法里引用到了这个properties,注入到了当前的属性当中,这一步看着也没什么,平淡无奇。
但是我们可以看到上面,他注入了一个serviceRegistry,服务注册!
看到这里,我们眼前一亮,那我们就点进去看看。
我们可以看到它是一个interface,那我们就看看它的实现类吧:
可以看到,它下面只有一个实现类,就是这个EurekaServiceRegistry,看到里面有个方法叫register
那这就是注册了,我们就在这里第一行打个断点看看能不能走过来:
上一个断点放行后我们发现果然进来了,那我们就挨个读一下这个register里面都有什么内容。
先点进这个maybeInitializeClient
方法里:
可以看到这里有一句话:force initialization of possibly scoped proxies
小编英语不好哈,拿着百度翻译了一下:
强制初始化一些代理,那我们看看到底强制化初始的什么内容,那就先点进getInfo里:
可以看到这里返回了一个instanceInfo,这又是什么东东呢?既然搞不懂那就把断点放过来看看:
看到里面属性还是挺多的,我们就来找几个看看:
我们可以看到,这里有个instanceId是一个ip地址:eureka-client:端口,我们就可以发现,这不就是我当前的eureka-client实例么,然后我们看下面的appName、homePageUrl…从这些我们可以看出,都是我们当前实例的一些信息,那这不就是要向注册中心注册的一些内容么,那这时候就应该明白了,这个instanceInfo就是要向注册中心注册的一些内容了。
那我们就再回到我们的register方法里,了解了maybeInitializeClient方法后我们继续往下走:
可以看大这里有个setInstanceStatus方法,这个方法就是我们在注册中心页面中看到的服务状态,那我们看看这里的状态是什么:
我们都知道服务状态有UP,也有DOWN对吧,我们可以看到这里的状态是UP。
我们再继续看:
这里的HealthCheck我们先不管了,我们可以发现,虽然我们看完了register,但是我们发现它并没有向注册中心发送请求,那这看起来就并不是请求发起的地方,那我们再看看Debug栈,找找上一层是谁调用的:
在Debug栈里,我们就可以看到一个EurekaAutoServiceRegistration,点进去看看:
可以看到里面是serviceRegistry.register
这里调用到的。
那我们就在下面打个断点:
我们发现果然走进来了,这一句看起来就有些蹊跷,因为它这里publishEvent 发布了一个事件,那是什么事件呢?
InstanceRegisteredEvent
已经注册的事件? 那不对啊,还没有注册呢,就已经发布了一个已经注册的事件么?
那我们看一下它的参数:
在这里面我们可以看出,这就是要往注册中心注册的内容,那我们再看一下,它这里通过publishEvent
向Spring中的context发布了一个事件,这里的context其实就是ApplicationContext:
我们先在这个断点里别往下走,我们先打开注册中心页面:
我们发现并没有Instance注册进来。
我们再看看源码:
但是我们可以看到执行完这行代码后,这里的running设置成了true,确实是运行起来了。
各位小伙伴可以通过小编上面介绍的方式,尝试自己探究一下,我这里就不详细介绍了,我就直接做个剧透好了,下一个衔接到的地方是在DiscoveryClient里:
我们找到register
这个方法。
可以看到,在这个方法里,是一个正儿八经的register方法,通过这个register方法,就可以完成服务间调用了,但是它不是这么简单的一层,我们就看看它到底是哪个类发起的调用吧:
可以看到,是一个SessionedEurekaHttpClient在这个类里发起的调用,我们寻找一下这个register:
可以看到,它是在一个顶层的抽象类中,在这个方法里,我们可以看到,它是通过execute执行的,我们看到这里面的参数传入了一个delegate,这样一个代理,然后它使用了一个代理进行了注册。
我们再进到这个SessionedEurekaHttpClient.execute
这个方法里,看看里面是怎么实现的:
首先第一步是获取系统时间,第二步是系统时间减去上次连接的时间,
往下走:
在这里通过eurekaHttpClientRef获取了一个实例,如果实例为空,它这里会调用一个工具类的方法:getOrSetAnotherClient
获取了一个新的实例。
然后我们获取到HttpClient后,然后调用execute
方法,而这里的execute
方法其实又回到了EurekaHttpClientDecorator:
这个地方非常绕,它这几个代理对象总是在父类子类里传来传去,所以说在代码可读性上来讲,小编认为Eureka的代码可读性并没有那么高,和Spring的源码比代码水平确实是差了一截…
最后
以上就是内向刺猬为你收集整理的彻底搞懂Eureka——探讨吐血绕人的服务注册逻辑的全部内容,希望文章能够帮你解决彻底搞懂Eureka——探讨吐血绕人的服务注册逻辑所遇到的程序开发问题。
如果觉得靠谱客网站的内容还不错,欢迎将靠谱客网站推荐给程序员好友。
发表评论 取消回复