概述
Spring Cloud Eureka是Spring Cloud Netflix项目下的服务治理模块。
由于微服务概念的引入,使大型服务在一定程度上彻底的解耦,当服务集群足够庞大的时候,服务治理成为了微服务的痛点之一。
Eureka是Spring Could中服务发现的推荐组件,保证服务的高可用性,它有着丰富的API,使得Eureka作为服务发现与治理都比较方便。
架构与原理
- Eureka Server:服务的注册中心,负责维护注册的服务列表。
- Service Provider:服务提供方,作为一个Eureka Client,向Eureka Server做服务注册、续约和下线等操作,注册的主要数据包括服务名、机器ip、端口号、域名等等。
- Service Consumer:服务消费方,作为一个Eureka Client,向Eureka Server获取Service Provider的注册信息,并通过远程调用与Service Provider进行通信
Eureka Server作为一个独立的部署单元,以REST API的形式为服务实例提供了注册、管理和查询等操作。同时,Eureka Server也为我们提供了可视化的监控页面,可以直观地看到各个Eureka Server当前的运行状态和所有已注册服务的情况。如图:
原理:
服务启动后向Eureka注册,Eureka Server会将注册信息向其他Eureka Server进行同步,当服务消费者要调用服务提供者,则向服务注册中心获取服务提供者地址,
然后会将服务提供者地址缓存在本地,下次再调用时,则直接从本地缓存中取,完成一次调用。
当服务注册中心Eureka Server检测到服务提供者因为宕机、网络原因不可用时,则在服务注册中心将服务置为DOWN状态,并把当前服务提供者状态向订阅者发布,订阅过的服务消费者更新本地缓存。
服务提供者在启动后,周期性(默认30秒)向Eureka Server发送心跳,以证明当前服务是可用状态。Eureka Server在一定的时间(默认90秒)未收到客户端的心跳,则认为服务宕机,注销该实例。
源码解读:
eureka主体实现方式:
ApplicationResource类接收Http服务请求,调用PeerAwareInstanceRegistryImpl的register方法,PeerAwareInstanceRegistryImpl完成服务注册后,调用replicateToPeers向其它Eureka Server节点(Peer)做状态同步。
eureka client
启动时候会创建一个定时任务,定时任务会将本地的服务配置信息,也就是注册到远端的服务信息自动刷新到注册服务器上,实现了服务注册以及缓存更新的机制。
1、com.netflix.discovery.DiscoveryClient.java中的可以看到initScheduledTasks方法,它封装了一个instanceInfoReplicator的定时任务,以一定的时间(默认30秒)来刷新服务的缓存和心跳信息。
2、instanceInfoReplicator中的run方法调用register来实现注册功能,start方法实现了定时刷新调用,定时注册到eureka
eureka server
1、com.netflix.eureka.resources.ApplicationResource 中使用addInstance方法接收来自client的请求消息,然后进行处理,最终的注册信息缓存在ConcurrentHashMap中,实现服务缓存。
Eureka的自我保护机制:
在默认情况下,Eureka Server在默认90s时间内没有收到服务端的心跳(默认30秒一次心跳,三次心跳),会将该服务注销。在一般情况下,网络通信的故障率较高,在网络通信出现异常时,Eureka Server如果正常注销服务,
将会导致大部分服务不可用,这违背了微服务高可用的初衷,在这种情况下,Eureka Server有自我保护机制,当它在短时间内丢失过多的客户端时(默认15分钟内低于85%),该节点将进入自我保护模式,不再注销服务,并且同时继续提供新服务的注册,当网络故障修复之后,该节点能自动的退出自我保护模式。
总之一句话:不管好数据坏数据,一个不落。
核心特性
- Eureka通过相互注册与复制支持高可用
- Eureka支持用户认证
- Eureka Client支持注册表缓存
- Eureka提供保护模式以解决网络分区故障
- Eureka提供健康检查
- Eureka支持RESTFUL API
为什么选择Eureka而非Zookeeper作为服务发现组件,以下对比:
使用方法
服务注册过程:
当实例状态发生变化时(上线Or下线),都会请求到eureka-server发送一个状态,在一定的时间后,eureka会对该服务进行加入或者删除,然后进行eureka集群的缓存复制。
创建Eureka Sever服务
1.创建一个Spring Boot工程,命名问Eureka-Server,并在pom文件中引入依赖:
1 | <properties> |
2.创建启动类
1 | @EnableEurekaServer //用来指定该项目为Eureka的服务注册中心 |
3.配置server服务
1 | # server (eureka 默认端口为:8761) |
4.配置完成启动eureka server即可,访问http://localhost:8761/eureka/
Eureka高可用集群配置:
三注册中心,两两互相注册将eureka.client.serviceUrl.defaultZone值设置为其他两节点值即可。
创建Eureka Client项目
1.创建Springboot项目,引入如下依赖:
1 | <properties> |
2.新增启动类
1 | /** |
3.配置client连接上服务发现
1 | # server |
4.启动后,能在Eureka-web端上看到服务的列表
服务发现使用场景
服务发现并不是为了服务发现而服务发现,是为了使用一些必要的功能而必不可少的组件,服务发现的下游有丰富的内部服务调用工具
- Ribbon,实现客户端的负载均衡。
- Hystrix,断路器。
- Feign,RESTFUL Web Service客户端,整合了Ribbon和Hystrix。
服务调用端负载均衡——Ribbon
Ribbon是Netflix发布的开源项目,主要功能是为REST客户端实现负载均衡。它主要包括六个组件:
- ServerList,负载均衡使用的服务器列表。这个列表会缓存在负载均衡器中,并定期更新。当Ribbon与Eureka结合使用时,ServerList的实现类就是DiscoveryEnabledNIWSServerList,它会保存Eureka Server中注册的服务实例表。
- ServerListFilter,服务器列表过滤器。这是一个接口,主要用于对Service Consumer获取到的服务器列表进行预过滤,过滤的结果也是ServerList。Ribbon提供了多种过滤器的实现。
- IPing,探测服务实例是否存活的策略。
- IRule,负载均衡策略,其实现类表述的策略包括:轮询、随机、根据响应时间加权,(可以自定义负载均衡策略,实现完之后可以重新注入ribbon)
- ILoadBalancer,负载均衡器。这也是一个接口,Ribbon为其提供了多个实现,比如ZoneAwareLoadBalancer。而上层代码通过调用其API进行服务调用的负载均衡选择。一般ILoadBalancer的实现类中会引用一个IRule。
- RestClient,服务调用器。顾名思义,这就是负载均衡后,Ribbon向Service Provider发起REST请求的工具。
Ribbon工作时会做四件事情:
1.优先选择在同一个Zone(区域)且负载较少的Eureka Server;
2.定期从Eureka更新并过滤服务实例列表;
3.根据用户指定的策略,在从Server取到的服务注册列表中选择一个实例的地址;
4.通过RestClient进行服务调用。
Ribbon的源码实现大致原理:
LoadBalancerClient : 继承了ServiceInstanceChooser接口,实现类是RibbonLoadBalancerClient.主要方法有choose(ServiceInstanceChooser用来选择instance) ,execute(LoadBalancerClient 用来执行).
ILoadBalancer:接口方法有addServers,chooseServer,markServerDown,getReachableServers,getAllServers.(负载均衡)实现类为BaseLoadBalancer 和 DynamicServerListLoadBalancer.
BaseLoadBalancer :主要由以下类进行配置IClientConfig(基本配置,用于初始化) IRule(路由策略) IPing (判断响应) (静态配置负载均衡)
DynamicServerListLoadBalancer: ServerList(用于从Eureka中获取服务列表) ServerListFilter(列表过滤) 动态配置负载均衡)
负载均衡过程:
1.RibbonLoadBalancerClient接收到一个service-id之后,调用ServiceInstanceChooser的choose方法,choose方法首先得到 ILoadBalancer (当中有client列表)。再利用ILoadBalancer 的chooseServer方法得到普通Server实例并实例化RibbonServer,并返回。chooseserver会通过loadbalancer中的rule来返回正确的instance。
2.DynamicServerListLoadBalancer由iconfig初始化,初始化完成后调用updateListOfServers方法获得所有ServerList。(方法中通过ServerList实现类来访问EurekaClient中的注册列表)
3.BaseLoadBalancer中有一个PingTask任务,他每10秒钟会向EurekaClient发送一个Ping。如果从Eureka拉取的注册列表发生了改变,则重新更新列表。
4.LoadBalancerClient根据注册列表和IRule来进行负载均衡
Ribbon的应用
springcloud提供了默认的配置RibbonClientConfiguration。它提供了包含ILoadBalancer,ServerListFilter在内的许多配置。
你可以更改默认的配置,更改方法为在.property文件中添加
服务调用端熔断——Hystrix
Netflix创建了一个名为Hystrix的库,实现了断路器的模式。
“断路器”本身是一种开关装置,当某个服务单元发生故障之后,通过断路器的故障监控(类似熔断保险丝),向调用方返回一个符合预期的、可处理的备选响应(FallBack),而不是长时间的等待或者抛出调用方无法处理的异常,这样就保证了服务调用方的线程不会被长时间、不必要地占用,从而避免了故障在分布式系统中的蔓延,乃至雪崩。
正常情况下,在请求失败频率较低的情况下,Hystrix还是会直接把故障返回给客户端。只有当失败次数达到阈值(默认在20秒内失败5次)时,断路器打开并且不进行后续通信,而是直接返回备选响应。
当然,Hystrix的备选响应也是可以由开发者定制的。
除了隔离依赖服务的调用以外,Hystrix还提供了准实时的调用监控(Hystrix Dashboard),Hystrix会持续地记录所有通过Hystrix发起的请求的执行信息,并以统计报表和图形的形式展示给用户,包括每秒执行多少请求多少成功,多少失败等。Netflix通过hystrix-metrics-event-stream项目实现了对以上指标的监控。Spring Cloud也提供了Hystrix Dashboard的整合,对监控内容转化成可视化界面,Hystrix Dashboard Wiki上详细说明了图上每个指标的含义。
服务调用端代码抽象和封装——Feign
Feign是一个声明式的Web Service客户端,它的目的就是让Web Service调用更加简单。
它整合了Ribbon和Hystrix,从而让我们不再需要显式地使用这两个组件。
Feign还提供了HTTP请求的模板,通过编写简单的接口和插入注解,我们就可以定义好HTTP请求的参数、格式、地址等信息。
接下来,Feign会完全代理HTTP的请求,我们只需要像调用方法一样调用它就可以完成服务请求。
Feign具有如下特性:
- 可插拔的注解支持,包括Feign注解和JAX-RS注解
- 支持可插拔的HTTP编码器和解码器
- 支持Hystrix和它的Fallback
- 支持Ribbon的负载均衡
- 支持HTTP请求和响应的压缩
以下是一个Feign的简单示例:
1 | @SpringBootApplication |
说明:
(1)使用 @Component
注解向SpringBoot中注入该组件。
(2)使用@FeignClient("XXX")
注解来绑定该接口对应的服务。
注意:在启动类上加 @EnableFeignClients 注解,如果定义的Feign接口定义跟启动类不在一个包名下,还需要制定扫描的包名:@EnableFeignClients(basePackages = "xxx.xxx.xxx")
建议将接口定义,单独抽一个项目出来,后面打成公共的jar,这样无论是哪个项目需要调用接口,引入公共的接口SDK jar即可,不需要重新定义一遍。
注意事项
eureka在服务下线后30秒节点还存在,需妥善处理
替代方案
Eureka->consul consul实际也是CP型服务发现,并且监控的指标较多,可作为替代方案
Ribbon->nginx 负载均衡nginx有相对较为成熟的机制,但是配置项较多,谨慎使用