How To Send Heartbeats Periodically With ScheduledExecutorService In Java

While working on a licensing application, I had to develop a system to send heartbeats to a licensing server every 10 mins. In this blog post, we’ll explore how to send heartbeats periodically to a server using ScheduledExecutorService.

Introduction

A heartbeat is a small, periodic message that a node sends to another node in the system to let it know it’s still alive and functioning correctly.

In the software floating licensing model, a licensed software has to check out the license from the licensing server on startup and release it on exit. So that it can be available for use by other users, in this context, the heartbeat means, the licensed software needs to send periodic pings to the licensing server to indicate that it is still alive and using the license. So that, if there is no message/ping received for a specified period, the licensing server can release the license claimed by the software assuming it is killed and no longer alive.

ScheduledExecutorService is a class in Java concurrency API that can schedule tasks to run periodically or at a specific time in the future. We are going to use this service to schedule a task to run repeatedly at a set interval.

Sending Heartbeats Periodically

Now that we understand the importance of heartbeats, let’s see how we can implement them using ScheduledExecutorService in Java. The first step is to create a class that will send the heartbeats. Here’s an example of what that might look like:

package com.siriusag.license.util;

import com.siriusag.license.api.LicenseManager;
import com.siriusag.license.event.ApplicationEventPublisher;
import com.siriusag.license.event.ShutdownEvent;
import lombok.extern.slf4j.Slf4j;

import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicInteger;

@Slf4j
public class HeartbeatPinger {
    private final long period;
    private final long initialDelay;
    private final TimeUnit periodTimeUnit;
    private final LicenseManager licenseManager;
    private final ScheduledExecutorService timerService = Executors.newSingleThreadScheduledExecutor();

    public HeartbeatPinger(long period, long initialDelay, TimeUnit periodTimeUnit, LicenseManager licenseManager) {
        this.period = period;
        this.initialDelay = initialDelay;
        this.periodTimeUnit = periodTimeUnit;
        this.licenseManager = licenseManager;
    }

    public void start() {
        AtomicInteger failureCount = new AtomicInteger();
        timerService.scheduleAtFixedRate(() -> {
            try {
                licenseManager.ping();
            } catch (Throwable e) {
                log.warn("Connection to license server lost ..." );
                if (failureCount.incrementAndGet() >= 3) {
                    ApplicationEventPublisher.getInstance().publishEvent(new ShutdownEvent("Ping failed 3 times consecutively"));
                    stop();
                }
            }
        }, initialDelay, period, periodTimeUnit);
    }

    public void stop() {
        timerService.shutdown();
    }
}

In this example, we’ve created a class called HeartbeatPinger in which we’ve created an instance of ScheduledExecutorService using the Executors.newScheduledThreadPool method. And then, we are scheduling a task using timerservice.scheduleAtFixedRate method. The HeartbeatPinger class has four constructor arguments:

  • period – the period between successive executions
  • initialDelay – the time to delay the first execution
  • periodTimeUnit – the time unit of the initialDelay and period parameters
  • licenseManager – which is the class responsible for sending heartbeats with a REST API call to the licensing server.

In addition to sending the heartbeats, HeartbeatPinger can also trigger a shutdown event when the ping to the server fails 3 times continuously. So that the listener of this event can exit the application in order to restrict the user from using the licensed software. We are using AtomicInteger to count the ping failures.

AtomicInteger is an int value that may be updated atomically. it is used in applications such as atomically incremented counters, and cannot be used as a replacement for an Integer. However, this class does extend Number to allow uniform access by tools and utilities that deal with numerically-based classes.

Instantiating Heartbeat Sender

The next step is to create an instance of HeartbeatPinger and schedule the sending task to run at the desired interval. Here’s an example of how to do that:

HeartbeatPinger heartbeatPinger = new HeartbeatPinger(10, 10, TimeUnit.MINUTES, this);

Starting the Scheduled Task

Now, we can call the start() method of the HeartbeatPinger to schedule the task to run every 10 minutes on application startup.

// Send heartbeats to server every 10 minutes to keep the license active
heartbeatPinger.start();

Stopping the Scheduled Task

Finally, we can call the stop() method of the HeartbeatPinger to shut down the ScheduledExecutorService on application exit.

// Shutdown the ExecutorService to stop the scheduled task
heartbeatPinger.stop();

Conclusion

That’s it, folks! With these few lines of code, we’ve created a system that will send heartbeats to a server at a set interval using ScheduledExecutorService in Java.

Leave a Reply