前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >基于Reids与AOP实现的定时任务锁-ScheduledLock

基于Reids与AOP实现的定时任务锁-ScheduledLock

作者头像
Diuut
发布2022-11-22 20:18:08
6350
发布2022-11-22 20:18:08
举报
文章被收录于专栏:Diuut

需求: 项目中的定时任务会定时执行,但如果部署多个服务器的话就会在同一时间,每个服务都会执行一次,如果是新建或者修改类的操作的话就会有冲突,因此需要一个方案处理这个多处执行的问题。

简单来说就是通过aop环绕切片将需要加锁的方法包起来,然后在执行前往redis中用setIfAbsent写入一个key,如果返回true,说明没有其他服务正在执行该方法,就继续执行,如果返回false,说明已经有其他服务正在执行该方法,就不执行。

为了方便使用采用了自定义注解的方式,如果哪个定时任务需要使用的话直接加一个@ScheduledLock注解即可。

该方案使用到了redis,使用以及配置方法略过。

实现如下:

引入相关依赖

代码语言:javascript
复制
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-aop</artifactId>
        </dependency>
        <dependency>
            <groupId>org.aspectj</groupId>
            <artifactId>aspectjrt</artifactId>
        </dependency>
        <dependency>
            <groupId>org.aspectj</groupId>
            <artifactId>aspectjweaver</artifactId>
        </dependency>

添加自定义注解接口

代码语言:javascript
复制
package com.miracle.qaodo.annotation;
import java.lang.annotation.*;

/**
 * @Author Diuut
 * @Date 2021/2/23  16:17
 */
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
public @interface ScheduledLock {
}

实现AOP逻辑

代码语言:javascript
复制
package com.miracle.qaodo.aspect;

import com.miracle.qaodo.annotation.ScheduledLock;
import lombok.extern.slf4j.Slf4j;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.reflect.MethodSignature;
import org.springframework.beans.BeansException;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Component;

import java.lang.reflect.Method;
import java.util.concurrent.TimeUnit;

/**
 * @Author Diuut
 * @Date 2021/2/23  16:20
 */
@Aspect
@Component
@Slf4j
public class ScheduleLockAspect implements ApplicationContextAware {
    //直接autowired redistemplate会报错Unexpected error occurred in scheduled task
    //因为@Scheduled注解方式级别高于资源注入级别,导致了资源注入失败
    //使用ApplicationContextAware,它实现了这个接口的bean,当spring容器初始化的时候,会自动的将ApplicationContext注入进来

    private static ApplicationContext context;

    @Override
    public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
        context = applicationContext;
    }

    public static ApplicationContext getContext() {
        return context;
    }

    public static Object getBean(String name) {
        return getContext().getBean(name);
    }

    @Around(value = "@annotation(scheduledLock)")
    public Object around(ProceedingJoinPoint point, ScheduledLock scheduledLock) {
        //拦截的类名
        Class clazz = point.getTarget().getClass();
        //拦截的方法
        Method method = ((MethodSignature) point.getSignature()).getMethod();
        log.info("定时任务锁 拦截了类:" + clazz + " 方法:" + method);
        Object proceed = null;
        RedisTemplate<String,String> redisTemplate = (RedisTemplate)getBean("redisTemplate");
        if (redisTemplate.opsForValue().setIfAbsent("qdchess-SchdulesLock-" + method.getName(), "lock",10, TimeUnit.SECONDS)) {
        //此处的key是可以根据自己的使用情况进行设置,只要方法之间不重复即可。
            log.info("其他服务未执行,通过执行");
            //获取锁,如果为false说明有其他服务正在执行,跳过执行
            try {
                proceed = point.proceed();          //执行定时任务
               redisTemplate.delete("qdchess-SchdulesLock-" + method.getName());
                return proceed;
            } catch (Throwable throwable) {
                redisTemplate.delete("qdchess-SchdulesLock-" + method.getName());
                throwable.printStackTrace();
                return null;
            }
        }
        log.info("其他服务已执行,未通过执行");
        return proceed;
    }
}

在测试编写期间反复报错Unexpected error occurred in scheduled task,经查询是因为@Scheduled注解方式级别高于资源注入级别,导致了资源注入失败,解决方案是类实现ApplicationContextAware,它实现了这个接口的bean,当spring容器初始化的时候,会自动的将ApplicationContext注入进来。

使用方法就是在使用定时任务的地方加一个@ScheduledLock注解即可。如:

代码语言:javascript
复制
    @Scheduled(cron = "0,20,40 * * * * ?")
    @ScheduledLock
    public void testLock(){
        log.info(ServerTimer.getFull());
    }

Post Views: 291

本文参与 腾讯云自媒体同步曝光计划,分享自作者个人站点/博客。
原始发表:2021年2月24日,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
相关产品与服务
云数据库 Redis
腾讯云数据库 Redis(TencentDB for Redis)是腾讯云打造的兼容 Redis 协议的缓存和存储服务。丰富的数据结构能帮助您完成不同类型的业务场景开发。支持主从热备,提供自动容灾切换、数据备份、故障迁移、实例监控、在线扩容、数据回档等全套的数据库服务。
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档