2020/11/带你看Eureka源代码

首先,我们来看一下Eureka的架构图,有一个整体的认识。传送门:https://github.com/Netflix/eureka/wiki/Eureka-at-a-glance

http://static.cyblogs.com/eureka_architecture.png

Eureka核心功能

服务注册

在微服务启动时,首先,服务提供者需要将自己的服务注册到服务注册中心,服务提供者在启动的时候会发送REST请求将自己注册到服务注册中心上,并带上一些元信息。服务注册中心接收到REST请求,会将元信息存储在一个双层Map中,第一层key是服务名,第二层key是具体服务的实例名。

注意:在服务注册时,需要确认一下eureka.client.register-with-eureka=true是否正确,如果为false是禁止向服务注册中心注册的。

服务同步

当服务成功的注册到了注册中心之后,由于注册中心可能是高可用的集群,那么我们的服务可能只注册到了一个集群中的一个注册中心上,被一个注册中心所维护,而不被另外一个注册中心所维护,那么这个时候,我们就需要将这个注册中心的信息同步给集群中其他的注册中心,这就叫服务同步。那么他是如何实现的呢?

由于在集群中,一个注册中心互为其他注册中心的服务,当服务提供者请求到一个服务注册中心后,它会将请求转发到其他服务注册中心,实现注册中心之间的服务同步。

通过服务同步,服务提供者的服务信息可以通过集群中的任何一个服务注册中心获取。

服务续约

在注册完成后。服务提供者会维护一个心跳告诉注册中心服务,心跳间隔大约是30S,防止注册中心剔除服务, 正常情况下,如果Eureka Server90秒没有收到Eureka客户的续约,它会将实例从其注册表中删除。这个过程称为服务续约。

服务获取

当一切的注册相关工作完成后,我们自然要获取服务清单,那么如何获取服务呢? 启动服务消费者后,消费者会发送一个REST请求给服务注册中心,来获取上面注册的服务清单。 而服务注册中心会维护一份只读清单返回给消费者客户端,该缓存清单30s更新一次。

服务调用

消费者获取服务清单后,可以通过服务名获取到具体服务实例与实例的元数据信息。这个时候,我们可以通过Ribbon调用我们的目标服务,默认采用轮询的方式,从而实现负载均衡。

服务下线

当我们需要对服务实例进行正常的关闭操作时,它会触发一个服务下线的REST请求给服务端。注册中心接收到请求后,将该服务状态置为DOWN,并把下线时间传播出去。

失效剔除

有的时候,我们的服务意外挂掉了,那么Eureka如何识别出我们异常的服务,并将其剔除呢?

服务注册中心启动时会创建定时任务,默认60s一次,将当前清单中超时(90s)没有续约的服务剔除。

自我保护

当失效剔除机制引入的时候,就会有一个问题,如果一个地区网络特别不稳定,那么服务可能不会续约,但我们还需要这个服务存在。这个时候,我们怎么解决呢?

还好,Eureka拥有自我保护机制,可以很好的解决这个问题。Eureka Server在运行期间,会统计心跳失败的比例在15分钟之内是否低于85%,如果低于,就会将当前实例注册信息保护起来,同时提示一个警告,一旦进入保护模式,Eureka Server将会尝试保护其服务注册表中的信息,不再删除服务注册表中的数据。也就是不会注销任何微服务。 但是保护机制也有可能会出现问题,导致服务实例不能够被正确剔除。比如在保护期间,实例出现问题,那么客户端很容易拿到实际已经不存在的服务实例,会出现调用失败。

源码细节与验证

1
2
3
4
5
6
7
8
9
10
11
protected void postInit() {
renewsLastMin.start();
if (evictionTaskRef.get() != null) {
evictionTaskRef.get().cancel();
}
evictionTaskRef.set(new EvictionTask());
// 服务剔除,private long evictionIntervalTimerInMs = 60 * 1000;
evictionTimer.schedule(evictionTaskRef.get(),
serverConfig.getEvictionIntervalTimerInMs(),
serverConfig.getEvictionIntervalTimerInMs());
}

参考地址

如果大家喜欢我的文章,可以关注个人订阅号。欢迎随时留言、交流。如果想加入微信群的话一起讨论的话,请加管理员微信号:chengcheng222e,他会拉你们进群。

简栈文化服务订阅号