Eureka client启动流程图和源码分析

Eureka server启动流程图和源码分析

流程图

Eureka server启动流程图

源码解析

由流程图看出,Eureka client启动时大致会做三件事,服务注册,心跳续约,服务更新,本质上都是通过模拟http请求,调用服务端提供的对应接口来实现

服务注册

通过服务端提供的“服务注册”接口,将当前client信息注册到注册中心。client初始化时,会将服务注册线程延迟40秒执行,并且每次注册结束后,会再次将当前线程延迟30秒(可配置)执行。这样当eureca服务内存丢失后(如重启服务),在一定时间内还能得到client信息。

配置如下:

eureka:
  client:
    initial-instance-info-replication-interval-seconds: 20 #首次注册延迟20秒
    instance-info-replication-interval-seconds: 60 #以后每次60秒注册一次
    registry-fetch-interval-seconds: 30 #心跳续约间隔时间

首次注册源码如下:

//client初始化时会调用此方法
public void start(int initialDelayMs) {
        if (started.compareAndSet(false, true)) {
            instanceInfo.setIsDirty();  // for initial register
            //scheduler为ScheduledExecutorService延迟线程池,会在initialDelayMs秒后执行一次线程任务
            //this为当前类,实现了Runnable接口
            Future next = scheduler.schedule(this, initialDelayMs, TimeUnit.SECONDS);
            scheduledPeriodicRef.set(next);
        }
    }

当前类的run方法源码如下:

public void run() {
        try {
            //更新实例信息
            discoveryClient.refreshInstanceInfo();

            //若心跳续约异常,则dirtyTimestamp不为null 
            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 {
            //将当前线程类放在延迟replicationIntervalSeconds秒的延迟线程池里执行
            Future next = scheduler.schedule(this, replicationIntervalSeconds, TimeUnit.SECONDS);
            scheduledPeriodicRef.set(next);
        }
    }

心跳续约

每30秒调用服务端提供的心跳续约接口,服务端接收到请求,会修改最后续约时间为当前系统时间。

//可通过配置register-with-eureka进行控制
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);
                    ...

HeartbeatThread的run方法会调用renew(),源码如下:

boolean renew() {
        EurekaHttpResponse<InstanceInfo> httpResponse;
        try {
            //模拟http请求,调用服务端提供的“接收心跳续约”接口
            httpResponse = eurekaTransport.registrationClient.sendHeartBeat(instanceInfo.getAppName(), instanceInfo.getId(), instanceInfo, null);
            logger.debug(PREFIX + "{} - Heartbeat status: {}", appPathIdentifier, httpResponse.getStatusCode());
            //404异常处理
            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;
        }
    }

服务更新

每30秒(可配置)调用服务端提供的获取实例接口,首次会调用获取全量实例,以后调用“获取增量实例”增量更新。“获取增量实例”接口会返回其所有实例的按一定规则拼接的hashcode,客户端更新后也会以相同规则拼接hashcode,做数据一致性校验

//通过fetch-registry: true  参数控制
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);
        }

CacheRefreshThread的run()方法会调用refreshRegistry方法,源码如下:

//模拟http请求,调用服务端提供的“获取全量实例”接口
boolean success = fetchRegistry(remoteRegionsModified);
            if (success) {
                registrySize = localRegionApps.get().size();
                lastSuccessfulRegistryFetchTimestamp = System.currentTimeMillis();
            }

fetchRegistry()方法源码如下:

private boolean fetchRegistry(boolean forceFullRegistryFetch) {
        Stopwatch tracer = FETCH_REGISTRY_TIMER.start();

        try {
            // If the delta is disabled or if it is the first time, get all
            // applications
            Applications applications = getApplications();

            //如果首次调用
            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);
            }
          ...
        return true;
    }
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值