7

I am looking to replace Quartz as a job scheduler in our project. We already use Redis with cluster support as a distributed cache layer and we thought that maybe we could use Redis for job scheduling too. Has anyone implemented job scheduling in Java using Redis? I searched but could not find a library for this purpose. So I am starting to think that this is maybe not a popular solution?

Milen Kovachev
  • 5,111
  • 6
  • 41
  • 58

3 Answers3

6

Take a look at Redisson. It allows to schedule and execute tasks (with cron-expression support) using simple ScheduledExecutorService api and based on Redis queue.

Here is an example. First define a task using java.lang.Runnable interface. Each task can access to Redis instance via injected RedissonClient object.

public class RunnableTask implements Runnable {

    @RInject
    private RedissonClient redissonClient;

    @Override
    public void run() throws Exception {
        RMap<String, Integer> map = redissonClient.getMap("myMap");
        Long result = 0;
        for (Integer value : map.values()) {
            result += value;
        }
        redissonClient.getTopic("myMapTopic").publish(result);
    }

}

Submit it to Redis based ExecutorService.

RScheduledExecutorService executorService = redisson.getExecutorService("myExecutor");
RScheduledFuture<?> future = executorService.schedule(new CallableTask(), 10, 20, TimeUnit.MINUTES);

future.get();
// or cancel it
future.cancel(true);

// cancel by taskId
executorService.cancelTask(future.getTaskId());

Examples with cron expressions:

executorService.schedule(new RunnableTask(), CronSchedule.of("10 0/5 * * * ?"));

executorService.schedule(new RunnableTask(), CronSchedule.dailyAtHourAndMinute(10, 5));

executorService.schedule(new RunnableTask(), CronSchedule.weeklyOnDayAndHourAndMinute(12, 4, Calendar.MONDAY, Calendar.FRIDAY));

All tasks are distributed across Redisson nodes. You can run these nodes as much as you need.

Nikita Koksharov
  • 10,283
  • 1
  • 62
  • 71
1

How about Redis Labs' redis-quartz:

RedisJobStore A Quartz Scheduler JobStore that uses Redis for persistent storage.

We'd appreciate any feedback you have :)

Itamar Haber
  • 47,336
  • 7
  • 91
  • 117
  • 2
    Would redis-quartz play nicely with our Redis 3.0 cluster? – Milen Kovachev Mar 25 '16 at 15:44
  • Doesn't appear so - redis-quartz uses pipelined mode which, AFAIR, isn't supported by JedisCluster atm. Changing redis-quartz to not do that looks easy, so feel free to test/submit an issue/feature request/pull request if that's important to you. That said, unless you expect your job store to be massively loaded, I don't quite see the reason for a clustered deployment unless it is an existing one. If that is the case, note that in 99.999% of the cases you'd be better off using dedicated Redis databases for each task rather than trying use a single DB for everything. – Itamar Haber Mar 25 '16 at 16:06
  • Oh, there's an existing issue for that already: https://github.com/RedisLabs/redis-quartz/issues/6 #facepalm - want to add your say to it? – Itamar Haber Mar 25 '16 at 16:21
  • 1
    @ItamarHaber Pipelining works nicely with cluster, it's just the async nature that can make you cry when slots move. – mp911de Mar 27 '16 at 11:16
  • @mp911de I was referring specifically to Jedis' pipelining according to https://groups.google.com/forum/#!msg/jedis_redis/u6j8slokO3E/Dh5Q94TRjJUJ – Itamar Haber Mar 27 '16 at 15:29
1

I used Spring Task Scheduler with Shedlock and Redis to make scheduled task execution distributable.

In my XML configuration file I specify the task scheduler:

<task:scheduler id="myScheduler" pool-size="4"/>

<task:annotation-driven scheduler="myScheduler" />

In SchedulerLockConfiguration.java I create the LockProvider Spring bean and set up the Redis connection and the Jedis connection pool:

@Configuration
@EnableSchedulerLock(defaultLockAtMostFor = "PT10H")
public class SchedulerLockConfiguration {

  @Bean
  public JedisPool jedisPool() {
    JedisPoolConfig jedisPoolConfig = new JedisPoolConfig();
    jedisPoolConfig.setMaxTotal(100); // The maximum number of connections that are supported by the pool
    jedisPoolConfig.setMaxIdle(100);  // The maximum number of idle connections in the pool
    jedisPoolConfig.setMinIdle(10);  // The minimum number of idle connections in the pool
    return new JedisPool(
        jedisPoolConfig,
        Constants.REDIS_HOSTNAME,
        Integer.parseInt(Constants.REDIS_PORT),
        Integer.parseInt(Constants.REDIS_SESSION_TIMEOUT),
        null,
        Constants.REDIS_DATABASE_SHEDLOCK);
  }

  @Bean
  public LockProvider lockProvider(JedisPool jedisPool) {
    return new JedisLockProvider(jedisPool, "yourRedisNamespace");
  }

}

...then finally, I use @Scheduled and @SchedulerLock annotations on the methods I want to schedule:

@Scheduled(cron = "${my.cron.expression}")
@SchedulerLock(name = "myScheduler", lockAtLeastFor = "PT10M")
public void processDelayedAutomationRules() { ... }

When a @Scheduled job's cron kicks in, whichever applciation instance / server node puts the lock faster into the Redis store, that'll be the one executing it.

martoncsukas
  • 2,077
  • 20
  • 23