netfix eureka 浅析
摘要: 网上关于eureka 的介绍很多,也有很多优秀的文章可以作为参考,在文章末尾我会贴上自己看过觉得有收获或者总结性强的文章。主要内容会简单介绍 eureka 整体的架构和原理,部分代码实现,以及配置参数列表和一些注意事项。
什么是eureka?
Eureka是一种基于REST(Representational State Transfer)的服务,主要用于AWS云,用于定位服务,以实现中间层服务器的负载平衡和故障转移。我们将此服务称为Eureka Server。Eureka还附带了一个基于Java的客户端组件Eureka Client,它使与服务的交互变得更加容易。客户端还有一个内置的负载均衡器,可以进行基本的循环负载均衡。在Netflix,一个更复杂的负载均衡器包含Eureka基于流量,资源使用,错误条件等多种因素提供加权负载平衡,以提供卓越的弹性。
本文以官方wiki为主要参考,wiki: https://github.com/Netflix/eureka/wiki/Eureka-at-a-glance
这段是官方的翻译,说白了,就是我们现在使用的微服务架构中的注册中心,server端是一个服务注册信息中心,client端则作为信息的消费者和提供者。类似的注册中心 还有consul,zookeeper,etcd 等都可以作为注册中心,官方宣布 eureka2.x disconnected 之后,consul 或称为主流的替代方案。
eureka 原理及架构
官方架构图:https://github.com/Netflix/eureka/raw/master/images/eureka_architecture.png
服务在Eureka 注册,然后发送心跳每30秒更新一次租约。如果客户端无法续订租约几次,则会在大约90秒内将其从服务器注册表中删除。注册信息和续订将复制到群集中的所有eureka节点。来自任何区域的客户端都可以查找注册表信息(每30秒发生一次)以查找其服务(可能位于任何区域中)并进行远程调用。
eureka 一些概念及部分源码分析
在架构中可以得知,分为以下几个部分:
高可用框架使用的是三个server,两两注册的方式。
register 注册 :client 启动后发起注册,netflix源码是从第一次发起心跳(30s后)发生注册,springcloud 更改过设置,在client启动时就发起注册。 在 eureka-client 里的discoveryclient类中有一个register()法
1
2
3
4
5
6
7
8
9
10
11
12
13
14boolean register() throws Throwable {
logger.info(PREFIX + "{}: registering service...", appPathIdentifier);
EurekaHttpResponse<Void> httpResponse;
try {
httpResponse = eurekaTransport.registrationClient.register(instanceInfo);
} catch (Exception e) {
logger.warn(PREFIX + "{} - registration failed {}", appPathIdentifier, e.getMessage(), e);
throw e;
}
if (logger.isInfoEnabled()) {
logger.info(PREFIX + "{} - registration status: {}", appPathIdentifier, httpResponse.getStatusCode());
}
return httpResponse.getStatusCode() == Status.NO_CONTENT.getStatusCode();
}这里小小吐槽一下,logger.info 竟然 用了 isInfoEnabled() 判断,有些多余,这个原则是为了 减少 字符串 + 操作可能带来多余的性能复合,但现在用了通配符 {} ,在logger.info 的实现中已经使用 isInfoEnabled 方法来避免了。但是源码里经常这样写,可能是为了保险?
往下追踪eurekaTransport.registrationClient.register 方法可以发现,底层是使用 Jersey 实现了自己的eurekahttp 封装,jersey (一个 Java restful 框架) 还没有使用过,有空去研究一下。
往上追踪呢发现 register 方法是在一个 InstanceInfoReplicator类 ,继承了runnale 是一个定时任务,二这个定时任务在@inject DiscoveryClient 的时候就执行了initScheduledTasks() 方法,在这个方法内开启了这个定时任务。
renew 续约 :client 每30s 发生一次http请求,及上面提到的定时任务,来告诉server端,我依旧存活着。renew() 方法 与 register() 类似,都在 discoveryclient类中。服务端的续约接口自,eureka-core中的 com.netflix.eureka包下的InstanceResource类下,接口方法为renewLease(),追踪源码可以看到 心跳间隔是30s,剔除服务心跳超时时间是90s
可以通过设置参数来改变时间间隔,官方建议是谨慎改变这个时间
1
2eureka.instance.leaseRenewalIntervalInSeconds
eureka.instance.leaseExpirationDurationInSecondsgetRegistery 得到服务 : client 获得server端的 实例信息,同上,有一个fetchRegistry() 和 getApplications() 的方法,客户端想要获得应用可以使用getApplication方法,会从本地缓存中获得应用,本地缓存使用,AtomicReference
localRegionApps 来保存对象,保证线程安全。大量使用了这个。而 fetchRegistry() 方法则是 重新获得server端 register信息,并且更新本地缓存和远端服务端中该服务的状态。 cancel 服务下线 : 类似上面,使用的unregister() 方法。在@PreDestroy shutdown方法中调用,则是在服务关闭或者意外崩溃时,调用注销方法,包括关闭定时任务,更改自身服务状态,再向server端发送最后一次http请求。
replicate:这里我理解为实例信息在服务端间的同步,在eureka-core 中PeerEurekaNodes 类有写,每隔10分钟更新集群节点,每隔一分钟统计最近一分钟内所有Client的续约次数,也就是接收到的心跳次数,以此来作为是否触发服务信息回收的依据之一,EurekaServer每隔60S执行一次服务信息的回收,每隔30S执行一次,更新只读响应缓存,具体代码分析参考下面的文章。
eureka-server peer同步: https://blog.csdn.net/qq_27529917/article/details/80934523
eureka 的相关注意事项和问题
Eureka的几处缓存 (三处缓存,一处延时)
服务启动后最长可能需要2分钟时间才能被其它服务感知到,
eureka-server http缓存,server端相应getApplication 从缓存中拿取eureka-server 的缓存:
对于client的http请求,server端利用两个map做缓存,一个是readWriteCacheMap 一个是readOnlyCacheMap ,先请求readOnlyCacheMap ,若数据与 readWriteCacheMap ,则同步readWriteCacheMap 的数据,若readWriteCacheMap 还是没有,则直接从内存 registry 中获取,readOnlyCacheMap 缓存30s 定时器更新,readWriteCacheMap 缓存180s 定时器更新
eureka-client 对获取到的服务(30s更新)做了本地缓存,默认从本地缓存中读取 ,
负载均衡的ribbon 30s缓存,缓存 eureka-client 服务列表
启动延迟注册,需要等30s 后,发起心跳请求才注册
服务注册信息不会被二次传播
如果Eureka A的peer指向了B, B的peer指向了C,那么当服务向A注册时,B中会有该服务的注册信息,但是C中没有。需要全部写在 peer 里
多网卡环境下的IP选择问题
如果服务部署的机器上安装了多块网卡,它们分别对应IP地址A, B, C,此时:
Eureka会选择IP合法(标准ipv4地址)、索引值最小(eth0, eth1中eth0优先)且不在忽略列表中(可在application.properites中配置忽略哪些网卡)的网卡地址作为服务IP。
这个坑的详细分析见:http://blog.csdn.net/neosmith/article/details/53126924Eureka 的自我保护模式
如果有任何时间,Eureka Serve接收到的续约低于为该值配置的百分比(默认为15分钟内低于85%),则服务器开启自我保护模式,即不再剔除注册列表的信息。
以上问题的答案,在我参考的文章底部有,也是常见的一些问题。
生产上的eureka配置(仅参考)
server端配置
1 | ## 中小规模下,自我保护模式坑比好处多,所以关闭它 |
client 端配置
1 | ## 心跳间隔,5秒 |
主要针对缓存时间的配置,心跳缩短的弊端就是,在自我保护机制阈值的计算时候,按照每分钟计算,如果时间缩短为一倍,失败时候就会多一倍的计算,需要调整阈值,根据实际场景调试等,还有就是server的缓存机制太多层,详情看以下博客。