【一】微服务之服务发现概述
关于微服务,近年来可谓是大火,业界也吹刮着一种实践微服务的风潮。本人有幸在去年参与到一个向微服务过渡的产品,再结合自己所学的一些知识做一个总结,同时也是一个继续学习的过程。
如果说在实施微服务的所有经验中,挑出最重要的一点,那么我觉得应该是:
基础设施的建设决定微服务的实施效果
后面可能会写一篇关于具体踩坑的总结,大多也是关于基础设施组件的。如果在实施微服务的过程中,相应的基础设施没有配套跟上,是极有可能从一个坑跳到另一个坑中去的。
服务治理包括服务的注册和发现,是实施微服务过程中最基本的基础设施之一了。目前可以用作服务发现的组件主要有Eureka,Zookeeper,Consul,Etcd,在我们的项目中由于混用了Spring Cloud和Dubbo两套框架,所以Eureka和Zookeeper都有用到,本文先对Eureka进行分析,由于Zookeeper不属于专门的服务发现组件,具有多种功能,所以会专门做分析。
一个服务发现的服务端需要满足的需求:
服务注册:针对Client端的服务提供者,在启动时将自己注册到Server端。
服务发现:针对Client端的服务消费者,查询可用的服务列表。
服务列表保存:针对Server端,记录各个微服务的相关信息。
跨节点共享信息:针对Client端服务上线后,只会注册在一个服务端实例上,各个服务端之间要进行信息同步.
健康监测:监测已经注册的服务,如果长时间无法访问,则从列表中剔除。
我们现在提到的Eureka,总括了Netflix Eureka以及Spring Cloud Netflix包中对于Eureka的封装,但其实Netflix Eureka本身就已经是一套完整的基于REST的服务发现框架。Spring Cloud对于Eureka的集成,让它更容易的实施在微服务体系之中。
【二】Eureka源码分析
在阅读Eureka源码之前,我们尝试换一种思路,不像以往一样顺着流程去Debug,这次我们试着站在一个设计者的角度,假设这个服务注册中心是你接到的一个新需求,需要你完成从设计到实现,需求文档就是上一章节提到的,服务发现组件需要具备的功能。
首先,基本需求是必须实现的,也就是服务注册和发现,服务列表保存。我们把需要设计的点理顺清楚,再去看源码中是如何灵活运用各种技术提供了实现。
这样不单可以学到一些原理上的知识,更多的应该是学习为什么要这样设计,尽管是阅读别人的源码,也不要被动的接收,有主动思考的过程会更好。
那么我们首先梳理一下基本的注册流程如何实现?
首先,这个过程中有两种类型的三个角色,分别是Server和Client,Client又分为服务提供者,服务消费者,他们的交互关系如下图所示。
那么具体到每个步骤,我们应该有哪些设计思路呢?
Client端-服务提供者在启动时能注册到Eureka-Server中
客户端注册信息如何设计(Key-Value),key是serviceId,value是一个包含客户端信息的Object
客户端在启动时如何实例化这个对象,并注册到服务端
Server端保存服务信息,显然要设计为Map结构
Client端-服务消费者可以向Server获取服务列表,这里可以是服务端推送或者是客户端拉取的形式
【步骤一】服务提供者启动时注册
首先,我们在配置文件中做了一些Eureka相关的配置,所以【阅读目标一】如何读取这些配置。
然后,客户端是在启动过程中就完成了注册,所以【阅读目标二】如何结合SpringBoot的自动装配,完成启动过程中的初始化。
读取了配置并没有结束,是怎么在SpringBoot启动时完成了注册过程呢,所以【阅读目标三】启动时的注册过程。
接下来就开始这部分的源码阅读:
【阅读目标一】读取配置
SpringBoot的自动装配,一定会读取spring.factories文件,所以我们先来看的spring.factories文件
【阅读目标二】SpringBoot自动装配
与Eureka自动装配有关的必然是
"eureka.instance.xxx"相关配置
在这个方法中,创建了类型的实例,参数是EurekaInstanceConfig类型。
EurekaInstanceConfig是一个接口,与Eureka相关的实现类是org.springframework.cloud.netflix.eureka.EurekaInstanceConfigBean,我们会看到在这里完成了自动装配,会构建"eureka.instance"这个实例需要的所有信息。
"eureka.client.xxx"相关配置
在方法中创建了实例,把和传入
【阅读目标三】启动时的注册过程
随着源码来到
关于 注解,如果在构造方法使用,那么构造方法中的参数,将由IOC容器提供。
忽略一些参数判断的代码,我们看到定义了一些线程池,
然后,执行方法,
接下来我们以心跳线程池为例分析,是如何向Server端注册的。
有一个,打开它的源码:
乍一看似乎没有什么,但是小心有坑,有时秘密往往藏在不起眼的地方,就在方法中。
这个方法的流程是,先向服务端发起一次请求,如果当前实例没有被注册,会返回404,那么会执行进行注册。
【步骤二】Server端保存服务列表
想要了解Server端如何保存了服务列表,就要知道Server端如何接收Client端的HTTP请求。所以本步骤的阅读目标有两个,分别是服务提供者注册的后续部分,和Server端接收请求的部分。
【阅读目标一】走完客户端注册流程
如果想知道服务端是如何保存的,就需要进一步走完客户端的注册流程,之所以把最后的注册流程放在这里,是为了把一次HTTP请求-响应的过程放在一起。可想而知,客户端的注册会发送一个HTTP请求,将实例信息发送到服务端。
,我们看到这里,使用了jersey框架,拼装了一个URL,发送了一个HTTP请求。
【阅读目标二】Server端接收HTTP请求
先来看Server端的自动装配类,与之前的Client端类似,只是具体配置的内容不一样,先大概提一下,后面细说:
首先是spring-cloud-netflix-eureka-server-2.1.2.RELEASE.jar中的spring.factories文件
我们猜想这里一定会注册一个Jersey客户端,用来接收HTTP请求,我们看到下面Jersey客户端被初始化为一个拦截器Bean放入Spring容器中。
处理HTTP请求的逻辑放在类中,例如接收一个GET请求
关于如何分发处理请求,是通过Jersey框架完成的。这里类似于SpringMVC的dispatcherServlet,将接收到的请求首先到,然后交由处理
接下来我们来到ApplicationResource的POST请求,看他在处理POST请求(注册一个实例)时具体做了什么。
忽略前面一些状态判断的代码,我们来关注核心的部分
随着代码一步步深入,到达的是,这里就是注册的核心流程:
不出我们所料,我们看到了一个ConcurrentHashMap,留作纪念,我粘贴一下这个Server端保存实例信息的ConcurrentHashMap,标记到此一游~
【步骤三】客户端获取服务列表
刷新缓存,通过一个定时任务,定时向服务端获取服务信息,放在本地缓存中。
具体的拉取服务列表的逻辑在,基于上面的分析,我们基本可以知道,这里是通过一个Jersey客户端,发送一个HTTP的Get请求,这里大致的代码逻辑想必都可以猜想得到,这里就不再赘述。
至此,Eureka服务端,客户端请求接收和发送的基本流程,已经大体完成,但对于Eureka本身的分析还没有结束,还有一些Eureka作为服务注册中心为我们提供的特性,比如高可用,健康检查,是下一篇要研究的。
参考文献:《Spring微服务实战》,《Spring Cloud微服务之战》
长按订阅更多精彩▼
本文地址:http://syank.xrbh.cn/quote/6171.html 迅博思语资讯 http://syank.xrbh.cn/ , 查看更多