十分钟,读懂心跳机制【手撕eureka源码NO.3】

本文深入剖析Eureka服务的下线流程,包括客户端主动下线操作,以及心跳机制如何实现实时服务状态监控。通过源码解析,揭示了eureka-server如何通过更新心跳时间戳来检测服务健康状况并自动感知故障。
摘要由CSDN通过智能技术生成

本篇我们讲解eureka的心跳机制,来看看eureka的源码是通过怎样的设计来实现对服务的心跳检测的。

正式开始之前,先来思考一下eureka设计心跳机制是为了解决什么问题?

其实最主要的目的,就是通过心跳来达到对各个服务健康状态的监测。

也就是为了检测出宕机的服务,那些宕机的服务肯定是不能正常访问的,所以需要筛选出这些服务,并将这些不能正常提供访问支持的服务,从注册表中移除掉。

换句话讲,这些异常的服务都被下线啦。

所以本篇,我们从eureka的服务下线开始讲起。

服务怎么主动下线?

当我们重启服务,或者需要将某个服务关闭时,就需要将自己的服务下线,也就是告诉eureka-server:我要下线了,你记得把我的地址信息从注册表中删除!!

在关闭服务时,我们需要调用eureka-client提供DiscoveryClient.java中的shutdown()方法。

调用shutdown()方法后,eureka-client会向eureka-server发送一个服务下线的http请求,eureka-server就会将发送请求的服务实例从注册表中移除。

eureka-client端代码比较简单,直接贴出源码看一下即可:

public synchronized void shutdown() {
    //关闭定时任务
    cancelScheduledTasks();
    //通知eureka-server下线
    unregister();
    //其他..略..
}

void unregister() {
    //发送http请求
    EurekaHttpResponse<Void> httpResponse =
        eurekaTransport.registrationClient.cancel(instanceInfo.getAppName(), instanceInfo.getId()); 
}

主要的业务逻辑在eureka-server端,通过前面两篇文章,其实我们已经知道eureka-server大概要做哪些工作了,比如:从注册表中删除下线的服务将缓存数据过期把变动的服务实例信息加入到Queue

还是先上一张代码简图,查看一下源码的调用流程:

23-server-主动下线-代码简图

其中最主要的方法是internalCancel(..),贴出主要部分来看一看:

public abstract class AbstractInstanceRegistry implements InstanceRegistry {
    //注册表:记录所有服务的ip和端口号等信息
    private final ConcurrentHashMap<String, Map<String, Lease<InstanceInfo>>> registry
        = new ConcurrentHashMap<>();
    //记录最近状态发生变化的服务实例
    private ConcurrentLinkedQueue<RecentlyChangedItem> recentlyChangedQueue 
        = new ConcurrentLinkedQueue<>();
    
    //服务下线主要方法
    protected boolean internalCancel(String appName, String id, boolean isReplication) {
        //根据服务名,获取对应的服务实例
        Map<String, Lease<InstanceInfo>> gMap = registry.get(appName);
            Lease<InstanceInfo> leaseToCancel = null;
            if (gMap != null) {
                //从注册表中删除下线的那一个服务实例
                leaseToCancel = gMap.remove(id);
            }
        //记录下线时间
        leaseToCancel.cancel();
        //ActionType,客户端合并数据时会用到
        instanceInfo.setActionType(ActionType.DELETED);
        //向最近发生变化的队列添加数据
        recentlyChangedQueue.add(new RecentlyChangedItem(leaseToCancel));
        //清空缓存
        invalidateCache(appName, vip, svip);
    }
}

再次提醒: 文章中贴出来的代码,是经过提炼的,一些繁琐的if-else判断、log打印,或者是干扰理解的部分,文章中都没有贴进来。

主要是为了方便大家理解,也是为了增加文章的可读性。

AbstractInstanceRegistry.java前文我们已经多次用到过,这里就不再过多介绍。

recentlyChangedQueueConcurrentHashMap<> registry同样多次重复过,分别是存储最近变化的服务实例数据、注册表。

ActionType.DELETED在上一章客户端获取增量注册表后,本地有一个合并数据的过程,合并时根据ActionType的不同进行不同的操作,简单了解一下即可。

invalidateCache()在上一章讲解多级缓存机制时,同样也贴过源码和详细说明,这里就不再赘述了。

基本上服务主动下线的源码,只要认真学习过上两章的内容,这一小节就很容易理解了。

一张图,总结一下服务下线、缓存更新、eureka-client端感知到服务下线的过程:

心跳机制是怎么实现的?

首先,我们用一张图,来展示一下eureka心跳机制。

25-心跳机制-我还活着

从图中我们可以看出,eureka-client每隔30s就会向服务端大喊一声:我还活着。

如果转换为编程语言的话,这个过程是怎样实现的呢?想一想。

其实很简单,每隔30s,自然是用定时任务来实现。大喊一声,则是调用eureka-server端的接口,我们来看看eureka-client的代码实现。

定时任务的定义在DiscoveryClient.java中,不知道大家有没有注意到DiscoveryClientclent.java是一个非常重要的类,我们在学习源码的过程中,经常会读到这里面的代码。

先看一下方法调用简图:

代码片段:

public class DiscoveryClient implements EurekaClient {
    private void initScheduledTasks() {
        scheduler.schedule(
            new TimedSupervisorTask(
                "heartbeat",
                scheduler,
                heartbeatExecutor,
                renewalIntervalInSecs,
                TimeUnit.SECONDS,
                expBackOffBound,
                //定时任务执行的内容
                new HeartbeatThread()
            ),
            //定时间隔 30s
            renewalIntervalInSecs, TimeUnit.SECONDS);
	}

    //内部类(线程)
    private class HeartbeatThread implements Runnable {
        public void run() {
            //renew() 发送http请求
            if (renew()) {
                lastSuccessfulHeartbeatTimestamp = System.currentTimeMillis();
            }
        }
    }
}

eureka-client部分的代码比较简单,我们看到此处即可。

接下来看看eureka-server的处理。

先看一下方法调用的简图:

其中最主要的方法是Lease.java中的renew(),贴出源码看一下:

public class Lease<T> {
    private volatile long lastUpdateTimestamp;
    public void renew() {
        //更新时间戳
        lastUpdateTimestamp = System.currentTimeMillis() + duration;
    }
}

没错,其实eureka-server只是更新了一下发送心跳信息的服务的最后更新时间lastUpdateTimestamp

至此,eureka的整个心跳机制就已经讲解完毕。

但是心跳机制只是方法,不是目的,设计心跳机制的目的是为了监测异常的服务,从而让eureka-server能够达到自动感知故障的目的。

下一篇,我们来看看eureka-server是怎么实现对服务的故障感知的。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值