什么是注册中心?
随着微服务的盛行,越来越多的应用,开始拆成一个一个的服务,服务之间相互依赖,那么内部的服务是怎么相互调用的。例如:服务A部署在3个服务器上,3个实例有不同的ip地址。然后服务B依赖服务A,需要调用服务A。
在没有注册中心的情况下。我们可能是服务A前面架设了一个nginx
这样做服务B就可以只访问nginx的地址,然后转发到3个实例。
但是随着docker容器的盛行。ip地址已经不在固定,经常发生变化。这样的话我们就需要部署的时候手工去修改nginx的配置文件,或者使用脚本去更新配置文件,然后reload。
现在我们引入注册中心,只需要将服务注册到注册中心,然后网关等从注册中心获取实例的信息,实现动态路由。
注册中心需要的功能
服务注册
理论上来说 服务在启动的时候,就应该调用eureka server的api(register方法)进行注册。对于java 服务可以使用 eureka client封装的api去调用server的api进行注册对于spring cloud的应用 基于spring boot的自动配置,已经自动帮我们注册服务了
服务下线
Cancel(服务下线);Eureka client 会发送 下线请求给 Eureka server ,Eureka server 就会将 这个 client 的实例从注册列表中剔除;发送下线请求代码如下:DiscoveryManager.getInstance().shutdownComponent()
服务剔除
正常来说服务在关系应用的时候,应该通过钩子或者生命周期的回调方法去调用eureka server的de-register来删除自身的信息。但是为了解决服突然挂掉或其他情况没有及时删除自身的信息,eureka server要求client定时发送心跳(30s),来证明服务还存活,如果超过一定时间没有进行续约(90s),server就会认为该服务不健康自动剔除。
服务租约
Eureka client 会向 Eureka server 每隔30秒发送心跳;续约的目的是通知 Eureka server 自己代表的实例是处于存活状态;如果 Eureka server 90 秒内 未收到 续约心跳,就会将这个实例从服务列表中剔除;官方建议是不要修改续约间隔,原因是服务器要使用这些信息去确认 Eureka server 与 Eureka client 之间的交流是否存在广泛传播的问题;
数据一致性问题
作为注册中心,是不可能是单点的,一定是集群化,那么集群中eureka每个节点是如何进行数据同步的?一般来说:分布式系统中,数据副本的复制方式一般分为主从复制或对等复制(peer to peer)eureka使用的是对等复制主从复制:就好像mysql主从,写都写在主节点,然后由主节点复制给其他从节点,这样主节点的写压力大,但是从节点可以分担读压力对等复制:任何节点都可以接受写请求和读请求.然后副本间进行数据更新;但副本间数据同步时可能产生数据冲突;
Eureka Server也是一个Client,在启动时,通过请求其中一个节点(Server),将自身注册到Server上,并获取注册服务信息;每当Server信息变更后(client发起注册,续约,注销请求),就将信息通知给其他Server,来保持数据同步;在执行同步(复制)操作时,可能会有数据冲突,是通过lastDirtyTimestamp,最近一次变更时间来保证是最新数据;
客户端维护这一个Eureka Server不可用列表和可用列表,当有请求的时候,会从可用列表中获取一个地址,进行请求,失败按顺序换下一个地址为了防止每个Eureka client按配置文件配置的顺序来请求,导致请求server不均衡,client有个定时任务,每隔5分钟重新刷新并随机化Eureka Server的列表。
peer-peer复制不一定成功,所以server之间还可以通过hearbeat来修复
AP优于CP
分布式系统中有3个特性(CAP)Consistency:数据一致性,多个副本的情况下,可能由于网络,机器故障等问题导致,有的副本写入成功,有的失败,造成副本之间数据不一致,满足一致性要求对数据更新操作成功之后,多个副本要保持一致。
Availability:在任何时候对集群进行读写,集群要能正常响应,在一定的延时内完成
Partition Tolerance:分区容忍性,整个集群被分割多个无法相互通信的分区是,集群要仍然可用。
由于网络是不可控的,所以网络分区是客观存在,不可避免的。所以系统需要具备分区容忍性。在这个前提下设计只能在AP和CP之间选择。
zookeeper可以说是cp的 但是也不能说是强一致性的,,因为客户端提交一个写请求,只要过半的节点写成功就返回,这时有个读请求刚好读到没有同步的节点,这个时候就读不到新的数据,如果需要强一致性,需要读取数据的时候先执行一下sync,即先向主节点同步一下数据。。但是在网络分区的情况下,主节点,不在non-quorum分区,这个分区的读写请求都会报错,不可用无法满足Availability的特性而且在选主节点的时候zk集群不可用
eureka选择满足Availability,所以他是ap的,节点之间并不是强一致性的,设计者认为注册中心,保留可用及过期的数据总比丢掉可用的数据好。
自我保护
为了防止,偶尔的网络抖动或者,短暂的网络不可用,造成误判,导致清空服务实例的注册信息,这会严重影响Eureka的可用性(availability),所以引入了自我保护策略
eureka client和server端之前有个租约,client要定时发送心跳维持这个租约,表示自己还活着,eureka通过当注册的实例数,去计算每分钟应该从应用接收到的心跳数,如果1分钟接收的心跳数小于指定的阈值,就禁止定时任务剔除失效的实例。
eureka.server.enable-self-preservation = false阈值可以通过eureka.server.renewal-percent-threshold来设置 。设置小于0.5 即0.49默认是0.85
常见问题
为什么服务下线了,eureka server接口返回的信息还会存在?情况一: 应用异常挂掉,没有告诉eureka server要下线该服务的实例信息,这就需要靠定时清除任务去剔除(evictionTask)
调整evictionTask的调度频率。。默认是1分钟调整为5s
情况二: 应用告诉了eureka server要下线该服务的实例信息,,但是由于eureka server的rest api有 response cache。因此需要等待缓存过期才能呢更新
response cache的问题,可以根据情况禁止readonlyCacheMap或者调整过期时间
情况三: 开启了自我保护,导致不会因过期而被剔除。
为什么服务上线了,eureka client不能及时获取到?针对新服务上线client获取不及时,在测试环境可以适当提供client端拉取server注册信息的频率,比如将默认的30s改成5s
Eureka高可用
多台Eureka相互注册即可