心跳机制

11/7/2022

# 一、需求与背景

经常会有这样的场景:

  • 开发或者测试的时候,要测试一个定时任务job,刚好要测试的时候发现,不知道谁把job给执行了?
  • 有些业务同一时间只能有一个进程在运行,但是结果并不是预期的,所以需要知道是否有多个进程同时在跑?
  • 某些时候可能服务器停掉了,什么时候停的都不知道
  • 想知道进程运行多久了,进行一些统计分析
  • 等等其他

# 二、架构与思想

以上问题有很多种解决方案,比如服务发现、心跳、轮询等等,这里选择的是心跳机制。因为心跳机制非常简单,而且也只需要依赖一个数据库表,非常的轻便,适合各个项目。

# 2.1、守护daemon线程

在服务器启动的时候开启一个守护daemon线程,在守护daemon线程每隔一段时间将关于进程的基本信息存储到数据库表中。

# 2.2、记录关键信息

heart-beat记录了如下内容:

    private String projectPath;  // 项目启动路径
    private String serverIp; //服务器ip
    private Integer processNo;//进程号
    private LocalDateTime processStartTime;//进程开启时间
    private LocalDateTime heartBeatTime;//心跳当前时间
1
2
3
4
5

# 三、具体使用

sa-common项目中的 sa-common.yaml 配置文件中,有关于心跳时间间隔的配置

# 心跳配置
heart-beat:
  interval-seconds: 60

1
2
3
4

默认是 60秒,时间想改短或者改长都可以。

心跳由于是守护线程去处理,且只有一个线程,数据库操作也非常简单,所以整体性能影响非常非常非常小,所以这个心跳时长可以自由定义。当然,不改也可以,使用我们默认的 60秒。

# 四、实现原理

# 4.1、开启守护daemon线程

HeartBeatManager.java


/**
 * 心跳核心调度管理器
 *
 * @Author 1024创新实验室-主任: 卓大
 * @Date 2022-01-09 20:57:24
 * @Wechat zhuoda1024
 * @Email lab1024@163.com
 * @Copyright 1024创新实验室 ( https://1024lab.net )
 */
public class HeartBeatManager {
    private static final String THREAD_NAME_PREFIX = "sa-heart-beat";
    private static final int THREAD_COUNT = 1;
    private static final long INITIAL_DELAY = 60 * 1000L;

    private ScheduledThreadPoolExecutor threadPoolExecutor;//守护线程池
    private IHeartBeatRecordHandler heartBeatRecordHandler;//服务状态持久化处理类
    private long intervalMilliseconds;//调度配置信息

    public HeartBeatManager(Long intervalMilliseconds,
                            IHeartBeatRecordHandler heartBeatRecordHandler) {
        this.intervalMilliseconds = intervalMilliseconds;
        this.heartBeatRecordHandler = heartBeatRecordHandler;
        //使用守护线程去处理
        this.threadPoolExecutor = new ScheduledThreadPoolExecutor(THREAD_COUNT, r -> {
            Thread t = new Thread(r, THREAD_NAME_PREFIX);
            if (!t.isDaemon()) {
                t.setDaemon(true);
            }
            return t;
        });
        // 开始心跳
        this.beginHeartBeat();
    }

    //开启心跳
    private void beginHeartBeat() {
        HeartBeatRunnable heartBeatRunnable = new HeartBeatRunnable(heartBeatRecordHandler);
        threadPoolExecutor.scheduleWithFixedDelay(heartBeatRunnable, INITIAL_DELAY, intervalMilliseconds, TimeUnit.MILLISECONDS);
    }
}
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

# 4.2、心跳数据

HeartBeatRunnable.java

public class HeartBeatRunnable implements Runnable {

    private String projectPath;//项目路径
    private List<String> serverIps;//服务器ip(多网卡)
    private Integer processNo;//进程号
    private LocalDateTime processStartTime;//进程开启时间
    private IHeartBeatRecordHandler recordHandler;

    public HeartBeatRunnable(IHeartBeatRecordHandler recordHandler) {
        this.recordHandler = recordHandler;
        this.initServerInfo();
    }

    /**
     * 初始化心跳相关信息
     */
    private void initServerInfo(){
        RuntimeMXBean runtimeMXBean = ManagementFactory.getRuntimeMXBean();
       this.projectPath = System.getProperty("user.dir");
       this.serverIps = new ArrayList<>(NetUtil.localIpv4s());
       this.processNo = Integer.valueOf(runtimeMXBean.getName().split("@")[0]).intValue();
       this.processStartTime = LocalDateTime.ofInstant(Instant.ofEpochMilli(runtimeMXBean.getStartTime()), ZoneId.systemDefault());
    }

    @Override
    public void run() {
        HeartBeatRecord heartBeatRecord = new HeartBeatRecord();
        heartBeatRecord.setProjectPath(this.projectPath);
        heartBeatRecord.setServerIp(StringUtils.join(this.serverIps, ";"));
        heartBeatRecord.setProcessNo(this.processNo);
        heartBeatRecord.setProcessStartTime(this.processStartTime);
        heartBeatRecord.setHeartBeatTime(LocalDateTime.now());
        recordHandler.handler(heartBeatRecord);
    }
}
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

更多代码可见sa-common项目support.heartbeat 包。


# 联系我们

1024创新实验室-主任:卓大 (opens new window),混迹于各个技术圈,研究过计算机,熟悉点 java,略懂点前端。
1024创新实验室(河南·洛阳) (opens new window) 致力于成为中原领先、国内一流的技术团队,以技术创新为驱动,合作各类项目。

加 主任 “卓大” 微信
拉你入群,一起学习
关注 “小镇程序员”
分享代码与生活、技术与赚钱
请 “1024创新实验室” 喝咖啡
支持我们的开源与分享

告白气球 (钢琴版)
JESSE T