Eureka详解

前言

上一篇文章Eureka简介及简单使用 我们通过一些简单示例,对Eureka有了一些简单了解。

我们这片文章将深入了解Eureka,详细介绍Eureka的基础架构、节点间的通信机制以及一些进阶配置等。

正文

基础架构

Eureka服务治理基础架构的三个核心要素:

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

注:很多时候,客户端既是服务提供者也是服务调用者。

服务治理机制

要理解服务治理机制,我们先看一下这张图。

upload successful

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

服务提供者

服务提供者主要有服务注册、服务续约、服务下线等行为,我们分别来看下。

  • 服务注册

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

    服务注册时,需要 eureka.client.register-with-eureka参数为true(默认为true),如果设置为false该服务实例将不会进行服务注册操作。

  • 服务续约

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

    服务续约有两个重要参数可以进行配置(Eureka Server的配置文件里),如下:

    1
    2
    3
    4
    # 定义服务续约任务的调用间隔时间,默认30s
    eureka.instance.lease-renewal-interval-in-seconds=30
    # 定义服务失效的时间,默认90s
    eureka.instance.lease-expiration-duration-in-seconds=90
  • 服务下线

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

服务消费者

服务消费者主要有获取服务、服务调用等行为,我们分别来看下。

  • 获取服务

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

    如果想要修改缓存清单更新时间,可以通过下面参数进行修改,该参数默认30s。

    1
    eureka.client.registry-fetch-interval-seconds=30

    获取服务列表是服务消费者的基础,可以通过修改下面参数为false使服务消费者不生效,该值默认为true。

    1
    eureka.client.fetch-registry=true
  • 服务调用

    服务消费者在获取到服务清单后,通过服务名可以获取到提供该服务的实例名和该实例的元数据信息。有了这些服务实例的详细信息,服务消费者可以根据自己的需要选择要调用的实例。如采用轮询等方式,进而实现客户端负载均衡。

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

服务注册中心

服务注册中心主要有服务同步、失效剔除、自我保护等行为,我们分别来看下。

  • 服务同步

    如上图,两个服务提供者分别注册到了两个不同的服务注册中心上,即它们的信息分别被两个服务注册中心维护。由于服务注册中心之间相互注册为服务,当服务提供者发送注册请求到一个注册中心时,注册中心会将该请求转发给集群中相连的其他注册中心,从而实现注册中心之间的服务同步。

    服务同步后,两个服务提供者的服务信息可以在这两台注册中心中任意一台上获取到。

  • 失效剔除

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

  • 自我保护

    我们知道,服务注册到Eureka Server之后,会维护一个心跳连接,告诉Eureka Server自己还活着。Eureka Server在运行期间,会统计心跳的比例在15min之内是否低于85%,如果出现低于的情况(如单机调试、生产环境网络问题等),Eureka Server会将当前的实例信息保护起来,让这些实例不会过期,尽可能保护这些注册信息。但是保护期内如果实例出现问题,服务调用方拿到已经不存在的服务实例,就会出现调用失败的情况,所以服务调用方需要有容错机制,如请求重试、断路器等。

    Eureka Server进入自我保护后,注册中心信息面板上会出现类型下面的红色警告信息:

    1
    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的自我保护机制,这可能导致我们的服务实例不是那么准确。我们可以通过设置下面参数来关闭自我保护机制,该值默认为true,表示开启自我保护机制。

    1
    eureka.server.enable-self-preservation=false

源码分析

我们通过@EnableDiscoveryClient这个注解,可以找到com.netflix.discovery.DiscoveryClient这个类,过程略。

我们来看下这个类,这个类是Netflix开源包中的内容,位于eureka-client包下。

这个类主要有以下功能:

  • 向Eureka Server注册服务实例;
  • 向Eureka Server服务租约;
  • 当服务关闭时,向Eureka Server取消租约;
  • 查询Eureka Server中的服务实例列表。

为保证与Eureka Server交互,我们需要配置一个Eureka Server的URL列表,即之前所说的eureka.client.service-url参数。

关于这个类,我们主要看一下它的initScheduledTasks方法。我们可以看到这个方法在DiscoveryClient的构造器里被调用。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
private void initScheduledTasks() {
if (clientConfig.shouldFetchRegistry()) {
// registry cache refresh timer
int registryFetchIntervalSeconds = clientConfig.getRegistryFetchIntervalSeconds();
int expBackOffBound = clientConfig.getCacheRefreshExecutorExponentialBackOffBound();
scheduler.schedule(
new TimedSupervisorTask(
"cacheRefresh",
scheduler,
cacheRefreshExecutor,
registryFetchIntervalSeconds,
TimeUnit.SECONDS,
expBackOffBound,
new CacheRefreshThread()
),
registryFetchIntervalSeconds, TimeUnit.SECONDS);
}

if (clientConfig.shouldRegisterWithEureka()) {
int renewalIntervalInSecs = instanceInfo.getLeaseInfo().getRenewalIntervalInSecs();
int expBackOffBound = clientConfig.getHeartbeatExecutorExponentialBackOffBound();
logger.info("Starting heartbeat executor: " + "renew interval is: {}", renewalIntervalInSecs);

// Heartbeat timer
scheduler.schedule(
new TimedSupervisorTask(
"heartbeat",
scheduler,
heartbeatExecutor,
renewalIntervalInSecs,
TimeUnit.SECONDS,
expBackOffBound,
new HeartbeatThread()
),
renewalIntervalInSecs, TimeUnit.SECONDS);

// InstanceInfo replicator
instanceInfoReplicator = new InstanceInfoReplicator(
this,
instanceInfo,
clientConfig.getInstanceInfoReplicationIntervalSeconds(),
2); // burstSize

statusChangeListener = new ApplicationInfoManager.StatusChangeListener() {
@Override
public String getId() {
return "statusChangeListener";
}

@Override
public void notify(StatusChangeEvent statusChangeEvent) {
if (InstanceStatus.DOWN == statusChangeEvent.getStatus() ||
InstanceStatus.DOWN == statusChangeEvent.getPreviousStatus()) {
// log at warn level if DOWN was involved
logger.warn("Saw local status change event {}", statusChangeEvent);
} else {
logger.info("Saw local status change event {}", statusChangeEvent);
}
instanceInfoReplicator.onDemandUpdate();
}
};

if (clientConfig.shouldOnDemandUpdateStatusChange()) {
applicationInfoManager.registerStatusChangeListener(statusChangeListener);
}

instanceInfoReplicator.start(clientConfig.getInitialInstanceInfoReplicationIntervalSeconds());
} else {
logger.info("Not registering with Eureka server per configuration");
}
}

可以看到这个方法里面主要有几个定时任务线程,分别被两个if包围,分别是if (clientConfig.shouldFetchRegistry())if (clientConfig.shouldRegisterWithEureka()),这两个参数也是我们上面说的可以配置的,代表着是否开启服务消费eureka.client.fetch-registry=true和是否开启服务注册eureka.client.register-with-eureka=true

对于if (clientConfig.shouldRegisterWithEureka())逻辑里我们可以看到创建了InstanceInfoReplicator的一个实例并启动,该类继承Runable接口,我们找到它的run方法,如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
public void run() {
try {
discoveryClient.refreshInstanceInfo();

Long dirtyTimestamp = instanceInfo.isDirtyWithTime();
if (dirtyTimestamp != null) {
discoveryClient.register();
instanceInfo.unsetIsDirty(dirtyTimestamp);
}
} catch (Throwable t) {
logger.warn("There was a problem with the instance info replicator", t);
} finally {
Future next = scheduler.schedule(this, replicationIntervalSeconds, TimeUnit.SECONDS);
scheduledPeriodicRef.set(next);
}
}

可以看到里面调用了discoveryClient.register()方法,这个方法便是服务注册的主要方法,我们看下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
boolean register() throws Throwable {
logger.info(PREFIX + "{}: registering service...", appPathIdentifier);
EurekaHttpResponse<Void> httpResponse;
try {
httpResponse = eurekaTransport.registrationClient.register(instanceInfo);
} catch (Exception e) {
logger.warn(PREFIX + "{} - registration failed {}", appPathIdentifier, e.getMessage(), e);
throw e;
}
if (logger.isInfoEnabled()) {
logger.info(PREFIX + "{} - registration status: {}", appPathIdentifier, httpResponse.getStatusCode());
}
return httpResponse.getStatusCode() == Status.NO_CONTENT.getStatusCode();
}

可以看到注册操作通过REST进行,发送一个com.netflix.appinfo.InstanceInfo对象,该对象就是注册时客户端给服务端的服务元数据。

继续跟进registrationClient.register方法,找到RestTemplateEurekaHttpClient实现的register方法,代码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
public EurekaHttpResponse<Void> register(InstanceInfo info) {
String urlPath = serviceUrl + "apps/" + info.getAppName();

HttpHeaders headers = new HttpHeaders();
headers.add(HttpHeaders.ACCEPT_ENCODING, "gzip");
headers.add(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_JSON_VALUE);

ResponseEntity<Void> response = restTemplate.exchange(urlPath, HttpMethod.POST,
new HttpEntity<>(info, headers), Void.class);

return anEurekaHttpResponse(response.getStatusCodeValue())
.headers(headersOf(response)).build();
}

可以看到注册是以gzip和POST请求发送的。

com.netflix.eureka.resources.ApplicationsResource类,可以看到服务注册中心是如何处理服务注册请求的,如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
@Path("/{version}/apps")
@Produces({"application/xml", "application/json"})
public class ApplicationsResource {
...
@Path("{appId}")
public ApplicationResource getApplicationResource(
@PathParam("version") String version,
@PathParam("appId") String appId) {
CurrentRequestVersion.set(Version.toEnum(version));
return new ApplicationResource(appId, serverConfig, registry);
}
...
}
1
2
3
4
5
6
7
8
9
@POST
@Consumes({"application/json", "application/xml"})
public Response addInstance(InstanceInfo info,
@HeaderParam(PeerEurekaNode.HEADER_REPLICATION) String isReplication) {
...//部分代码略
registry.register(info, "true".equals(isReplication));
return Response.status(204).build(); // 204 to be backwards compatible
...
}

经过一些校验然后进行注册。

1
2
3
4
5
6
7
8
9
10
11
12
13
@Override
public void register(final InstanceInfo info, final boolean isReplication) {
handleRegistration(info, resolveInstanceLeaseDuration(info), isReplication);
super.register(info, isReplication);
}
private void handleRegistration(InstanceInfo info, int leaseDuration,
boolean isReplication) {
log("register " + info.getAppName() + ", vip " + info.getVIPAddress()
+ ", leaseDuration " + leaseDuration + ", isReplication "
+ isReplication);
publishEvent(new EurekaInstanceRegisteredEvent(this, info, leaseDuration,
isReplication));
}

最终通过publishEvent方法将注册事件传播出去,提供给其他Eureka Server。这一块的过多代码我们不在分析。

再来看下com.netflix.discovery.DiscoveryClient这个类剩下的两个Timer,它们分别调用了new HeartbeatThread()new CacheRefreshThread(),明显知道两个分别是心跳检测和刷新线程,里面也可以看到它们也可以通过参数进行配置一些信息。

我们先来看下HeartbeatThread类,代码如下:

1
2
3
4
5
6
7
8
private class HeartbeatThread implements Runnable {

public void run() {
if (renew()) {
lastSuccessfulHeartbeatTimestamp = System.currentTimeMillis();
}
}
}

可以看到renew方法,这是服务续约的主要方法,我们看下代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
boolean renew() {
EurekaHttpResponse<InstanceInfo> httpResponse;
try {
httpResponse = eurekaTransport.registrationClient.sendHeartBeat(instanceInfo.getAppName(), instanceInfo.getId(), instanceInfo, null);
logger.debug(PREFIX + "{} - Heartbeat status: {}", appPathIdentifier, httpResponse.getStatusCode());
if (httpResponse.getStatusCode() == Status.NOT_FOUND.getStatusCode()) {
REREGISTER_COUNTER.increment();
logger.info(PREFIX + "{} - Re-registering apps/{}", appPathIdentifier, instanceInfo.getAppName());
long timestamp = instanceInfo.setIsDirtyWithTime();
boolean success = register();
if (success) {
instanceInfo.unsetIsDirty(timestamp);
}
return success;
}
return httpResponse.getStatusCode() == Status.OK.getStatusCode();
} catch (Throwable e) {
logger.error(PREFIX + "{} - was unable to send heartbeat!", appPathIdentifier, e);
return false;
}
}

这段代码不再过多介绍。

我们再来看下CacheRefreshThread类,这个类可以获取服务列表并缓存。

1
2
3
4
5
class CacheRefreshThread implements Runnable {
public void run() {
refreshRegistry();
}
}

refreshRegistry方法和fetchRegistry方法代码较多,我只展示了关键部分。

1
2
3
4
5
6
7
8
9
10
@VisibleForTesting
void refreshRegistry() {
...
boolean success = fetchRegistry(remoteRegionsModified);
if (success) {
registrySize = localRegionApps.get().size();
lastSuccessfulRegistryFetchTimestamp = System.currentTimeMillis();
}
...
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
private boolean fetchRegistry(boolean forceFullRegistryFetch) {
...
if (clientConfig.shouldDisableDelta()
|| (!Strings.isNullOrEmpty(clientConfig.getRegistryRefreshSingleVipAddress()))
|| forceFullRegistryFetch
|| (applications == null)
|| (applications.getRegisteredApplications().size() == 0)
|| (applications.getVersion() == -1)) //Client application does not have latest library supporting delta
{
getAndStoreFullRegistry();
} else {
getAndUpdateDelta(applications);
}
...
}

关于fetchRegistry方法,可以看到它会根据是否第一次进行服务获取而发起了不同的REST请求和响应,如果是第一次,会获取全部服务信息getAndStoreFullRegistry方法,否则获取更新部分的信息getAndUpdateDelta

关于Eureka中的Region、Zone和serviceUrls,我们来看下,通过配置的eureka.client.service-url属性,我们可以很方便的找到org.springframework.cloud.netflix.eureka.EurekaClientConfigBean类中的getEurekaServerServiceUrls方法,它的代码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
public List<String> getEurekaServerServiceUrls(String myZone) {
String serviceUrls = this.serviceUrl.get(myZone);
if (serviceUrls == null || serviceUrls.isEmpty()) {
serviceUrls = this.serviceUrl.get(DEFAULT_ZONE);
}
if (!StringUtils.isEmpty(serviceUrls)) {
final String[] serviceUrlsSplit = StringUtils
.commaDelimitedListToStringArray(serviceUrls);
List<String> eurekaServiceUrls = new ArrayList<>(serviceUrlsSplit.length);
for (String eurekaServiceUrl : serviceUrlsSplit) {
if (!endsWithSlash(eurekaServiceUrl)) {
eurekaServiceUrl += "/";
}
eurekaServiceUrls.add(eurekaServiceUrl.trim());
}
return eurekaServiceUrls;
}

return new ArrayList<>();
}

这个方法在com.netflix.discovery.endpoint.EndpointUtilsgetServiceUrlsFromConfig方法会被调用,用于获取Region和Zone。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
public static List<String> getServiceUrlsFromConfig(EurekaClientConfig clientConfig, String instanceZone, boolean preferSameZone) {
List<String> orderedUrls = new ArrayList<String>();
String region = getRegion(clientConfig);
String[] availZones = clientConfig.getAvailabilityZones(clientConfig.getRegion());
if (availZones == null || availZones.length == 0) {
availZones = new String[1];
availZones[0] = DEFAULT_ZONE;
}
logger.debug("The availability zone for the given region {} are {}", region, availZones);
int myZoneOffset = getZoneOffset(instanceZone, preferSameZone, availZones);

List<String> serviceUrls = clientConfig.getEurekaServerServiceUrls(availZones[myZoneOffset]);
if (serviceUrls != null) {
orderedUrls.addAll(serviceUrls);
}
int currentOffset = myZoneOffset == (availZones.length - 1) ? 0 : (myZoneOffset + 1);
while (currentOffset != myZoneOffset) {
serviceUrls = clientConfig.getEurekaServerServiceUrls(availZones[currentOffset]);
if (serviceUrls != null) {
orderedUrls.addAll(serviceUrls);
}
if (currentOffset == (availZones.length - 1)) {
currentOffset = 0;
} else {
currentOffset++;
}
}

if (orderedUrls.size() < 1) {
throw new IllegalArgumentException("DiscoveryClient: invalid serviceUrl specified!");
}
return orderedUrls;
}

从上面代码可以看出,当我们没有为Region配置Zone时,将默认采用defaultZone,若要为应用指定Zone,可以通过eureka.client.availability-zones来进行指定,Zone可以设置多个,通过逗号分隔来进行设置,可以判断Region和Zone是一对多的关系。

设置Zone可以在负载均衡时实现区域亲和特性,例如Ribbon的负载均衡策略会优先访问客户端处于同一个Zone的服务端实例,该Zone域没有服务端可用实例后才会访问其他Zone。我们结合实际部署物理结构,可以有效地设计出针对区域性故障的容错集群。

配置信息

我们上面讲了许多源码,可能很多同学也没有细看,大家更注重的是Eureka的快速上手和使用,这儿,我把Eureka配置的一些参数总结下,来供大家参考,用于解决一些实际问题。

Eureka client 配置项

参数Key说明参数Value类型参数Value默认值注意
eureka.client.register-with-eureka是否向注册中心注册当前实例booleantrue对于高可用的Eureka Server集群,该值需为true,以形成一组相互注册的Server集群
eureka.client.fetch-registry是否需要检索服务信息(从注册中心获取服务信息)booleantrue同上
eureka.client.enabled是否启用Eureka Clientbooleantrue
eureka.client.service-url服务注册地址(注册中心地址)(一般使用默认配置eureka.client.service-url.defaultZone)Map<String,String>该值由于是Map,有一个默认key值defaultZone(常用),也可以自己指定Zone,如下格式eureka.client.service-url.testZone=http://172.30.13.173:8002/eureka/,http://172.30.13.173:8003/eureka/ 参数是服务注册中心地址,多个地址以逗号隔开,其中testZone需要通过eureka.client.availability-zones参数指定
eureka.client.availability-zones可用的Zone列表(一般无需配置)Map<String,String>该值由于是Map,有一个默认key值us-east-1(代表Region),也可以自己指定Region,如下格式eureka.client.availability-zones.testRegion=testZone,testZone1 参数表示可用的Zone,以逗号分开,其中testRegion需要通过eureka.client.region指定
eureka.client.regionEureka Client Region域Stringus-east-1
eureka.client.registry-fetch-interval-seconds从注册中心同步服务信息的间隔时间(单位:秒)int30
eureka.client.prefer-same-zone-eureka是否偏好使用处于相同Zone的Eureka服务端booleantrue
eureka.client.filter-only-up-instances获取实例时是否过滤,仅保留UP状态的实例booleantrue
eureka.client.use-dns-for-fetching-service-urls使用DNS来获取Eureka服务端的serviceUrlbooleanfalse
eureka.client.heartbeat-executor-thread-pool-size心跳连接池的初始化线程数int2
eureka.client.heartbeat-executor-exponential-back-off-bound心跳超时重试延迟时间的最大乘数值int10
eureka.client.cache-refresh-executor-thread-pool-size缓存刷新线程池的初始化线程数int2
eureka.client.cache-refresh-executor-exponential-back-off-bound缓存刷新超时重试延迟时间的最大乘数值int10
eureka.client.healthcheck.enabled是否进行健康状态检测booleantrue
eureka.client.instance-info-replication-interval-seconds更新实例信息的变化到Eureka服务端的间隔时间,单位为秒int30
eureka.client.initial-instance-info-replication-interval-seconds初始化实例信息的变化到Eureka服务端的间隔时间,单位为秒int40
eureka.client.eureka-service-url-poll-interval-seconds轮询Eureka服务地址更改的时间间隔,单位秒int300当我们与Spring Cloud Config配合,动态刷新Eureka的serviceURL地址时需要关注该参数
eureka.client.eureka-server-read-timeout-seconds读取Eureka Server信息超时时间,单位秒int8
eureka.client.eureka-server-connect-timeout-seconds连接Eureka Server的超时时间,单位秒int5
eureka.client.eureka-server-total-connections从Eureka客户端到Eureka服务端的连接总数int200
eureka.client.eureka-server-total-connections-per-host从Eureka客户端到每个Eureka服务端的连接总数int50
eureka.client.eureka-connection-idle-timeout-secondsEureka服务端空闲连接关闭时间,单位秒int30
eureka.client.eureka-server-port获取要用于构造服务url的端口,以便在eureka服务器列表来自DNS时联系eureka服务器String如果返回服务url eurekaServerServiceUrls(字符串),则不需要此信息。当useDnsForFetchingServiceUrls设置为true时,将使用DNS机制,eureka客户机希望DNS以某种方式配置,以便它能够动态获取更改的eureka服务器。更改在运行时生效
eureka.client.eureka-server-d-n-s-name获取要查询的DNS名称,以获得eureka服务器列表String如果通过实现serviceUrls返回服务url,则不需要此信息。当useDnsForFetchingServiceUrls设置为true时,将使用DNS机制,eureka客户机希望DNS以某种方式配置,以便它能够动态获取更改的eureka服务器。更改在运行时生效
eureka.client.eureka-server-u-r-l-context获取要用于构造服务URL的URL上下文,以便在eureka服务器列表来自DNS时通知eureka服务器String如果从eurekaServerServiceUrls返回服务url,则不需要此信息。当useDnsForFetchingServiceUrls设置为true时,将使用DNS机制,eureka客户机希望DNS以某种方式配置,以便它能够动态获取更改的eureka服务器。更改在运行时生效
eureka.client.proxy-host获取eureka服务的代理主机String
eureka.client.proxy-port获取eureka服务的代理端口String
eureka.client.proxy-user-name获取eureka服务的代理用户名String
eureka.client.proxy-password获取eureka服务的代理密码String
eureka.client.g-zip-contenteureka注册表的内容是否要进行压缩booleantrue
eureka.client.allow-redirects服务器是否可以将客户机请求重定向到备份服务器/集群booleanfalse如果设置为false,服务器将直接处理请求,如果设置为true,它可能向客户机发送HTTP重定向,并提供一个新的服务器位置。
eureka.client.should-enforce-registration-at-init客户端是否应在初始化期间强制注册booleanfalse
eureka.client.should-unregister-on-shutdown客户端关闭时是否应显式地从远程服务器注销自己booleantrue
eureka.client.registry-refresh-single-vip-address客户端是否只对单个VIP的注册地址信息感兴趣String
eureka.client.client-data-accept客户端数据接收的EurekaAccept名称Stringfullfull支持LegacyJacksonJson、JacksonJson、XStreamJson、XStreamXml、JacksonXml;compact支持JacksonJsonMini、JacksonXmlMini
eureka.client.fetch-remote-regions-registry将获取eureka注册表信息的区域的逗号分隔列表String配置此参数后,必须为每个availability zones定义regions用于返回availabilityZones参数。否则,将导致客户端启动失败。
eureka.client.decoder-name临时解码器String这是一个临时配置,一旦最新的编解码器稳定下来,就可以删除它(因为只有一个编解码器)
eureka.client.encoder-name临时编码器String这是一个临时配置,一旦最新的编解码器稳定下来,就可以删除它(因为只有一个编解码器)
eureka.client.order该参数可使“CompositeDiscoveryClient”对服务提供者上可用的客户端排序int0
eureka.client.disable-deltaeureka客户端是否应该禁用对delta的抓取,从而每次都是请求获取全部服务信息booleanfalse注意,设置为true后不会增量更新服务注册信息,而是全量更新,delta获取(增量更新)可以极大地减少流量,因为eureka服务器的更改速度通常比获取速度低得多。更改在运行时的下一个服务注册信息获取周期中有效,该周期由registryFetchIntervalSeconds指定
eureka.client.log-delta-diff是否根据服务注册信息记录eureka服务端和eureka客户端之间注册信息的差异booleanfalseEureka客户端尝试仅检索来自Eureka服务器的增量更改,以最小化网络流量。在接收到增量之后,eureka客户端将协调来自服务器的信息,以验证它没有遗漏某些信息。当客户端与服务器通信出现网络问题时,可能会发生协调失败。如果对账失败,eureka客户端将获得完整的服务注册信息。在获得完整的注册表信息时,eureka客户端可以记录客户端和服务器之间的差异,而这个设置控制着这一点。更改在运行时的下一个注册信息获取周期中有效,该周期由registryFetchIntervalSecondsr指定
eureka.client.on-demand-update-status-change是否将客户端状态同步到远程Eureka服务器booleantrue如果设置为true,则通过ApplicationInfoManager将本地状态变化注册/更新到远程eureka服务器
eureka.client.backup-registry-impl获取服务注册信息实现类的名称,该类需要实现BackupRegistry接口,在Eureka客户端启动时只能触发一次,来拿到服务注册信息String对于需要额外弹性来处理注册信息的应用程序来说,这个参数可能会用到,否则服务注册信息我们是无法显式操作的
eureka.client.property-resolverproperty参数转换,可以实现自定义property文件类PropertyResolver需要实现PropertyResolver接口的类
eureka.client.dollar-replacement在序列化/反序列化eureka服务器中的信息时,获取美元符号$的替换字符串String_-
eureka.client.escape-char-replacement在序列化/反序列化eureka服务器中的信息时,获取下划线符号_的替换字符串String__

Eureka instance 配置项

参数Key说明参数Value类型参数Value默认值注意
eureka.instance.hostname主机名String不配置的话将根据操作系统的主机名来获取
eureka.instance.appname服务名Stringunknown默认取spring.application.name的配置值,两者都没有配置为默认值unknown
eureka.instance.instance-id实例IDString一般如下配置eureka.instance.instance-id=${server.address}:${server.port}
eureka.instance.prefer-ip-address是否使用IP定义主机名标识booleanfalse
eureka.instance.lease-renewal-interval-in-secondsEureka客户端向服务端发送心跳的时间间隔,单位秒int30
eureka.instance.lease-expiration-duration-in-secondsEureka服务端在收到最后一次心跳之后等待的时间上限,单位秒int90超过改时间后服务端会将该实例从服务清单上剔除,从而禁止服务调用请求被发送到该实例上
eureka.instance.non-secure-port非安全通信端口号int80
eureka.instance.non-secure-port-enabled是否开启非安全通信端口号booleantrue
eureka.instance.secure-port安全通信端口号int443
eureka.instance.secure-port-enabled是否开启安全通信端口号booleanfalse
eureka.instance.namespace获取用于查找属性的名称空间StringeurekaSpringCloud下该属性不生效,会被忽略
eureka.instance.virtual-host-name获取为此实例定义的虚拟主机名Stringunknown这通常是其他实例使用虚拟主机名查找此实例的方式。如果你需要查找此实例,这个主机名也是完全合格的
eureka.instance.secure-virtual-host-name获取此实例的安全的虚拟主机名Stringunknown
eureka.instance.environment实例所属环境Environment设置后会读取指定环境下的spring.application.name属性
eureka.instance.health-check-url-path实例健康检测的相对路径String/actuator/health
eureka.instance.health-check-url实例健康状态检测的绝对地址URLString如果页面就是在当前服务实例下,只需提供healthCheckUrlPath参数即可,否则可能代理的其他的服务器信息,需要提供绝对地址,如果提供了绝对地址,优先使用绝对地址
eureka.instance.secure-health-check-url实例健康状态检查安全访问地址String
eureka.instance.home-page-url-pathEureka实例展示主页的相对路径String/
eureka.instance.home-page-urlEureka实例展示主页的绝对路径String参照health-check-url
eureka.instance.status-page-url-path实例状态信息监控相对地址String/actuator/info
eureka.instance.status-page-url实例状态信息监控绝对地址String参照health-check-url
eureka.instance.instance-enabled-onit是否使该实例在向eureka注册后是否可以立即进行通信booleanfalse有时,应用程序可能需要进行一些预处理,然后才能处理请求;如不需预处理,可设置为true
eureka.instance.registry.default-open-for-traffic-count该实例,注册服务中心,默认打开的通信数量int1
eureka.instance.registry.expected-number-of-clients-sending-renews单位时间期望的续约连接数量int1此数需要大于0,以确保注册中心可以根据注册数量调整失效清除策略,如果设为0,即使注册成功也不会重置速率阈值
eureka.instance.data-center-info返回部署此实例的数据中心。如果实例部署在AWS中,则此信息用于获取一些特定于AWS的实例信息DataCenterInfoDataCenterInfo.Name.MyOwn
eureka.instance.initial-status实例初始化状态InstanceStatusUP
eureka.instance.ip-address配置实例的IPAdressString此信息仅用于测试,因为与其他实例的通信主要使用getHostName中提供的信息进行
eureka.instance.app-group-name获取要在eureka中注册的应用程序组的名称String
eureka.instance.a-s-g-name用于AWS平台自动扩展的与此实例关联的组名String
eureka.instance.default-address-resolution-order默认的地址解析顺序String[][]
eureka.instance.metadata-map该服务实例的子定义元数据,可以被服务中心接受到Map<String,String>

Eureka Server配置项

参数Key说明参数Value类型参数Value默认值注意
eureka.server.enable-self-preservationEureka Server是否启用自我保护机制booleantrue
eureka.server.response-cache-update-interval-mseureka server刷新readCacheMap的时间,单位毫秒long30000client读取的是readCacheMap,这个时间决定了多久会把readWriteCacheMap的缓存更新到readCacheMap上,默认30s
eureka.server.response-cache-auto-expiration-in-secondseureka server缓存readWriteCacheMap失效时间,单位秒long180这个只有在这个时间过去后缓存才会失效,失效前不会更新,过期后从registry重新读取注册服务信息,registry是一个ConcurrentHashMap。
eureka.server.eviction-interval-timer-in-ms每次主动失效检测间隔,单位毫秒long60000
eureka.server.renewal-percent-threshold阈值因子,一段时间内心跳比例是否低于此值,低于此值开启自我保护double0.85
eureka.server.renewal-threshold-update-interval-ms阈值更新的时间间隔int900000结合阈值因子参数我们可以看到,默认情况下,当15min内心跳比例低于85%,Eureka会进入自我保护模式
eureka.server.rate-limiter-enabled是否启用请求频率限制booleanfalse
eureka.server.rate-limiter-burst-size请求频率大小限制int10
eureka.server.rate-limiter-full-fetch-average-rate请求频率的平均值int100
eureka.server.rate-limiter-registry-fetch-average-rate注册服务、拉去服务列表数据的请求频率的平均值int500
eureka.server.rate-limiter-throttle-standard-clients是否对标准的client进行频率请求限制。如果是false,则只对非标准client进行限制booleanfalse
eureka.server.rate-limiter-privileged-clients设置信任的client listSet
eureka.server.peer-eureka-nodes-update-interval-mseureka节点间间隔多长时间更新一次数据,单位毫秒int600000
eureka.server.peer-eureka-status-refresh-time-interval-mseureka服务状态的相互更新的时间间隔,单位毫秒int30000
eureka.server.peer-node-connect-timeout-mseureka对等节点间连接超时时间,单位毫秒int200
eureka.server.peer-node-connection-idle-timeout-secondseureka对等节点连接后的空闲时间,单位秒int30
eureka.server.peer-node-read-timeout-ms节点间的读数据连接超时时间,单位毫秒int200
eureka.server.peer-node-total-connectionseureka server 节点间连接的总共最大连接数量int1000
eureka.server.peer-node-total-connections-per-hosteureka server 节点间连接的单机最大连接数量int500
eureka.server.enable-replicated-request-compression发送复制数据是否在request中总是压缩booleanfalse
eureka.server.batch-replication指示群集节点之间的复制是否应批处理以提高网络效率booleanfalse
eureka.server.max-elements-in-peer-replication-pool允许备份到备份池的最大复制事件数量int10000这个备份池负责除状态更新的其他事件。可以根据内存大小,超时和复制流量,来设置此值得大小
eureka.server.max-elements-in-status-replication-pool允许备份到状态备份池的最大复制事件数量int10000
eureka.server.max-idle-thread-age-in-minutes-for-peer-replication多个服务中心相互同步信息线程的最大空闲时间,单位minlong15
eureka.server.max-idle-thread-in-minutes-age-for-status-replication状态同步线程的最大空闲时间,单位minlong10
eureka.server.max-threads-for-peer-replication服务注册中心各个instance相互复制数据的最大线程数量int20
eureka.server.max-threads-for-status-replication服务注册中心各个instance相互复制状态数据的最大线程数量int1
eureka.server.max-time-for-replicationinstance之间复制数据的通信时长,单位毫秒int300000
eureka.server.min-available-instances-for-peer-replication正常的对等服务instance最小数量int-1-1表示服务中心为单节点。
eureka.server.min-threads-for-peer-replicationinstance之间相互复制开启的最小线程数量int5
eureka.server.min-threads-for-status-replicationinstance之间用于状态复制,开启的最小线程数量int1
eureka.server.number-of-replication-retriesinstance之间复制数据时可以重试的次数int5
eureka.server.registry-sync-retries在服务节点启动时,eureka尝试获取注册信息的次数int0
eureka.server.registry-sync-retry-wait-ms在服务节点启动时,eureka多次尝试获取注册信息的间隔时间int30000
eureka.server.wait-time-in-ms-when-sync-empty当eureka server启动的时候,不能从对等节点获取instance注册信息的情况,应等待多长时间int300000
eureka.server.disable-delta-for-remote-regions过期数据,是否也提供给远程regionbooleanfalse
eureka.server.disable-transparent-fallback-to-other-region回退到远程区域中的应用程序的旧行为 (如果已配置) 如果本地区域中没有该应用程序的实例, 则将被禁用booleanfalse
eureka.server.g-zip-content-from-remote-region指示在服务器支持的情况下, 是否必须为远程区域压缩从eureka服务器获取的内容booleantrue
eureka.server.remote-region-connect-timeout-ms连接eureka remote note的连接超时时间,单位毫秒int1000
eureka.server.remote-region-app-whitelistremote region 应用白名单Map<String,String>
eureka.server.remote-region-connection-idle-timeout-seconds连接eureka remote note的连接空闲时间,单位秒int30
eureka.server.remote-region-fetch-thread-pool-size执行remote region 获取注册信息的请求线程池大小int20
eureka.server.remote-region-read-timeout-msremote region 从对等eureka加点读取数据的超时时间,单位毫秒int1000
eureka.server.remote-region-registry-fetch-interval从remote region 获取注册信息的时间间隔,单位秒int30
eureka.server.remote-region-total-connectionsremote region 连接eureka节点的总连接数量int1000
eureka.server.remote-region-total-connections-per-hostremote region 连接eureka节点的单机连接数量int50
eureka.server.remote-region-trust-storeremote region抓取注册信息的存储文件,而这个可靠的存储文件需要全限定名来指定String“”
eureka.server.remote-region-trust-store-passwordremote region 储存的文件的密码String“changeit”
eureka.server.remote-region-urls远程region URL地址Stringremote region url.多个逗号隔开
eureka.server.remote-region-urls-with-name远程region URL地址名称Map<String,String>remote region url.多个逗号隔开
eureka.server.a-s-g-cache-expiry-timeout-ms缓存ASG信息的过期时间,单位毫秒int60000
eureka.server.a-s-g-query-timeout-ms查询ASG信息的超时时间,单位毫秒int300
eureka.server.a-s-g-update-interval-ms服务更新ASG信息的频率,单位毫秒int30000
eureka.server.a-w-s-access-idAWS访问IDString
eureka.server.a-w-s-secret-keyAWS安全密钥String
eureka.server.binding-strategyAWS绑定策略AwsBindingStrategyeip
eureka.server.list-auto-scaling-groups-role-name用于从第三方AWS 帐户描述自动扩展分组的角色的名称String
eureka.server.prime-aws-replica-connections是否应该建立连接引导booleantrue
eureka.server.e-i-p-bind-rebind-retries服务端尝试绑定候选EIP的次数int3
eureka.server.e-i-p-binding-retry-interval-ms服务端绑定EIP的时间间隔.如果绑定就检查;如果绑定失效就重新绑定。当且仅当已经绑定的情况,单位毫秒int300000
eureka.server.e-i-p-binding-retry-interval-ms-when-unbound服务端绑定EIP的时间间隔.当且仅当服务为绑定的情况,单位毫秒int60000
eureka.server.route53-bind-rebind-retries服务端尝试绑定route53的次数int3
eureka.server.route53-binding-retry-interval-ms服务端间隔多长时间尝试绑定route53,单位毫秒int300000
eureka.server.route53-domain-t-t-lroute53 domain生存时间int30
eureka.server.delta-retention-timer-interval-in-ms间隔多长时间,清除过期的delta数据int30000
eureka.server.disable-delta过期数据,是否也提供给clientbooleanfalse
eureka.server.log-identity-headerseureka服务端是否记录client的身份headerbooleantrue
eureka.server.retention-time-in-m-s-in-delta-queue缓存增量数据的时间,以便在检索的时候不丢失信息int180000
eureka.server.sync-when-timestamp-differs当时间戳不一致的时候,是否进行同步booleantrue
eureka.server.use-read-only-response-cache是否采用只读缓存策略,只读策略对于缓存的数据不会过期booleantrue
eureka.server.json-codec-namejson的转换的实现类名String
eureka.server.property-resolver属性解析器PropertyResolver
eureka.server.xml-codec-nameeureka server xml的编解码实现名称String

总结

这篇文章通过对Eureka的一些分析,简单了解了Eureka的一些工作原理,对我们使用Eureka还是蛮有帮助的。

参考资料

  1. SpringCloud 微服务实战
  2. Eureka源码



-------------文章结束啦 ~\(≧▽≦)/~ 感谢您的阅读-------------

您的支持就是我创作的动力!

欢迎关注我的其它发布渠道