前言
我计划写一个SpringCloud源码系列的文章,毕竟最有效的学习方式,就是尝试把知识教授给其他人,在这个过程中如果遇到问题,就需要反思、再学习、再反思、再学习,渐渐就进步了。
虽然eureka已经停止维护了,但是它的设计理念、系统架构设计都是值得学习的,是有学习的价值的,所以springCloud源码系列的探秘之旅,我们从此处开始。
服务端和客户端的概念
eureka由服务端和客户端两部分组成,服务端作为单独的一个服务运行,而客户端则是需要和我们自己的业务系统进行集成。
服务端负责维护各个服务的地址信息,客户端则需要和服务端进行通信,获取其他服务的地址。
源码概览
本系列文章通过源码+图解说明方式,力求用简单的、轻松的方式辅助大家理解eureka的核心机制。
springCloud-eureka是对netflix-eureka的封装,所以我们直接通过学习Netflix的eureka源码就ok了,当然最后还是会研究一下springCloud是怎么对netflix进行封装的。
本文使用的是eureka 1.7.2版本,下载地址为:https://github.com/Netflix/eureka
下载下来的源码如图:
eureka-server和eureka-client是怎样通信的
eureka服务端作为一个单独的系统,可独立部署。就像平时我们自己开发的web服务一样,打成war包后,直接放入web容器中,再启动web容器(比如tomcat),eureka服务端就可以被客户端访问了。
eureka服务端提供了大量的接口,能够让客户端通过http请求进行调用,客户端一旦调用相应的接口(比如服务注册接口、服务下线接口等),服务端就可以做出相应的响应。
接下来通过一个服务注册的例子,来讲解eureka服务端在启动的过程中做了什么,eureka客户端的启动又做了些什么,服务注册又是怎么实现的。
eureka-server在项目启动期间做了些什么
首先,是eureka-server的启动过程,我们在eureka-core的源码中,查看一下eureka服务端启动时做了什么。
类名:EurekaBootStrap.java
主要涉及的方法:contextInitialized(),initEurekaEnvironment(),initEurekaServerContext()
说明:源代码太长,不适合放在文章里,所以用图片的方式做了一些简化。具体代码细节,建议大家可以从github下载源码,对照文章进行阅读。文章里只能梳理主要流程,展现主要的代码逻辑。
简单总结一下,eureka-server的启动,大概做了以下的事情:
1.做了一些初始化操作:读取配置文件、创建ApplicationInfoManager
、eurekaClient
、PeerAwareInstanceRegistry
、PeerEurekaNodes
等对象。具体查看上图。
**2.启动了一堆定时任务。**具体有哪些定时任务,我们后续查看到对应的机制时再讲,避免蒙圈。
图中的简化代码,大家有一个简单的理解即可,这里我们主要讲服务注册,所以只关注 PeerAwareInstanceRegistry
对象的创建即可。
来看看最关键的东西,注册表
客户端想要从服务端获取其他服务的信息,那么在服务端必定也必须要有一个地方保存所有的服务信息,对不对?
所以eureka的设计里,服务端的内部就维护了一张注册表,每当有新的服务注册时,服务端就会去更新这张注册表,注册表的初始化方法就是 new PeerAwareInstanceRegistry()
。
追进源码我们发现,实际创建的是AbstractInstanceRegistry.java类的对象,还是用一张图来简化该类。
AbstractInstanceRegistry.java中包含一些成员变量,暂时我们主要关注registry
对象,其余的部分还是等到学习对应的机制时再讲解。
registry
是一个ConcurrentHashMap
对象,它就是真正的注册表。至此我们已经知道在eureka-server里,是通过一个Map来维护服务注册信息。
服务注册是怎样实现的
接下来,来看看eureka-client调用服务注册接口时,eureka-server做了什么。
eureka-server端接收请求的类是eureka-core工程的resources包下的ApplicationResource.java,由于eureka的开发没有使用我们熟悉的springmvc,为了便于理解,我们可以把resources包下的类当做我们平常开发对应的Controller。
首先,我们先来看看InstanceInfo对象,也就是eureka-client调用服务注册接口时传递了什么参数。
在这里为了更直观的演示,我们借助于eureka-server项目中自带的单元测试来断点调试一下。
单元测试项目截图:
服务注册的单元测试代码,贴一小段,大概看一下eureka-server的测试代码长什么样:
public class EurekaClientServerRestIntegrationTest {
@Test
public void testRegistration() throws Exception {
InstanceInfo instanceInfo = instanceInfoIt.next();
EurekaHttpResponse<Void> httpResponse = jerseyEurekaClient.register(instanceInfo);
assertThat(httpResponse.getStatusCode(), is(equalTo(204)));
}
}
断点看看InstanceInfo中都是些什么数据:
InstanceInfo中的数据很多,我标注了一些常用的大家一眼就能看明白的参数。
前面提到过引进注册中心就是为了解决服务之间自动获取通信地址的难题,解决的方法就是所有的服务都把ip和端口号放在一个统一的地方(eureka-server),需要其他服务的地址时再从同一个地方去取即可。
现在大家就能知道,每个服务是怎么把自己的ip和端口号交给eureka-server的了。
看明白了服务注册时的请求参数,下面通过一张流程图来理解eureka-server在服务注册中具体做了哪些事情。
eureka-server服务注册流程如下
图中列出了服务注册过程中eureka-server端主要执行的逻辑,暂时我们只需要知道,eureka-server接收到服务注册的请求后,会将eureka-client传递过来的客户端的数据,保存进registry
中即可。其他逻辑暂时可忽略。
通过断点调试,看看registry
内部的数据长什么样的。
画一张图,方便理解registry
的数据结构。
至此,服务注册的相关流程就结束了,下一篇,我们来看看eureka-client是怎么获取注册表的。