Eureka服务治理由三部分构成
- 注册中心
- 服务提供者
- 服务消费者
注册中心负责维护服务提供者,从而使服务消费者可以从注册中心获取服务列表。
服务治理原理
通过一个图来解释
上图的组成部分:
- 注册中心1和注册中心2互相注册为彼此的一个服务组成了高可用集群
- 将两个服务提供者分别注册到注册中心1和注册中心2
- 让两个服务消费者分别指向注册中心1和注册中心2
Eureka各个组成部分的工作机制
- 首先两个服务提供者分别发送http请求注册到服务中心,注册中心接收到请求后会存储它的服务名和服务具体的实例名。
- 此时,因为现在建立的是一个服务中心集群,所以这两个注册中心都会将他们接收到的注册请求发送给集群中其他的注册中心,从而实现集群服务同步。
- 服务提供者在完成服务注册后,需要定时向注册中心发送‘心跳’,注册中心接收到服务发来的心跳就会知道这个服务当前正常运作。
- 服务消费者现在可以向注册中心获取服务实例了,当它发送一个请求到注册中心,注册中心会返回一份只读的服务清单返回给消费者客户端,消费者可以自己决定具体调用哪个服务实例。
- 如果现在有其中一个服务终止运作,它会告诉服务中心,并且这个下线消息会在集群中传播。
- 服务注册中心每隔60s将持续90s没有发送心跳的服务从自身剔除。
- Eureka Server有一个自我保护机制,如果在15分钟之内一个服务的心跳的正常比例低于85%,注册中心会将其保护起来而不是将其剔除。之后会定时检查这个服务的健康状态,如果没有恢复才会将其提出,否则关闭自我保护机制。
Eureka源码解析
客户端@EnableDiscoveryClient
注解
/**
* Annotation to enable a DiscoveryClient implementation.
* @author Spencer Gibb
*/
/*...*/
public @interface EnableDiscoveryClient {
/**
* If true, the ServiceRegistry will automatically register the local server.
* @return - {@code true} if you want to automatically register.
*/
boolean autoRegister() default true;
}
这个注解的作用是开启DiscoveryClient
实例,注解中有一个参数默认为true
表示自动注册服务。这个类的注释翻译过来大概意思如下:
这是一个用于与注册中心交互的类。
1、向Eureka Server注册实例
2、向Eureka Server续订租约(发送心跳)
3、在关机期间从Eureka服务器取消租约(告诉注册中心,要关机了)
4、查询Eureka Server注册服务/实例列表
这是它的一个构造方法
DiscoveryClient(ApplicationInfoManager applicationInfoManager, EurekaClientConfig config, AbstractDiscoveryClientOptionalArgs args,
Provider<BackupRegistry> backupRegistryProvider, EndpointRandomizer endpointRandomizer)
在这个构造器中,调用了一个initScheduledTasks()
方法
private void initScheduledTasks() {
...
if (clientConfig.shouldRegisterWithEureka()) {
...
// Heartbeat timer
heartbeatTask = new TimedSupervisorTask(
"heartbeat",
scheduler,
heartbeatExecutor,
renewalIntervalInSecs,
TimeUnit.SECONDS,
expBackOffBound,
new HeartbeatThread()
);
...
instanceInfoReplicator.start(clientConfig.getInitialInstanceInfoReplicationIntervalSeconds());
}
...
}
从上面方法中的判断语句中可以看出,instanceInfoReplicator
的作用是开启一个注册服务的任务。这个类中有一个run()
方法,执行了discoveryClient.register()
用来完成客户端的注册。register()
方法:
boolean register() throws Throwable {
...
try {
httpResponse = eurekaTransport.registrationClient.register(instanceInfo);
} catch (Exception e) {
...
}
...
return httpResponse.getStatusCode() == Status.NO_CONTENT.getStatusCode();
}
这个方法的作用就是将这个服务的实例对象传递到服务中心并注册,同时在initScheduledTasks
方法中还有一个定时任务用来发送心跳,这是为了在将客户端注册到Eureka Server之后,开启服务续约。
上面的部分看到了将服务的注册与续约源码,那么消费客户端是如何获取服务的呢,同样的也是包含在initScheduledTasks
方法里:
if (clientConfig.shouldFetchRegistry()) {
int registryFetchIntervalSeconds = clientConfig.getRegistryFetchIntervalSeconds();
...
}
这个代码块中包含定时更新当前客户端服务清单的任务。getRegistryFetchIntervalSeconds()
方法的返回值对应的就是这个定时任务的执行间隔。
以上就是Eureka服务注册、续约、发现的核心代码,最后再来看看注册中心是如何处理注册请求的,注册请求调用的地址是下面这个restful接口:
@POST
@Consumes({"application/json", "application/xml"})
public Response addInstance(InstanceInfo info, @HeaderParam("x-netflix-discovery-replication") String isReplication) {
logger.debug("Registering instance {} (replication={})", info.getId(), isReplication);
if (this.isBlank(info.getId())) {
return Response.status(400).entity("Missing instanceId").build();
} else if (this.isBlank(info.getHostName())) {
...
this.registry.register(info, "true".equals(isReplication));
return ...
}
在这个接口中,info对应的就是传递过来的客户端信息,经过一系列的if
校验,会调用register
方法将服务进行注册:
public void register(InstanceInfo info, boolean isReplication) {
...
super.register(info, leaseDuration, isReplication);
...
}
public void register(InstanceInfo registrant, int leaseDuration, boolean isReplication) {
this.read.lock();
try {
...
ConcurrentHashMap<String, Lease<InstanceInfo>> gNewMap = new ConcurrentHashMap();
gMap = (Map)this.registry.putIfAbsent(registrant.getAppName(), gNewMap);
...
}
可以看到注册中心会将请求注册的服务信息对象存储到一个双层Map
结构中,第一层以服务名作为key
;第二层存储服务的实例信息,并且将实例名作为第二层Map
中的key
。
至此,有关Eureka
服务治理源码的大体执行脉略就叙述完了,更具体的细节流程可以按照这个思路去自己查看。