SpringCloud之服务注册原理剖析
文章目录
一、微服务注册流程
1、注册流程图
对注册流程的分析:
- 微服务实例首先从配置文件中提取基本的配置信息,构成
EurekaClientConfig clientConfig
- 从
clientConfig
中获取相关的注册信息:- region、zone、serviceUrls 构造服务中心列表
- RenewalIntervalInSecs、HeartBeat时间间隔、RefreshCache时间间隔等
- 依据构造的服务中心列表,并且依据续租间隔、心跳间隔、刷新服务列表缓存间隔,初始化一系列基于线程框架的定时任务,这些任务创建基于Http RestAPI的异步请求,首先注册到服务中心,其余的定时发送给Eureka Server,并获取响应信息。
以上就是微服务实例向服务中心注册的流程。
2、简单的eureka server的搭建
以下将搭建一个拥有:服务中心(eureka server)、微服务实例(provider)的简单demo。并且依据这个demo,来剖析微服务注册的底层实现。
2.1、eureka server的搭建
- 添加依赖
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-eureka-server</artifactId>
</dependency>
- 启动类的注解配置
/**
* @author linxu
*/
@SpringBootApplication
@EnableAutoConfiguration(exclude = {DataSourceAutoConfiguration.class,RedisAutoConfiguration.class})
//成为服务注册中心
@EnableEurekaServer
public class EurekaServerApplication {
public static void main(String[] args) {
SpringApplication.run(EurekaServerApplication.class, args);
}
}
- 配置文件
server:
port: 8088
tomcat:
max-threads: 200
eureka:
instance:
hostname: localhost
status-page-url-path: /actuator/info
health-check-url-path: /actuator/health
prefer-ip-address: false
client:
#不获取注册信息,因为是服务中心
fetch-registry: false
#不注册到eureka server,单机不需要
register-with-eureka: true
service-url:
defaultZone: http://${eureka.instance.hostname}:${server.port}/eureka/
server:
#关闭安全机制
enable-self-preservation: false
#剔除实例的检测时间为5S
eviction-interval-timer-in-ms: 5000
spring:
application:
name: eureka-Server
通过上述配置,就完成了最为精简版的eureka server。
2.2、client的搭建
- 添加依赖
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
</dependency>
- 启动类的注解配置
/**
* @author linxu
*/
@SpringBootApplication
@EnableEurekaClient
//注册discoveryClient的bean
@EnableDiscoveryClient
public class ProviderApplication {
public static void main(String[] args) {
SpringApplication.run(ProviderApplication.class, args);
}
}
- 配置文件的配置
server:
port: 12389
spring:
application:
name: provider
eureka:
client:
service-url:
defaultZone : http://localhost:8088/eureka/
#注册到服务中心
fetch-registry: true
register-with-eureka: true
healthcheck:
enabled: true
instance:
#服务实例过期时间
lease-expiration-duration-in-seconds: 2
#续租时间间隔
lease-renewal-interval-in-seconds: 1
以上就是最为精简版的微服务实例的搭建。
两者同时运行之后,访问浏览器:localhost:8088,可以看到如下界面:
那么说明搭建成功了!
二、微服务注册实现
好了,上面已经搭建了基本的一个服务治理中心和一个服务提供者,下面就要来分析是怎么实现的。
1、配置信息的提取
我们先看配置信息:
eureka:
client:
service-url:
defaultZone : http://localhost:8088/eureka/
#注册到服务中心
fetch-registry: true
register-with-eureka: true
healthcheck:
enabled: true
instance:
#服务实例过期时间
lease-expiration-duration-in-seconds: 2
#续租时间间隔
lease-renewal-interval-in-seconds: 1
上述的配置信息都是在eureka.client和eureka.instance下;由此判断这些便是实现微服务注册的基本配置。
我们直接跳进封装配置的类,即EurekaClientConfig
package com.netflix.discovery;
import javax.annotation.Nullable;
import javax.inject.Singleton;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import com.google.inject.ProvidedBy;
import com.netflix.appinfo.EurekaAccept;
import com.netflix.config.DynamicPropertyFactory;
import com.netflix.config.DynamicStringProperty;
import com.netflix.discovery.internal.util.Archaius1Utils;
import com.netflix.discovery.providers.DefaultEurekaClientConfigProvider;
import com.netflix.discovery.shared.transport.DefaultEurekaTransportConfig;
import com.netflix.discovery.shared.transport.EurekaTransportConfig;
import static com.netflix.discovery.PropertyBasedClientConfigConstants.*;
@Singleton
@ProvidedBy(DefaultEurekaClientConfigProvider.class)
public class DefaultEurekaClientConfig implements EurekaClientConfig {
/**
* @deprecated 2016-08-29 use {@link com.netflix.discovery.CommonConstants#DEFAULT_CONFIG_NAMESPACE}
*/
@Deprecated
public static final String DEFAULT_NAMESPACE = CommonConstants.DEFAULT_CONFIG_NAMESPACE + ".";
public static final String DEFAULT_ZONE = "defaultZone";
public static final String URL_SEPARATOR = "\\s*,\\s*";
private final String namespace;
private final DynamicPropertyFactory configInstance;
private final EurekaTransportConfig transportConfig;
public DefaultEurekaClientConfig() {
this(CommonConstants.DEFAULT_CONFIG_NAMESPACE);
}
public DefaultEurekaClientConfig(String namespace) {
this.namespace = namespace.endsWith(".")
? namespace
: namespace + ".";
this.configInstance = Archaius1Utils.initConfig(CommonConstants.CONFIG_FILE_NAME);
this.transportConfig = new DefaultEurekaTransportConfig(namespace, configInstance);
}
/*
* (non-Javadoc)
*
* @see
* com.netflix.discovery.EurekaClientConfig#getRegistryFetchIntervalSeconds
* ()
*/
@Override
public int getRegistryFetchIntervalSeconds() {
return configInstance.getIntProperty(
namespace + REGISTRY_REFRESH_INTERVAL_KEY, 30).get();
}
@Override
public int getInstanceInfoReplicationIntervalSeconds() {
return configInstance.getIntProperty(
namespace + REGISTRATION_REPLICATION_INTERVAL_KEY, 30).get();
}
@Override
public int getInitialInstanceInfoReplicationIntervalSeconds() {
return configInstance.getIntProperty(
namespace + INITIAL_REGISTRATION_REPLICATION_DELAY_KEY, 40).get();
}
/*
* (non-Javadoc)
*
* @see com.netflix.discovery.EurekaClientConfig#getProxyPort()
*/
@Override
public String getProxyPort() {
return configInstance.getStringProperty(
namespace + EUREKA_SERVER_PROXY_PORT_KEY, null).get();
}
@Override
public String getProxyUserName() {
return configInstance.getStringProperty(
namespace + EUREKA_SERVER_PROXY_USERNAME_KEY, null).get();
}
@Override
public String getProxyPassword() {
return configInstance.getStringProperty(
namespace + EUREKA_SERVER_PROXY_PASSWORD_KEY, null).get();
}
@Override
public boolean shouldGZipContent() {
return configInstance.getBooleanProperty(
namespace + EUREKA_SERVER_GZIP_CONTENT_KEY, true).get();
}
@Override
public int getEurekaServerReadTimeoutSeconds() {
return configInstance.getIntProperty(
namespace + EUREKA_SERVER_READ_TIMEOUT_KEY, 8).get();
}
/*
*获取连接超时的时间
*/
@Override
public int getEurekaServerConnectTimeoutSeconds() {
return configInstance.getIntProperty(
namespace + EUREKA_SERVER_CONNECT_TIMEOUT_KEY, 5).get();
}
@Override
public String getBackupRegistryImpl() {
return configInstance.getStringProperty(namespace + BACKUP_REGISTRY_CLASSNAME_KEY,
null).get();
}
@Override
public int getEurekaServerTotalConnections() {
return configInstance.getIntProperty(
namespace + EUREKA_SERVER_MAX_CONNECTIONS_KEY, 200).get();
}
@Override
public int getEurekaServerTotalConnectionsPerHost() {
return configInstance.getIntProperty(
namespace + EUREKA_SERVER_MAX_CONNECTIONS_PER_HOST_KEY, 50).get();
}
@Override
public String getEurekaServerURLContext() {
return configInstance.getStringProperty(
namespace + EUREKA_SERVER_URL_CONTEXT_KEY,
configInstance.getStringProperty(namespace + EUREKA_SERVER_FALLBACK_URL_CONTEXT_KEY, null)
.get()).get();
}
/*
* 获取服务中心的端口
*/
@Override
public String getEurekaServerPort() {
return configInstance.getStringProperty(
namespace + EUREKA_SERVER_PORT_KEY,
configInstance.getStringProperty(namespace + EUREKA_SERVER_FALLBACK_PORT_KEY, null)
.get()).get();
}
/*
* 获取dns名称
*/
@Override
public String getEurekaServerDNSName() {
return configInstance.getStringProperty(
namespace + EUREKA_SERVER_DNS_NAME_KEY,
configInstance
.getStringProperty(namespace + EUREKA_SERVER_FALLBACK_DNS_NAME_KEY, null)
.get()).get();
}
/*
*是否采用dns拉取服务列表
*/
@Override
public boolean shouldUseDnsForFetchingServiceUrls() {
return configInstance.getBooleanProperty(namespace + SHOULD_USE_DNS_KEY,
false).get();
}
/*
* 是否注册到服务中心的配置对应
*/
@Override
public boolean shouldRegisterWithEureka() {
return configInstance.getBooleanProperty(
namespace + REGISTRATION_ENABLED_KEY, true).get();
}
@Override
public boolean shouldUnregisterOnShutdown() {
return configInstance.getBooleanProperty(
namespace + SHOULD_UNREGISTER_ON_SHUTDOWN_KEY, true).get();
}
@Override
public boolean shouldPreferSameZoneEureka() {
return configInstance.getBooleanProperty(namespace + SHOULD_PREFER_SAME_ZONE_SERVER_KEY,
true).get();
}
@Override
public boolean allowRedirects() {
return configInstance.getBooleanProperty(namespace + SHOULD_ALLOW_REDIRECTS_KEY, false).get();
}
@Override
public boolean shouldLogDeltaDiff() {
return configInstance.getBooleanProperty(
namespace + SHOULD_LOG_DELTA_DIFF_KEY, false).get();
}
@Override
public boolean shouldDisableDelta() {
return configInstance.getBooleanProperty(namespace + SHOULD_DISABLE_DELTA_KEY,
false).get();
}
@Nullable
@Override
public String fetchRegistryForRemoteRegions() {
return configInstance.getStringProperty(namespace + SHOULD_FETCH_REMOTE_REGION_KEY, null).get();
}
/*
* 获取region
*/
@Override
public String getRegion() {
DynamicStringProperty defaultEurekaRegion = configInstance.getStringProperty(CLIENT_REGION_FALLBACK_KEY, Values.DEFAULT_CLIENT_REGION);
return configInstance.getStringProperty(namespace + CLIENT_REGION_KEY, defaultEurekaRegion.get()).get();
}
/*
*获取有效的zone
*/
@Override
public String[] getAvailabilityZones(String region) {
return configInstance
.getStringProperty(
namespace + region + "." + CONFIG_AVAILABILITY_ZONE_PREFIX,
DEFAULT_ZONE).get().split(URL_SEPARATOR);
}
/*
* 获取服务URLS
*/
@Override
public List<String> getEurekaServerServiceUrls(String myZone) {
String serviceUrls = configInstance.getStringProperty(
namespace + CONFIG_EUREKA_SERVER_SERVICE_URL_PREFIX + "." + myZone, null).get();
if (serviceUrls == null || serviceUrls.isEmpty()) {
serviceUrls = configInstance.getStringProperty(
namespace + CONFIG_EUREKA_SERVER_SERVICE_URL_PREFIX + ".default", null).get();
}
if (serviceUrls != null) {
return Arrays.asList(serviceUrls.split(URL_SEPARATOR));
}
return new ArrayList<String>();
}
@Override
public boolean shouldFilterOnlyUpInstances() {
return configInstance.getBooleanProperty(
namespace + SHOULD_FILTER_ONLY_UP_INSTANCES_KEY, true).get();
}
@Override
public int getEurekaConnectionIdleTimeoutSeconds() {
return configInstance.getIntProperty(
namespace + EUREKA_SERVER_CONNECTION_IDLE_TIMEOUT_KEY, 30)
.get();
}
/**
* 这就是是否拉取注册信息的配置获取
*/
@Override
public boolean shouldFetchRegistry() {
return configInstance.getBooleanProperty(
namespace + FETCH_REGISTRY_ENABLED_KEY, true).get();
}
/*
* (non-Javadoc)
*
* @see com.netflix.discovery.EurekaClientConfig#getRegistryRefreshSingleVipAddress()
*/
@Override
public String getRegistryRefreshSingleVipAddress() {
return configInstance.getStringProperty(
namespace + FETCH_SINGLE_VIP_ONLY_KEY, null).get();
}
/**
* 心跳刷新线程池大小
*
* @see com.netflix.discovery.EurekaClientConfig#getHeartbeatExecutorThreadPoolSize()
*/
@Override
public int getHeartbeatExecutorThreadPoolSize() {
return configInstance.getIntProperty(
namespace + HEARTBEAT_THREADPOOL_SIZE_KEY, Values.DEFAULT_EXECUTOR_THREAD_POOL_SIZE).get();
}
@Override
public int getHeartbeatExecutorExponentialBackOffBound() {
return configInstance.getIntProperty(
namespace + HEARTBEAT_BACKOFF_BOUND_KEY, Values.DEFAULT_EXECUTOR_THREAD_POOL_BACKOFF_BOUND).get();
}
/**
* 获取缓存刷新线程池的大小
*
* @see com.netflix.discovery.EurekaClientConfig#getCacheRefreshExecutorThreadPoolSize()
*/
@Override
public int getCacheRefreshExecutorThreadPoolSize() {
return configInstance.getIntProperty(
namespace + CACHEREFRESH_THREADPOOL_SIZE_KEY, Values.DEFAULT_EXECUTOR_THREAD_POOL_SIZE).get();
}
@Override
public int getCacheRefreshExecutorExponentialBackOffBound() {
return configInstance.getIntProperty(
namespace + CACHEREFRESH_BACKOFF_BOUND_KEY, Values.DEFAULT_EXECUTOR_THREAD_POOL_BACKOFF_BOUND).get();
}
...
}
以上内容很大,还有部分方法没有列出来,如果能过全部看完,知道方法名就行,无非就是做如下的工作:
- 构造一个ClientConfig类用于封装configInstance的内容
- 而真正拥有配置信息的对象,是 configInstance 。
- 封装了一系列的配置获取方法。
以上就属于配置信息的封装。接下来要进入构造服务中心列表了。
2、构造服务中心列表
经过上面的搭建,我们都知道,在微服务实例的启动类上面,存在着一个注解 @EnableDiscoveryClient
从该注解入手进行剖析,找到了 DiscoveryClient
这个类,并且有如下类图:
由于EurekaClient等接口只是一个规范,我们直接跳进DiscoveryClient的源码。
找到 getServiceUrlsFromConfig()
的方法:
/**
* @deprecated see replacement in {@link com.netflix.discovery.endpoint.EndpointUtils}
*
* Get the list of all eureka service urls from properties file for the eureka client to talk to.
*
* @param instanceZone The zone in which the client resides
* @param preferSameZone true if we have to prefer the same zone as the client, false otherwise
* @return The list of all eureka service urls for the eureka client to talk to
*/
@Deprecated
@Override
public List<String> getServiceUrlsFromConfig(String instanceZone, boolean preferSameZone) {
return EndpointUtils.getServiceUrlsFromConfig(clientConfig, instanceZone, preferSameZone);
}
很不幸,这个方法已经过期了,但是,它告诉我们,迁移到了 EndpointUtils
,于是,我们找到了:
public static List<String> getServiceUrlsFromConfig(EurekaClientConfig clientConfig, String instanceZone, boolean preferSameZone) {
List<String> orderedUrls = new ArrayList<String>();
String region = getRegion(clientConfig);
//根据region去获取zone,如果拿到的是空的,则是defaultZone
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);
//尽可能获取preferZone的下标,如果不行,就返回默认zone
int myZoneOffset = getZoneOffset(instanceZone, preferSameZone, availZones);
//拉取注册中心的url,依据是zone.
List<String> serviceUrls = clientConfig.getEurekaServerServiceUrls(availZones[myZoneOffset]);
//添加进注册中心url的列表;这里添加的是首先zone的serviceUrls
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;
}
/**
* Gets the zone to pick up for this instance.
*/
private static int getZoneOffset(String myZone, boolean preferSameZone, String[] availZones) {
for (int i = 0; i < availZones.length; i++) {
//遍历zone,找到与preferSameZone相同的zone,返回下标
if (myZone != null && (availZones[i].equalsIgnoreCase(myZone.trim()) == preferSameZone)) {
return i;
}
}
//找不到,则返回0,即默认索引
return 0;
}
对于以上服务列表构建方法的分析如下:
- 获取配置的region,由region提取zone
- zone存在则加入zone列表,不存在则是defaultZone,即拉取配置文件中对应的默认空间
- 尽可能找到preferzone相同的zone,实在找不到,就返回默认zone的下标。
- 依据zone的下标,获取对应的服务列表,也就是服务注册中心的地址,添加进orderedList(首选注册中心)
- orderedList是一个基于ArrayList创建的List,因此,是有序的。
- 完成了首选服务注册中心的地址列表的构造。
- 在对其它的zone进行处理,提取其它zone的服务注册中心的地址列表。
以上就是服务中心地址列表的提取工作。
3、初始化定时任务
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");
}
}
}
对初始化定时任务的分析:
- 定时任务可以分为如下4类:
- instanceInfoReplicator
- heartbeat
- cacheRefresh
- Renew
这些定时任务是后续实例与服务中心在运行过程中的关联关系的保证。
4、注册
前面说了那么多,完成了各类的工作,包括设定好了定时任务,那么,就应该注册实例了!
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();
}
以上代码只是对真正注册代码的封装,继续跳:
@Override
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();
}
上述代码就是基于RestTemplate发送POST方法的注册请求。注册的信息包括如下:
public InstanceInfo(InstanceInfo ii) {
this.instanceId = ii.instanceId;
this.appName = ii.appName;
this.appGroupName = ii.appGroupName;
this.ipAddr = ii.ipAddr;
this.sid = ii.sid;
this.port = ii.port;
this.securePort = ii.securePort;
this.homePageUrl = ii.homePageUrl;
this.statusPageUrl = ii.statusPageUrl;
this.healthCheckUrl = ii.healthCheckUrl;
this.secureHealthCheckUrl = ii.secureHealthCheckUrl;
this.vipAddress = ii.vipAddress;
this.secureVipAddress = ii.secureVipAddress;
this.statusPageRelativeUrl = ii.statusPageRelativeUrl;
this.statusPageExplicitUrl = ii.statusPageExplicitUrl;
this.healthCheckRelativeUrl = ii.healthCheckRelativeUrl;
this.healthCheckSecureExplicitUrl = ii.healthCheckSecureExplicitUrl;
this.vipAddressUnresolved = ii.vipAddressUnresolved;
this.secureVipAddressUnresolved = ii.secureVipAddressUnresolved;
this.healthCheckExplicitUrl = ii.healthCheckExplicitUrl;
this.countryId = ii.countryId;
this.isSecurePortEnabled = ii.isSecurePortEnabled;
this.isUnsecurePortEnabled = ii.isUnsecurePortEnabled;
this.dataCenterInfo = ii.dataCenterInfo;
this.hostName = ii.hostName;
this.status = ii.status;
this.overriddenStatus = ii.overriddenStatus;
this.isInstanceInfoDirty = ii.isInstanceInfoDirty;
this.leaseInfo = ii.leaseInfo;
this.isCoordinatingDiscoveryServer = ii.isCoordinatingDiscoveryServer;
this.metadata = ii.metadata;
this.lastUpdatedTimestamp = ii.lastUpdatedTimestamp;
this.lastDirtyTimestamp = ii.lastDirtyTimestamp;
this.actionType = ii.actionType;
this.asgName = ii.asgName;
this.version = ii.version;
}
以上的Info就是一个服务中心能够获取的实例的信息,而当服务中心接收到这个信息之后,便会进行实例注册,记录这个微服务实例。
三、总结
终于讲完了服务注册的原理,这个过程很长,但是只要你了解了,相信对于SpringCloud的运行机制会更加熟悉。总结一下:
- 微服务实例
- 提取配置
- 封装配置
- 构造有序的服务中心地址列表(按照zone的排序)
- 初始化一系列运行时的定时任务
- 发起注册
- 服务中心
- 接收请求并注册到自己的存储列表
- 响应已注册的微服务实例的续租、心跳等请求