理解微服务注册到Eureka Server上的过程(以appname为例)

阅读本文你将了解

  • 微服务注册到Eureka Server上的粗粒度过程
  • 为什么appname是大写。
  • appName的配置:spring.application.name与eureka.instance.appname,及它们的优先级。

Prepare

首先解释一下什么是appname

图中的MICROSERVICE-PROVIDER-USER就是appname。下面我们来分析一下它的详细逻辑。

分析

(1) 首先找到:org.springframework.cloud.netflix.eureka.EurekaClientAutoConfiguration.EurekaClientConfiguration.eurekaApplicationInfoManager(EurekaInstanceConfig)

里面有以下代码:

1
2
3
4
5
6
@Bean
@ConditionalOnMissingBean(value = ApplicationInfoManager.class, search = SearchStrategy.CURRENT)
public ApplicationInfoManager eurekaApplicationInfoManager(EurekaInstanceConfig config) {
InstanceInfo instanceInfo = new InstanceInfoFactory().create(config);
return new ApplicationInfoManager(config, instanceInfo);
}

从中,可以看到,该方法调用了new InstanceInfoFactory().create(config);

(2) 跟进org.springframework.cloud.netflix.eureka.InstanceInfoFactory.create(EurekaInstanceConfig) ,可以看到以下代码:

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
public InstanceInfo create(EurekaInstanceConfig config) {
LeaseInfo.Builder leaseInfoBuilder = LeaseInfo.Builder.newBuilder()
.setRenewalIntervalInSecs(config.getLeaseRenewalIntervalInSeconds())
.setDurationInSecs(config.getLeaseExpirationDurationInSeconds());
// Builder the instance information to be registered with eureka
// server
InstanceInfo.Builder builder = InstanceInfo.Builder.newBuilder();
String namespace = config.getNamespace();
if (!namespace.endsWith(".")) {
namespace = namespace + ".";
}
builder.setNamespace(namespace).setAppName(config.getAppname())
.setInstanceId(config.getInstanceId())
.setAppGroupName(config.getAppGroupName())
.setDataCenterInfo(config.getDataCenterInfo())
.setIPAddr(config.getIpAddress()).setHostName(config.getHostName(false))
.setPort(config.getNonSecurePort())
.enablePort(InstanceInfo.PortType.UNSECURE,
config.isNonSecurePortEnabled())
.setSecurePort(config.getSecurePort())
.enablePort(InstanceInfo.PortType.SECURE, config.getSecurePortEnabled())
.setVIPAddress(config.getVirtualHostName())
.setSecureVIPAddress(config.getSecureVirtualHostName())
.setHomePageUrl(config.getHomePageUrlPath(), config.getHomePageUrl())
.setStatusPageUrl(config.getStatusPageUrlPath(),
config.getStatusPageUrl())
.setHealthCheckUrls(config.getHealthCheckUrlPath(),
config.getHealthCheckUrl(), config.getSecureHealthCheckUrl())
.setASGName(config.getASGName());
// Start off with the STARTING state to avoid traffic
if (!config.isInstanceEnabledOnit()) {
InstanceInfo.InstanceStatus initialStatus = InstanceInfo.InstanceStatus.STARTING;
if (log.isInfoEnabled()) {
log.info("Setting initial instance status as: " + initialStatus);
}
builder.setStatus(initialStatus);
}
else {
if (log.isInfoEnabled()) {
log.info("Setting initial instance status as: "
+ InstanceInfo.InstanceStatus.UP
+ ". This may be too early for the instance to advertise itself as available. "
+ "You would instead want to control this via a healthcheck handler.");
}
}
// Add any user-specific metadata information
for (Map.Entry<String, String> mapEntry : config.getMetadataMap().entrySet()) {
String key = mapEntry.getKey();
String value = mapEntry.getValue();
builder.add(key, value);
}
InstanceInfo instanceInfo = builder.build();
instanceInfo.setLeaseInfo(leaseInfoBuilder.build());
return instanceInfo;
}

代码比较长,但是不难看出,该代码是在通过传入的配置,去构建一个InstanceInfo,也就是某个微服务实例的各种信息。

在代码中,找到以下代码:

1
2
builder.setNamespace(namespace).setAppName(config.getAppname())
.setInstanceId(config.getInstanceId())

我们不妨跟进setAppname中看看。

(3) 查看com.netflix.appinfo.InstanceInfo.Builder.setAppName(String)

1
2
3
4
public Builder setAppName(String appName) {
result.appName = StringCache.intern(appName.toUpperCase(Locale.ROOT));
return this;
}

我们可以看到,是该方法将appName转大写了。

(4) 回到

1
2
builder.setNamespace(namespace).setAppName(config.getAppname())
.setInstanceId(config.getInstanceId())

这段代码,通过DEBUG,我们可以看到代码中的config,是

1
org.springframework.cloud.netflix.eureka.EurekaInstanceConfigBean

(5) 查看该类,我们会发现这正式Eureka Instance的配置类。因此,我们可以使用eureka.instance.appname去配置appName。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
@Data
@ConfigurationProperties("eureka.instance")
public class EurekaInstanceConfigBean implements CloudEurekaInstanceConfig, EnvironmentAware, InitializingBean {
private static final String UNKNOWN = "unknown";
@Getter(AccessLevel.PRIVATE)
@Setter(AccessLevel.PRIVATE)
private HostInfo hostInfo;
@Getter(AccessLevel.PRIVATE)
@Setter(AccessLevel.PRIVATE)
private InetUtils inetUtils;
/**
* Get the name of the application to be registered with eureka.
*/
private String appname = UNKNOWN;

(6) 那么,为什么我们可以通过spring.application.name去配置微服务的appName呢?代码在这里:

org.springframework.cloud.netflix.eureka.EurekaInstanceConfigBean.afterPropertiesSet()

1
2
3
4
5
6
7
8
9
10
@Override
public void afterPropertiesSet() throws Exception {
RelaxedPropertyResolver springPropertyResolver = new RelaxedPropertyResolver(this.environment, "spring.application.");
String springAppName = springPropertyResolver.getProperty("name");
if(StringUtils.hasText(springAppName)) {
setAppname(springAppName);
setVirtualHostName(springAppName);
setSecureVirtualHostName(springAppName);
}
}

经过以上分析,我们可以看到spring.application.name的优先级比eureka.instance.appname高。例如:

1
2
3
4
5
6
7
8
9
10
11
12
server:
port: 8000
spring:
application:
name: microservice-provider-user
eureka:
client:
serviceUrl:
defaultZone: http://localhost:8761/eureka/
instance:
appname: abc
prefer-ip-address: true

两者都配置的时候,注册到Eureka Server上的appname是microservice-provider-user

上一篇