分布式定时任务轻量级解决方案ShedLock

微信搜索“onebyte”发现更多技术文章。

01 什么是ShedLock

ShedLock利用锁(分布式锁)机制,确保处于多个节点的定时任务只执行一次。注意,ShedLock本身并不是一个分布式定时调度器。

Please note that ShedLock is not and will never be full-fledged scheduler, it's just a lock. 

此外,ShedLock是基于时间来实现的分布式锁,它假定每个节点上的时钟已同步。

Moreover, the locks are time-based and ShedLock assumes that clocks on the nodes are synchronized.

02 

ShedLock核心组件

  • Core - 锁机制的支持;

  • Integration - 使用Spring AOP,Micronaut AOP或自定义开发与应用程序集成;

  • Lock provider - 基于额外扩展程序(例如SQL数据库,Mongo,Redis等)实现锁机制。

  • 03 

    如何使用

    使用ShedLock非常简单,需遵循以下3个步骤:

  • 启用并配置ShedLock,@EnableSchedulerLock;

  • 使用注解标注调度任务,使用@SchedulerLock注解标注即可;

  • 配置锁实现。

  • 第一步:启用并配置ShedLock;Enable and configure Scheduled locking (Spring)

    添加依赖

    <dependency>
        <groupId>net.javacrumbs.shedlock</groupId>
        <artifactId>shedlock-spring</artifactId>
        <version>4.12.0</version>
    </dependency>

    启用,其中@EnableScheduling开启Spring对任务调度的支持

    @Configuration
    @EnableScheduling
    @EnableSchedulerLock(defaultLockAtMostFor = "30s")
    public class ShedLockConfig {
        ...}

    第二步:使用注解标注调度任务;Annotate your scheduled tasks

    import net.javacrumbs.shedlock.core.SchedulerLock;

    @Scheduled(cron = "0 */2 * * * *")
    @SchedulerLock(name = "scheduledTaskName", lockAtMostFor = "2s", lockAtLeastFor = "30s")
    public void scheduledTask() {
       // do something}

    @SchedulerLock注解说明:

  • 标识调度任务以被ShedLock识别,并执行代理(获取锁后再执行任务);

  • name属性:必须指定,ShedLock保证具有相同name的定时任务同一时刻仅执行一次;

  • lockAtMostFor属性:任务获得锁后的最长持有时间;在正常情况下,任务执行完毕后会立即释放锁,这里的时间设置防止程序无法正常释放锁导致死锁。此外,lockAtMostFor设置的时间务必大于任务的执行时间,否则可能存在多个线程持有该锁,不能保证任务执行结果的正确性。如果未在@SchedulerLock中指定lockAtMostFor,则将使用@EnableSchedulerLock中的默认值。

  • lockAtLeastFor属性:任务获取锁后最短持有时间;在任务执行时间很短且节点之间的时钟不同步的情况下,该属性阻止任务在多个节点执行。
  • 第三步:配置锁实现;Configure LockProvider

    官方支持如下组件的锁实现,这里我们选用Redis,Redis (using Spring RedisConnectionFactory);

    Lock Providers

  • JdbcTemplate

  • Mongo

  • DynamoDB

  • DynamoDB 2

  • ZooKeeper (using Curator)

  • Redis (using Spring RedisConnectionFactory)

  • Redis (using Jedis)

  • Hazelcast

  • Couchbase

  • ElasticSearch

  • CosmosDB

  • Cassandra

  • Consul

  • ArangoDB

  • Etcd

  • Multi-tenancy

  • 添加Redis依赖,这里我们以SpringBoot为例:

    <!-- redis -->
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-data-redis</artifactId>
    </dependency>
    <!-- commons-pool2,为redis提供连接池 -->
    <dependency>
        <groupId>org.apache.commons</groupId>
        <artifactId>commons-pool2</artifactId>
    </dependency>
        
    <dependency>
        <groupId>net.javacrumbs.shedlock</groupId>
    <artifactId>shedlock-provider-redis-spring</artifactId>
        <version>4.12.0</version>
    </dependency>

    配置lockProvider:

    import net.javacrumbs.shedlock.core.LockProvider;
    import net.javacrumbs.shedlock.provider.redis.spring.RedisLockProvider;
    import net.javacrumbs.shedlock.spring.annotation.EnableSchedulerLock;
    import org.springframework.context.annotation.Bean;
    import org.springframework.context.annotation.Configuration;
    import org.springframework.data.redis.connection.RedisConnectionFactory;

    /**
     * @ClassName ShedLockConfiguration
     * @Author yangqk */
    @Configuration
    @EnableSchedulerLock(defaultLockAtMostFor = "30s")
    public class ShedLockConfig {
        @Bean
        public LockProvider lockProvider(RedisConnectionFactory redisConnectionFactory) {
            return new RedisLockProvider(redisConnectionFactory);
        }}

    注意:Redis实现的锁在哨兵或者集群模式下,发生主从故障时候,将存在不可靠的问题。04

    ShedLock的原理简析

    ShedLock基于代理实现对任务调度的控制。在执行任务前,会先获取锁,拿到锁的任务才能执行。如下图所示:

    与Spring整合的2种代理模式

    一种基于aop对ScheduledMethod进行代理(PROXY_METHOD),另一种是基于aop对TaskScheduler进行代理(PROXY_SCHEDULER)。

    代理模式一:Scheduled Method proxy

    从4.0.0版本开始,默认的代理方式。

    这种模式的主要优点是,它与希望以某种方式更改默认Spring调度机制的其他框架很好地配合使用。缺点是即使直接调用该方法也需要先获取锁。如果该方法返回一个值并且该锁由另一个进程持有,则将返回null或空的Optional对象(不支持原始返回类型)。

    注意:Final和non-public的方法无法执行代理,必须将其改为public的,或者使用TaskScheduler代理。

    代理模式二:TaskScheduler proxy

    这种模式将Spring TaskScheduler包装在AOP代理中。其开启方式(PROXY_SCHEDULER是4.0.0之前的默认方法):

    @EnableSchedulerLock(interceptMode = PROXY_SCHEDULER)

    如果在spring容器内没有发现TaskScheduler的实例,则会为其创建一个默认的实例。如果你有特殊需要,只需创建一个实现TaskScheduler接口的bean,它将自动包装到AOP代理中。如下代码的MySpecialTaskScheduler既是自定义的实现:

    @Bean
    public TaskScheduler taskScheduler() {
        return new MySpecialTaskScheduler();}

    此种代理方式的原理示意图如下:

    总结:ShedLock的使用比较简单,但并不是分布式调度任务的完美解决方案;对于追求分布式调度任务高可用、强一致性的的系统来说,建议选型更为优秀的解决方案。

    05

    引用

    [1] https://github.com/lukas-krecan/ShedLock

    [2] https://www.baeldung.com/shedlock-spring

    [3] https://rieckpil.de/lock-scheduled-tasks-with-shedlock-and-spring-boot/

    点击上方“蓝字”,发现更多精彩。

    最后编辑于
    ©著作权归作者所有,转载或内容合作请联系作者
    • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
      沈念sama阅读 220,458评论 6 513
    • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
      沈念sama阅读 94,030评论 3 396
    • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
      开封第一讲书人阅读 166,879评论 0 358
    • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
      开封第一讲书人阅读 59,278评论 1 295
    • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
      茶点故事阅读 68,296评论 6 397
    • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
      开封第一讲书人阅读 52,019评论 1 308
    • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
      沈念sama阅读 40,633评论 3 420
    • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
      开封第一讲书人阅读 39,541评论 0 276
    • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
      沈念sama阅读 46,068评论 1 319
    • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
      茶点故事阅读 38,181评论 3 340
    • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
      茶点故事阅读 40,318评论 1 352
    • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
      沈念sama阅读 35,991评论 5 347
    • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
      茶点故事阅读 41,670评论 3 331
    • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
      开封第一讲书人阅读 32,183评论 0 23
    • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
      开封第一讲书人阅读 33,302评论 1 272
    • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
      沈念sama阅读 48,655评论 3 375
    • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
      茶点故事阅读 45,327评论 2 358

    推荐阅读更多精彩内容