涉及到一部分概念:

    • 服务注册:Eureka client会通过REST请求向eureka server注册自己的服务,提供自身的元数据,例如:IP,端口,,运行状态,指标的URL等,eureka收到请求之后,会将这些信息保存到concurrentHashMap中。
    • 服务续约:在服务注册后,会维护一个心跳来持续通知Eureka Server,说明服务为可用状态,防止Server端剔除。Eureka client在默认的情况下,以30秒发送一个心跳来进行服务续约。
    • 服务同步:Eureka Server之间会互相进行注册,构建Eureka Server集群,不同Eureka Server之间会进行服务同步,用来保证服务信息的一致性。
    • 获取服务:服务消费者(Eureka Client)在启动的时候,会发送一个REST请求给Eureka Server,获取上面注册的服务清单,并且缓存在Eureka Client本地,默认缓存30秒。同时,为了性能考虑,Eureka Server也会维护一份只读的服务清单缓存,该缓存每隔30秒更新一次。
    • 服务调用:服务消费者在获取到服务清单后,就可以根据清单中的服务列表信息,查找到其他服务的地址,从而进行远程调用。Eureka有Region和Zone的概念,一个Region可以包含多个Zone,在进行服务调用时,优先访问处于同一个Zone中的服务提供者。
    • 服务下线:当Eureka Client需要关闭或重启时,就不希望在这个时间段内再有请求进来,所以,就需要提前先发送REST请求给Eureka Server,告诉Eureka Server自己要下线了,Eureka Server在收到请求后,就会把该服务状态置为下线(DOWN),并把该下线事件传播出去。
    • 服务剔除:有时候,服务实例可能会因为网络故障等原因导致不能提供服务,而此时该实例也没有发送请求给Eureka Server来进行服务下线,所以,还需要有服务剔除的机制。Eureka Server在启动的时候会创建一个定时任务,每隔一段时间(默认60秒),从当前服务清单中把超时没有续约(默认90秒)的服务剔除。
    • 自我保护:既然Eureka Server会定时剔除超时没有续约的服务,那就有可能出现一种场景,网络一段时间内发生了异常,所有的服务都没能够进行续约,Eureka Server就把所有的服务都剔除了,这样显然不太合理。所以,就有了自我保护机制,当短时间内,统计续约失败的比例,如果达到一定阈值,则会触发自我保护的机制,在该机制下,Eureka Server不会剔除任何的微服务,等到正常后,再退出自我保护机制。

    注册信息:
    ApplicationResource------addInstance
    image.png

    1. @POST
    2. @Consumes({"application/json", "application/xml"})
    3. public Response addInstance(InstanceInfo info,
    4. @HeaderParam(PeerEurekaNode.HEADER_REPLICATION) String isReplication) {
    5. logger.debug("Registering instance {} (replication={})", info.getId(), isReplication);
    6. // validate that the instanceinfo contains all the necessary required fields
    7. if (isBlank(info.getId())) {
    8. return Response.status(400).entity("Missing instanceId").build();
    9. } else if (isBlank(info.getHostName())) {
    10. return Response.status(400).entity("Missing hostname").build();
    11. } else if (isBlank(info.getIPAddr())) {
    12. return Response.status(400).entity("Missing ip address").build();
    13. } else if (isBlank(info.getAppName())) {
    14. return Response.status(400).entity("Missing appName").build();
    15. } else if (!appName.equals(info.getAppName())) {
    16. return Response.status(400).entity("Mismatched appName, expecting " + appName + " but was " + info.getAppName()).build();
    17. } else if (info.getDataCenterInfo() == null) {
    18. return Response.status(400).entity("Missing dataCenterInfo").build();
    19. } else if (info.getDataCenterInfo().getName() == null) {
    20. return Response.status(400).entity("Missing dataCenterInfo Name").build();
    21. }
    22. // handle cases where clients may be registering with bad DataCenterInfo with missing data
    23. DataCenterInfo dataCenterInfo = info.getDataCenterInfo();
    24. if (dataCenterInfo instanceof UniqueIdentifier) {
    25. String dataCenterInfoId = ((UniqueIdentifier) dataCenterInfo).getId();
    26. if (isBlank(dataCenterInfoId)) {
    27. boolean experimental = "true".equalsIgnoreCase(serverConfig.getExperimental("registration.validation.dataCenterInfoId"));
    28. if (experimental) {
    29. String entity = "DataCenterInfo of type " + dataCenterInfo.getClass() + " must contain a valid id";
    30. return Response.status(400).entity(entity).build();
    31. } else if (dataCenterInfo instanceof AmazonInfo) {
    32. AmazonInfo amazonInfo = (AmazonInfo) dataCenterInfo;
    33. String effectiveId = amazonInfo.get(AmazonInfo.MetaDataKey.instanceId);
    34. if (effectiveId == null) {
    35. amazonInfo.getMetadata().put(AmazonInfo.MetaDataKey.instanceId.getName(), info.getId());
    36. }
    37. } else {
    38. logger.warn("Registering DataCenterInfo of type {} without an appropriate id", dataCenterInfo.getClass());
    39. }
    40. }
    41. }
    42. registry.register(info, "true".equals(isReplication));
    43. return Response.status(204).build(); // 204 to be backwards compatible
    44. }