个人专题目录
一、Eureka Server的几个对外接口实现
1 Register
首先来看Register(服务注册),这个接口会在Service Provider启动时被调用来实现服务注册。同时,当Service Provider的服务状态发生变化时(如自身检测认为Down的时候),也会调用来更新服务状态。
接口实现比较简单,如下图所示。
ApplicationResource类接收Http服务请求,调用PeerAwareInstanceRegistryImpl的register方法
PeerAwareInstanceRegistryImpl完成服务注册后,调用replicateToPeers向其它Eureka Server节点(Peer)做状态同步(异步操作)
注册的服务列表保存在一个嵌套的hash map中:
l 第一层hash map的key是app name,也就是应用名字
l 第二层hash map的key是instance name,也就是实例名字
Hash map定义如下:
private final ConcurrentHashMap
2 Renew
Renew(服务续约)操作由Service Provider定期调用,类似于heartbeat。主要是用来告诉Eureka Server Service Provider还活着,避免服务被剔除掉。接口实现如下图所示。
可以看到,接口实现方式和register基本一致:首先更新自身状态,再同步到其它Peer。
3 Cancel
Cancel(服务下线)一般在Service Provider shut down的时候调用,用来把自身的服务从Eureka Server中删除,以防客户端调用不存在的服务。接口实现如下图所示。
4 Fetch Registries
Fetch Registries由Service Consumer调用,用来获取Eureka Server上注册的服务。
为了提高性能,服务列表在Eureka Server会缓存一份,同时每30秒更新一次。
5 Eviction
Eviction(失效服务剔除)用来定期(默认为每60秒)在Eureka Server检测失效的服务,检测标准就是超过一定时间没有Renew的服务。
默认失效时间为90秒,也就是如果有服务超过90秒没有向Eureka Server发起Renew请求的话,就会被当做失效服务剔除掉。
失效时间可以通过eureka.instance.leaseExpirationDurationInSeconds进行配置,定期扫描时间可以通过eureka.server.evictionIntervalTimerInMs进行配置。
接口实现逻辑见下图:
6 How Peer Replicates
在前面的Register、Renew、Cancel接口实现中,我们看到了都会有replicateToPeers操作,这个就是用来做Peer之间的状态同步。
通过这种方式,Service Provider只需要通知到任意一个Eureka Server后就能保证状态会在所有的Eureka Server中得到更新。
具体实现方式其实很简单,就是接收到Service Provider请求的Eureka Server,把请求再次转发到其它的Eureka Server,调用同样的接口,传入同样的参数,除了会在header中标记isReplication=true,从而避免重复的replicate。
Peer之间的状态是采用异步的方式同步的,所以不保证节点间的状态一定是一致的,不过基本能保证最终状态是一致的。
结合服务发现的场景,实际上也并不需要节点间的状态强一致。在一段时间内(比如30秒),节点A比节点B多一个服务实例或少一个服务实例,在业务上也是完全可以接受的(Service Consumer侧一般也会实现错误重试和负载均衡机制)。
所以按照CAP理论,Eureka的选择就是放弃C,选择AP。
7 How Peer Nodes are Discovered
Eureka Server是怎么知道有多少Peer的呢?
Eureka Server在启动后会调用EurekaClientConfig.getEurekaServerServiceUrls来获取所有的Peer节点,并且会定期更新。定期更新频率可以通过eureka.server.peerEurekaNodesUpdateIntervalMs配置。
这个方法的默认实现是从配置文件读取,所以如果Eureka Server节点相对固定的话,可以通过在配置文件中配置来实现。
如果希望能更灵活的控制Eureka Server节点,比如动态扩容/缩容,那么可以override getEurekaServerServiceUrls方法,提供自己的实现,比如可以通过数据库读取Eureka Server列表。
具体实现如下图所示:
8 How New Peer Initializes
最后再来看一下一个新的Eureka Server节点加进来,或者Eureka Server重启后,如何来做初始化,从而能够正常提供服务。
具体实现如下图所示,简而言之就是启动时把自己当做是Service Consumer从其它Peer Eureka获取所有服务的注册信息。然后对每个服务,在自己这里执行Register,isReplication=true,从而完成初始化。
二、Service Provider实现细节
现在来看下Service Provider的实现细节,主要就是Register、Renew、Cancel这3个操作。
1 Register
Service Provider要对外提供服务,一个很重要的步骤就是把自己注册到Eureka Server上。
这部分的实现比较简单,只需要在启动时和实例状态变化时调用Eureka Server的接口注册即可。需要注意的是,需要确保配置eureka.client.registerWithEureka=true。
2 Renew
Renew操作会在Service Provider端定期发起,用来通知Eureka Server自己还活着。 这里有两个比较重要的配置需要注意一下:
instance.leaseRenewalIntervalInSeconds
Renew频率。默认是30秒,也就是每30秒会向Eureka Server发起Renew操作。
instance.leaseExpirationDurationInSeconds
服务失效时间。默认是90秒,也就是如果Eureka Server在90秒内没有接收到来自Service Provider的Renew操作,就会把Service Provider剔除。
具体实现如下:
3 Cancel
在Service Provider服务shut down的时候,需要及时通知Eureka Server把自己剔除,从而避免客户端调用已经下线的服务。
逻辑本身比较简单,通过对方法标记@PreDestroy,从而在服务shut down的时候会被触发。
4 How Eureka Servers are Discovered
Service Provider是怎么知道Eureka Server的地址呢?
其实这部分的主体逻辑和 7、How Peer Nodes are Discovered几乎是一样的。
也是默认从配置文件读取,如果需要更灵活的控制,可以通过override getEurekaServerServiceUrls方法来提供自己的实现。定期更新频率可以通过eureka.client.eurekaServiceUrlPollIntervalSeconds配置。
‘
三、Service Consumer实现细节
Service Consumer这块的实现相对就简单一些,因为它只涉及到从Eureka Server获取服务列表和更新服务列表。
1 Fetch Service Registries
Service Consumer在启动时会从Eureka Server获取所有服务列表,并在本地缓存。需要注意的是,需要确保配置eureka.client.shouldFetchRegistry=true。
2 Update Service Registries
由于在本地有一份缓存,所以需要定期更新,定期更新频率可以通过eureka.client.registryFetchIntervalSeconds配置。
3 How Eureka Servers are Discovered
Service Consumer和Service Provider一样,也有一个如何知道Eureka Server地址的问题。
其实由于Service Consumer和Service Provider本质上是同一个Eureka客户端,所以这部分逻辑是一样的。