这篇文章主要讲的是eureka的架构和详细配置。
eureka的基础架构可以分成三个部分:
1.服务注册中心:提供服务的发现和注册的功能。
2.服务提供者:提供服务的应用,可以是spring boot应用,也可以是其他遵循eureka通信机制的应用。
3.消费者应用从服务注册中心获取服务列表,从而使消费者可以知道去何处调用所v需要的服务。既可以使用Ribbon,也可以使用Feign.
如图所示
注明:这张图来自于 spring cloud 微服务实战一书。
“服务提供者”在启动的时候会通过发送REST请求的方式将自己注册到Eureka Server上,同时带上了自身服务的一些元
数据信息。Eureka Server接收到这个REST请求之后,将元数据信息存储在一个双层结构Map中。其中第一层的key是服
务名,第二层的key是具体服务的实例名。
比如在上图中所示,这里的两个服务提供者分别注册到了两个不同的服务注册中心上。
信息分别被两个注册中心所维护。同时,这两个服务注册中心又相互注册,当服务提供
这发送注册请求到一个服务注册中心的时候,它会将该请求转发给集群中相互连接的其他注册中心,从而实现了服务的同步。
在注册完成服务之后,服务提供者会维护一个心跳用来持续告诉eureka server,我还活着。
以防止eureka server将该服务从服务列表中排除出去,这种操作就叫做服务续约。
eureka.instance.lease-renewal-interval-in-seconds参数用于定义服务续约任务的调用
间隔时间,默认为30秒。eureka.instance.lease-expiration-duration-in-
seconds参数用于定义服务失效的时间,默认为90秒。
当我们启动服务消费者的时候,他会发送一个Rest请求到服务注册中i性能,来获取
上面注册的服务清单。为了性能考虑,eureka server会维护一份只读的服务清单返回给客
户端,同时该缓存清单会每隔30秒更新一次。若希望修改缓存清单的更新时间,可以
通过eureka.client.registry-fetch-interval-seconds=30参数进行修改,该参数默认值为30,
单位为秒。
服务消费者在获取服务清单后,通过服务名可以获得具体提供服务的实例名和该实例
的元数据信息。因为有这些服务实例的详细信息,所以客户端可以根据自己的需要决
定具体调用哪个实例,在Ribbon中会默认采用轮询的方式进行调用,从而实现客户端的
负载均衡。
在系统运行过程中必然会面临关闭或重启服务的某个实例的情况,在服务关闭期间,我
们自然不希望客户端会继续调用关闭了的实例。所以在客户端程序中,当服务实例进
行正常的关闭操作时,它会触发一个服务下线的REST请求给Eureka Server,告诉服务注
册中心: "我要下线了”。服务端在接收到请求之后,将该服务状态置为下线(DOWN),并
把该下线事件传播出去。
有些时候,我们的服务实例并不一定会正常下线,可能由于内存溢出、网络故障等原因
使得服务不能正常工作,而服务注册中心并未收到“服务下线”的请求。为了从服务列表
中将这些无法提供服务的实例剔除, Eureka Server在启动的时候会创建一个定时任务,
默认每隔一段时间(默认为60秒)将当前清单中超时(默认为90秒)没有续约的服务剔除
出去。
有的时候会遇到这样的:EMERGENCY! EUREKA MAY BE INCORRECTLY CLAIMING
INSTANCES ARE UP WHEN THEY'RE NOT.RENEWALS ARE LESSER THAN THRE
SHOLD AND HENCE THE INSTANCES ARE NOT BEING EXPIRED JUSTTO BE SAFE.
实际上,该警告就是触发了Eureka Server的自我保护机制。之前我们介绍过,服务注册到
Eureka Server之后,会维护一个心跳连接,告诉Eureka Server自己还活着。Eureka Server
在运行期间,会统计心跳失败的比例在15分钟之内是否低于85%,如果出现低于的情况(在
单机调试的时候很容易满足,实际在生产环境上通常是由于网络不稳定导致), EurekaSer
ver会将当前的实例注册信息保护起来,让这些实例不会过期,尽可能保护这些注册信息。
但是,在这段保护期间内实例若出现问题,那么客户端很容易拿到实际已经不存在的服务
实例,会出现调用失败的情况,所以客户端必须要有容错机制,比如可以使用请求重试、
断路器等机制。
由于本地调试很容易触发注册中心的保护机制,这会使得注册中心维护的服务实例不那么
准确。所以,我们在本地进行开发的时候,可以使用eureka.server.enable-self-preservation
=false参数来关闭保护机制,以确保注册中心可以将不可用的实,例正确剔除。
Eureka客户端的配置主要分为以下两个方面。
·服务注册相关的配置信息,包括服务注册中心的地址、服务获取的间隔时间、可用区域等。
·服务实例相关的配置信息,包括服务实例的名称、IP地址、端口号、健康检查路径等。
在配置文件中指定注册中心,主要通过eureka.client.serviceUrl参数实现。该参数的定义如下所示,它的配置值存储在
HashMap类型中,并且设置有一组默认值,默认值的key为defaultZone、 value为http://localhost:8761/eureka/。、
另外,为了服务注册中心的安全考虑,很多时候我们都会为服务注册中心加入安全校验。这个时候,在配置 serviceUrl时,
需要在value值的URL中加入相应的安全校验信息,比如http: //: @localhost: 1111/eureka.其中,
为安全校验信息的用户名, 为该用户的密码。
参数名 | 说明 | 默认值 |
---|---|---|
enabled | 启动eureka客户端 | true |
registryFetchIntervalSeconds | 从eureka服务端获取注册信息系的时间间隔,默认是秒 | 30 |
instanceInfoReplicationIntervalSeconds | 更新实例信息的变化到eureka服务端的时间间隔,单位是秒 | 40 |
eurekaServiceUrlPollIntervalSeconds | 轮询eureka服务端地址更改的时间间隔,当我们与spring cloud config配合,动态刷新eureka的serviceUrl地址时候需要关注改参数 | 300 |
eurekaServerReadTimeoutSeconds | 读取eureka server信息的超时时间,单位是秒 | 8 |
eurekaServerConnectTimeoutSeconds | 连接eureka server的超时时间,单位是秒 | 5 |
eurekaServerTotalConnections | 从eureka client 到所有eureka server的连接总数 | 200 |
eurekaServerTotalConnectionPerHost | 从eureka client到 eureka server主机端的最大连接总数 | 50 |
eurekaConnectionIdleTimeoutSeconds | eureka server连接的空闲关闭时间,单位是秒 | 30 |
heartbeatExecotorTheadPoolSize | 心跳连接 池的初始化线程数 | 2 |
heartbeatExecotorExponentialBackOffBouund | 心跳超时重试延迟时间的最大乘数值 | 10 |
cacheRefreshExecutorThreadPoolSize | 缓存刷新线程池的初始化线程 | 2 |
cacheRefreshExecutorExponetialBackOffBound | 缓存刷新重试延迟时间的最大乘数值 | 10 |
userDnsForFetchingServiceUrls | 使用DNS来获取eureka服务端的serviceUrl | false |
RegisterWithEureka | 是否要将自身的实例信息注册到eureka服务端 | true |
preferSameZoneEureka | 是否偏好使用处于相同Zone的eureka client | true |
filterOnlyUpInstances | 获取实例时是否过滤,仅保留UP状态的实例 | true |
fetchRegistry | 是否从eureka服务端获取注册信息 | true |
关于服务客户端配置,都是以eureka.instance为前缀。都是通过eurekaInstanceConfigBean进行加载。
什么是元数据:
就是eureka客户端向服务注册中心发送注册请求的时候,用来描述自身服务信息到的对象,
其中包含一些标准化的元数据,比如服务名称,实例名称,实例IP,实例端口等用于服务治理的重要信息。
以及一些用户负载均衡策略或者是其他特殊用途的自定义元数据。
实例名,即InstanceInfo中的instanceId参数,它是区分同一服务中不同实例!的唯一标
识。在Netflix Eureka的原生实现中,实例名采用主机名作为默认值,这样的设置使得在
同一主机上无法启动多个相同的服务实例。所以,在Spring Cloud Eureka的配置中,针
对同一主机中启动多实例的情况,对实例名的默认命名做了更为合理的扩展,它采用了
如下默认规则:
${spring.cloud.client.hostname):$(spring. application.name):$(spring.application.
instance id: ${server.port)}
对于实例名的命名规则,我们可以通过eureka.instance.instanceId参数来进行配置。比
如,在本地进行客户端负载均衡调试时,需要启动同一服务的多个实例,如果我们直接启
动同一个应用必然会产生端口冲突。虽然可以在命令行中指定不同的server.port来启
动,但是这样还是略显麻烦。实际上,我们可以直接通过设置server.port=0或者使用随机
数server.port=${random.int [10000, 19999]}来让Tomcat启动的时候采用随机端口。但
是这个时候我们会发现注册到Eureka Server的实例名都是相同的,这会使得只有一个
服务实例能够正常提供服务。对于这个问题,我们就可以通过设置实例名规则来轻松
解决:
eureka.instance.instanceld-$ (spring. application.name):Srandom.int}}
通过上面的配置,利用应用名加随机数的方式来区分不同的实例,从而实现在同一主机
上,不指定端口就能轻松启动多个实例的效果。