一、基础架构
在上一节当中,我们通过一个简单的服务注册与发现示例,构建了Eureka服务治理体系中的三个核心角色:服务注册中心、服务提供者、服务消费者。通过以上章节,相信我们已经对Eureka的服务治理有了一些初步的认识。
至此,我们已经学会了如何构建服务注册中心(包括单节点和高可用部署),也知道了如何构建Eureka的服务提供者和消费者。但是,在实际开发中这些是远远不够的。在此,我们将更深次的学习Eureka的基础架构、节点间的通信和一些进阶配置!
在上述示例中,我们讲的示例虽然简单,但是包含了整个Eureka服务治理基础架构的三个核心要素。
- 服务注册中心:Eureka提供的服务端,提供服务的注册与发现功能,也就是我们的eureka-server
- 服务提供者:提供服务的应用。他将自己提供的服务注册到Eureka注册中心里,以供其他应用发现、使用,也就是我们的eureka-service-hello
- 服务消费者:消费者从注册中心获取服务列表,从而使消费者知道在何处调用其所需要的服务,上述中使用了Ribbon来实现消费服务,后续会介绍Feign的消费方式
二、服务提供者
服务提供者在启动的时候会通过发送REST请求的方式将自己注册到Eureka Server上,同时带上了自身服务的一些元数据信息。Eureka Server将元数据信息存储在一个双层Map中,其中第一层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-registry=true 参数没有被修改成false。
若希望
修改缓存清单的更新时间
,可以通过:
eureka.client.registry-fetch-interval-seconds=30
该参数默认值为30,单位为秒!
服务调用
:服务消费者获取服务清单后,通过服务名可以获得具体的提供服务的实例名和该实例的元数据信息。因为有这些服务实例的详情信息,所以客户端可以根据自己的需要决定具体调用哪个实例。在
Ribbon中会默认采用轮询的方式进行调用
,从而实现客户端的负载均衡!
四、服务注册中心
失效剔除
:有些时候,我们服务实例并不一定会正常下线,可以一些特殊原因而不能正常工作(内存溢出、网络故障),而注册中心并未收到“服务下线”的请求。为了从服务列表中将这些无法提供服务的实例剔除,Eureka Server在启动的时候会创建一个定时任务,默认每隔一段时间(默认时间60秒)将默认清单中超时(默认时间90秒--
eureka.instance.lease-expiration-duration-in-seconds 定义的时间
)没有续约的服务剔除出去。
自我保护:在我们本地调试基于Eureka 程序时,基本上都会碰到这样一个问题,在服务注册中心的信息面板中会出现类似下面的红色警告信息:
EMERGENCY! EUREKA MAY BE INCORRECTLY CLAIMING INSTANCES ARE UP WHEN THEY'RE NOT. RENEWALS ARE LESSER THAN THRESHOLD AND HENCE THE INSTANCES ARE NOT BEING EXPIRED JUST TO BE SAFE.
实际上,这种警告就是触发了Eureka Server的自我保护机制。如果Eureka Server在一定时间内(默认90秒)没有接收到某个微服务实例的心跳,Eureka Server将会移除该实例。但是当网络分区故障发生时,微服务与Eureka Server之间无法正常通信,而微服务本身是正常运行的,此时不应该移除这个微服务,所以引入了自我保护机制。
自我保护机制的工作机制是如果在15分钟内超过85%的客户端节点都没有正常的心跳,那么Eureka就认为客户端与注册中心出现了网络故障,Eureka Server自动进入自我保护机制,此时会出现以下几种情况:
1、Eureka Server不再从注册列表中移除因为长时间没收到心跳而应该过期的服务。
2、Eureka Server仍然能够接受新服务的注册和查询请求,但是不会被同步到其它节点上,保证当前节点依然可用。
3、当网络稳定时,当前Eureka Server新的注册信息会被同步到其它节点中。
因此Eureka Server可以很好的应对因网络故障导致部分节点失联的情况,而不会像ZK那样如果有一半不可用的情况会导致整个集群不可用而变成瘫痪。
自我保护开关
Eureka自我保护机制,通过配置
eureka.server.enable-self-preservation
来true打开/false禁用自我保护机制,默认打开状态,建议生产环境打开此配置。
五:配置Region和Zone
Region和Zone就相当于大区和机房,一个Region(大区)可以有很多的Zone(机房)。在Spring Cloud中,服务消费者会优先查找在同一个Zone的服务,之后在去查找其他的服务。如果该项配置使用的好,那么项目请求的响应时间将大大缩短!
六、注册中心的安全机制
为了注册中心的安全考虑,很多时候我们都会为注册中心加入安全校验!
服务端:
1、Eureka Server pom.xml 中加入依赖
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-security</artifactId>
</dependency>
2.在application.properties加入认证的用户名和密
security.user.name=你的用户名
security.user.password=你的密码
3.在访问路径上加入用户名密码
eureka.client.serviceUrl.defaultZone=http://${security.user.name}:${security.user.password}@127.0.0.1:${server.port}/eureka/
client端:
这个时候,在配置service-url时,需要在value值的URL中加入相应的安全检验信息,比如
http://<username>:<password>@localhost:8080/eureka/
。其中
<username>为安全检验名,<password>为该用户的密码!
七、Eureka 注意要点解释
以下摘抄于:http://fengyilin.iteye.com/blog/2367265 fengyilin 的博客,如有侵权,请联系我删除!
基础架构:
![](https://i-blog.csdnimg.cn/blog_migrate/3457b64bfca2028679782373d5c41a4c.png)
1.Eureka 包含server和client两个基本组件,以下摘自Netflix的描述
引用
Eureka 是一种基于REST服务,主要用在AWS云中,用来提供负载均衡和中间层服务的故障转移,我们把这种REST服务称作Eureka server。Eureka 还包含一个基于Java的客户端,可以用来更好的server交互,Eureka client包含一个內建的基于round-robin(轮询)的负载均衡策略。
2.每一个Eureka client应用被称作一个instance。Eureka client应用和Eureka client也有着细微的差别,前者指的是我们自己的应用程序,后者代表的是框架提供的一个组件。(在maven工程中,自己的应用程序需要依赖Eureka client相关的jar)
3.Eureka 具有很灵活的动态配置特性。既有用来设置初始配置的属性,也有用来周期性的检查这些配置是否有变化的配置。其中的大多数配置都可以在运行时修改,并在下一个刷新周期中起作用。例如:Eureka client用来注册到Eureka server的URL可以动态改变,并在5分钟后被识别(eureka.client.eurekaServiceUrlPollIntervalSeconds配置)。所有的配置属性如下:
Eureka server:
org.springframework.cloud.netflix.eureka.server.EurekaServerConfigBean,此类实现了com.netflix.eureka.EurekaServerConfig.所有的配置属性以eureka.server开头
Eureka client:
org.springframework.cloud.netflix.eureka.EurekaClientConfigBean,此类实现了com.netflix.discovery.EurekaClientConfig,所有的配置属性以eureka.client开头
Eureka instance:
org.springframework.cloud.netflix.eureka.EurekaInstanceConfigBean,此类间接实现了com.netflix.appinfo.EurekaInstanceConfig,所有的配置属性以eureka.instance开头。
二、Eureka Instance 和Eureka client主要配置
1、在Eureka中,一个 instance 通过一个 eureka.instance.instanceId 来唯一标识,如果这个值没有设置,就采用eureka.instance.metadataMap.instanceId 来代替。instance 之间通过 eureka.instance.appName 来彼此访问,在spring cloud中默认值是 spring.application.name ,如果没有设置则为 UNKNOWN。在实际使用中 spring.application.name 不可或缺,因为相同名字的应用会被Eureka合并成一个群集(Eureka Server会以双层Map进行存储信息,第一层Map的key 就是服务名, 第二层 Map的 key 就是 instanceId)。eureka.instance.instanceId 也可以不设置,直接使用缺省值(client.hostname:application.name:port)
,同一个appName下InstanceId不能相同。属性eureka.instance.virtualHostName目前在spring cloud中目前没有用,默认值是appName或者UNKNOWN。
2、如果 eureka.client.registerWithEureka 设置成true(默认值true),应用启动时,会利用指定的eureka.client.serviceUrl.defaultZone(也可以自定义Zone) 注册到对应的Eureka server中。之后每隔30s(通过eureka.instance.leaseRenewalIntervalInSeconds来配置)向Eureka server发送一次心跳,如果Eureka server在90s(通过eureka.instance.leaseExpirationDurationInSeconds配置)内没有收到某个instance发来的心跳就会把这个instance从注册中心中移走。发送心跳的操作是一个异步任务,如果发送失败,则以2的指数形式延长重试的时间,直到达到eureka.instance.leaseRenewalIntervalInSeconds * eureka.client.heartbeatExecutorExponentialBackOffBound这个上限,之后一直以这个上限值作为重试间隔,直至重新连接到Eureka server,并且重新尝试连接到Eureka server的次数是不受限制的。
3、在Eureka server中每一个instance都由一个包含大量这个 instance 信息的
com.netflix.appinfo.InstanceInfo
标识,client向Eureka server 发送心跳和更新注册信息是不相同的,InstanceInfo 也以固定的频率发送到Eureka server,这些信息在Eureka client启动后的40s(通过 eureka.client.initialInstanceInfoReplicationIntervalSeconds 配置)首次发送,之后每隔30s(通过eureka.client.instanceInfoReplicationIntervalSeconds配置)发送一次。
4、如果 eureka.client.fetchRegistry 设置成true(默认值true),Eureka client在启动时会从Eureka server获取注册信息并缓存到本地,之后只会增量获取信息(可以把 eureka.client.shouldDisableDelta 设置成false来强制每次都全量获取)。获取注册信息的操作也是一个异步任务,每隔30秒执行一次(通过 eureka.client.registryFetchIntervalSeconds 配置),如果操作失败,也是以2的指数形式延长重试时间,直到达到
eureka.client.registryFetchIntervalSeconds * eureka.client.cacheRefreshExecutorExponentialBackOffBound 这个上限,之后一直以这个上限值作为重试间隔,直至重新获取到注册信息,并且重新尝试获取注册信息的次数是不受限制的。
这些任务都是在com.netflix.discovery.DiscoveryClient中启动,spring cloud用org.springframework.cloud.netflix.eureka.CloudEurekaClient对这个类进行了扩展。
更详尽的知识请参考:
http://fengyilin.iteye.com/blog/2367265