什么是eureka
什么是服务治理
服务注册中心: eureka提供的服务端,提供服务注册与发现的功能.
eureka:
erver:
enable-self-preservation: false
服务提供者: 提供服务的应用,可以是springboot应用以及其他技术平台上遵循eureka 通信规范的应用,通过将自身的一些信息提供给eureka 服务注册中心,以供消费者用.
服务注册:在服务实例启动时通过发送REST请求将自己注册到EUREKA SERVER服务上,并且带上自身相应的元数据信息,EUREKA SERVER 收到请求后通过一 系列校验,一个双层map中,第一层key为服务名,第二层key为具体服务实例.通过设置启动时将不会注册到服务中心
eureka:
client:
register-with-eureka: false
服务同步:在我们项目为了应付种种情况,往往会设置多个服务注册中心,所以在服务提供者发送注册信息到一个服务注册中心时,会将请求转发给集群中的
其他服务注册中心
服务续约:在注册完成后,服务提供者会启动一个定时任务(默认30s)定时向服务注册中心续约,以防止服务时效被剔除.
服务下线:在系统运行期间遇到关闭或者重启服务的情况时,客户端会发送一个服务下线的REST请求给EUREKA SERVER,EUREKA SERVER 收到请求后将该服务状 态设置为下线(DOWN).并且广播该事件
eureka:
client:
fetch-registry: false
源码分析
eureka:
client:
service-url:
defaultZone: http://localhost:7777/eureka/,http://localhost:7777/eureka/
通过查看EnableDiscoveryClient类注释信息Annotation to enable a DiscoveryClient implementation.
可以发现用来开启DiscoveryClient 实例,通过访问该类注释可以了解该类的用途
1.向EUREKA SERVER 注册实例
2.向EUREKA SERVER 续租
3. 向EUREKA SERVER 获取服务实例列表
4.向EUREKA SERVER 取消注册
通过查看该类方法,发现一个register()方法,该方法向Eureka Server发起注册请求,并且传入一个com.netflix.appinfo.InstanceInfo对象,该对象在
DiscoveryClient初始化的时候赋值,包含了该服务提供给服务注册中心的一些元数据
/**
* Register with the eureka service by making the appropriate REST call.
*/
boolean register() throws Throwable {
EurekaHttpResponse<Void> httpResponse;
try {
httpResponse = eurekaTransport.registrationClient.register(instanceInfo);
} catch (Exception e) {
logger.warn("{} - registration failed {}", PREFIX + appPathIdentifier, e.getMessage(), e);
throw e;
}
if (logger.isInfoEnabled()) {
logger.info("{} - registration status: {}", PREFIX + appPathIdentifier, httpResponse.getStatusCode());
}
return httpResponse.getStatusCode() == 204;
}
并且该方法是由
initScheduledTasks()方法进行调用
/**
* Initializes all scheduled tasks.
*/
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");
}
}
通过查看该方法创建了两个定时任务,用来进行服务获取以及服务续约,通过eurekaclientconfig的配置信息来进行服务注册,续约,以及服务清单获取,以及定时任务的
时间等.
我们在配置一个服务的时候还配置了对应的serverurl信息等,我们通过在DiscoveryClient类搜索serviceurl关键字可以发现获取serviceurl的方法标识过期并且连接 到com.netflix.discovery.endpoint.EndpointUtils 类里面,在该类下面发现getServiceUrlsFromConfig()方法:
/**
* Get the list of all eureka service urls from properties file for the eureka client to talk to.
*
* @param clientConfig the clientConfig to use
* @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
*/
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, Arrays.toString(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,并且只返回了一个Region和多个Zone,若配置文件中没有配置的话,默认使用default和defaultZone,所以一个微服务应用只能属于一个
Region,并且可以配置多个Zone,在获取到region和zone后开始加载对应的Eureka Server的具体地址,并且通过地址向对应的注册中心注册,续约获取服务等.