Spring Cloud Eureka 详解

一、Spring Cloud Eureka简介       

       Spring Cloud Eureka,使用Netflix Eureka 来实现服务的注册与发现, 他既包含了服务端组件,也包含了客户端组件,并且服务端与客户端均采用Java编写,所以Eureka主要适用于通过Java编写的分布式系统,或是与JVM兼容语言构建的系统。但是,由于Eureka的服务端的服务治理机制提供了完备的Restful API,所以它也支持将非Java语言构建的微服务应用纳入Eureka的服务治理体系中来。只是在使用其他语言平台的时候,需要自己来实现Eureka的客户端程序。不过庆幸的是,在目前几个较为流行的开发平台上,都已经有了一些针对Eureka注册中心的客户端实现框架,比如.NET平台的Steeltoe,Node.js的eureka-js-client等。

       Eureka服务端,我们也称为服务注册中心,它同其他服务注册中心一样,支持高可用配置。它依托于强一致性提供良好的服务实例可用性,可以应对多种不同的故障场景。如果Eureka以集群模式部署,当集群中有分片出现故障时,那么Eureka就转入自我保护模式。它允许在分片故障的期间继续提供服务的发现和注册,当故障分片恢复运行时集群中的其他分片会把它们的状态再次同步回来。以AWS上的实践为例,Netflix推荐每个可用的区域运行一个Eureka服务端,通过它来形成集群。不同可用区域的服务注册中心通过异步模式互相复制各自的状态,这意味着在任意给定的时间点每个实例关于所有服务的状态是有细微差别的。

       Eureka客服端,主要处理服务的注册与发现。客户端服务通过注解和参数配置的方式,嵌入在客户端应用程序的代码中,在应用程序运行时,Eureka客户端向注册中心注册自身提供的服务并定期性的发送心跳来更新它的服务租约。同时,它也能从服务端查询当前注册的服务信息并把它们缓存到本地并周期性的刷新服务状态。

二、Eureka服务治理体系

      Eureka服务治理体系三个核心角色:服务注册中心、服务提供者以及服务消费者。

基础架构

  • 服务注册中心:Eureka提供的服务端,提供服务注册与发现的功能。
  • 服务提供者:提供服务的应用,可以是SpringBoot应用,也可以是其他技术平台且遵循Eureka通信机制的应用。它将自己提供的服务注册到Eureka,以供其他应用发现。
  • 服务消费者:消费者应用从服务注册中心获取服务列表,从而使消费者知道可以从何处调用其所需要的服务。

    很多时候客户端即使服务提供者也是服务消费者,服务注册中心就是不同服务之间相互调用的纽带。

服务治理机制

    我们将通过下图,来展示Eureka服务治理体系是如何运行起来的。

Eureka服务治理体系
  • ”服务注册中心-1“和“服务注册中心-2”,它们相互注册成了高可用集群。
  • “服务提供者”启动了两个实例,一个注册到“服务注册中心-1”上,另外一个注册到“服务注册中心-2”上。
  • 还要两个“服务消费者”,它们分别指向了一个注册中心。

   根据上面的结构,下面我们来详细了解一下,从服务注册开始到服务调用,及各个元素所涉及的一些重要通信行为。

   服务提供者

   服务注册

   “服务提供者”在启动的时候会通过发送REST请求的方式将自己注册到Eureka Server上,同时带上了自身服务的一些元数据信息。Eureka Server接收到这个REST请求之后,将元数据存储在一个双层结构Map中,其中第一层的key是服务名,第二层的key是具体服务的实例名。

   在服务注册时,需要确认一下 eureka.client.register-with-eureka=true参数是否正确,该值默认为true。若设置为false将不会启动注册操作。

   服务同步

   如图架构所示,这里的两个服务提供者分别注册到了两个不同的服务注册中心上,也就是说,它们的信息分别被两个服务注册中心所维护。此时,由于服务注册中心之间因为相互注册为服务,当服务提供者发送注册请求到一个服务注册中心时,它会将该请求转发给集群中相连的其他注册中心,从而实现注册中心之间的服务同步,通过服务同步,两个服务提供者的服务信息就可以通过这两台服务注册中心中的任意一台获取到。

   服务续约

   在注册完服务之后,服务提供者会维护一个心跳用来持续告诉Eureka Server:“我还活着”,以防止Eureka Server 的“剔除任务”将该服务实例从服务列表中排除出去,我们称该操作为服务续约(Renew)。

   关于服务续约的两个重要属性,我们可以关注并根据需要来进行调整。

   eureka.instance.lease-renewal-interval-in-seconds=30

   eureka.instance.lease-expiration-duration-in-seconds=90

  eureka.instance.lease-renewal-interval-in-seconds参数用于定义服务续约任务调用间隔时间,默认为30秒。eureka.instance.lease-expiration-duration-in-seconds参数用于定义服务失效的时间,默认为90秒。

    服务消费者

    获取服务

    到这里,在服务注册中心已经注册了一个服务,并且该服务有两个实例。当我们启动服务消费者时,它会发送一个REST请求给服务注册中心,来获取上面注册的服务清单。为了性能考虑,Eureka Server 会维护一份只读的服务清单来返回给客户端,同时该缓存清单会每隔30秒更新一次。

  获取服务是服务消费者的基础,所以要确保 eureka-client-fetch-registery=true 参数没有被修改成false,该值默认为 true。若想修改缓存清单的更新时间,可以通过 eureka-client.registry-fetch-interval-seconds=30 参数来进行修改,该值默认为30,单位为秒。

    服务调用

    服务消费者在获取服务清单后,通过服务名可以获得具体提供服务的实例名和该实例的元数据信息。因为有这些服务实例的详细信息,所以客户端可以根据自己的需要决定具体需要调用的实例,在Ribbon中会默认采用轮询的方式进行调用,从而实现客户端的负载均衡。

    对于访问实例的选择,Eureka中有Region和Zone的概念,一个Region中可以包含多个Zone,每个服务客户端需要被注册到一个Zone中,所以每个客户端对应一个Region和一个Zone。在进行服务调用的时候,优先访问同处一个Zone中的服务提供方,若访问不到,就访问其他Zone。

    服务下线

    在系统运行过程中必然会面临关闭或重启服务的某个实例的情况,在服务关闭期间,我们自然不希望客户端会继续调用关闭了的实例。所以在客户端程序中,当服务实例进行正常的关闭操作时,它会触发一个服务下线的REST请求给 Eureka Server,告诉服务注册中心:“我要下线了”。服务端在接收到请求之后,将该服务状态设置为下线(DOWN),并把该下线事件传播出去。

    服务注册中心

    失效剔除

    有些时候,我们的服务实例并不一定会正常下线,可能由于内存溢出、网络故障等原因使得服务不能正常工作,而服务注册中心并未收到“服务下线”的请求。为了从服务列表中将这些无法提供服务的实例剔除,Eureka Server在启动的时候会创建一个定时任务,默认每隔一段时间(默认为60秒)将当前清单中超时(默认为90秒)抹油续约的服务剔除出去。

    自我保护

    当我们在本地调试基于Eureka的程序时,基本上都会碰到这样的一个问题,在服务注册中心的信息面板中出现类似下面的红色警告信息:

     实际上,该警告就是触发了Eureka Server的自我保护机制。之前介绍过,服务注册到Eureka Server之后,会维护一个心跳连接,告诉Eureka Server 自己还活着。Eureka Server 在运行期间,会统计心跳失败的比例在15分钟之内低于85%,如果出现低于的情况,Eureka Server 会将当前的实例信息保护起来,让这些实例不会过期,尽可能保护这些注册信息。但是,在保护期间内实例若出现问题,那么客户端很容易拿到实际已经不存在的服务实例,会出现调用失败的情况,所以客户端必须要有容错机制,比如可以使用请求重试、断路器等机制。

  由于在本地调试很容易触发注册中心的保护机制,使得注册中心维护的服务实例不那么准确。可以在本地进行开发时,使用 eureka-server.enable-self-preservation=false 参数来关闭保护机制,确保注册中心将不可用的实例正确剔除。

猜你喜欢

转载自blog.csdn.net/weixin_41400063/article/details/81592757