Spring Boot中如何为定时任务配置多LockProvider实现分表ShedLock?
问题描述
我在Spring Boot中尝试创建两个ShedLock配置,以便通过不同的数据库表为定时任务加锁,已定义如下两个LockProvider Bean:
// 主锁提供者 @Bean("mainLockProvider") public LockProvider mainLockProvider(DataSource dataSource) { return new JdbcTemplateLockProvider( JdbcTemplateLockProvider.Configuration.builder() .withJdbcTemplate(new JdbcTemplate(dataSource)) .usingDbTime() .withTableName("main_shedlock_table") .build()); } // 副锁提供者 @Bean("secondaryLockProvider") public LockProvider secondaryLockProvider(DataSource dataSource) { return new JdbcTemplateLockProvider( JdbcTemplateLockProvider.Configuration.builder() .withJdbcTemplate(new JdbcTemplate(dataSource)) .usingDbTime() .withTableName("secondary_shedlock_table") .build()); }
但定义定时任务时发现无法指定使用哪个LockProvider,因为@SchedulerLock注解不支持传入LockProvider参数:
@Scheduled(cron = "*/5 * * * *") @SchedulerLock(name = "some_scheduled_task", lockAtLeastFor = "PT15S", lockAtMostFor = "PT1M") public void scheduledTaskUsingMainLockProvider() { // 本应在main_shedlock_table中创建锁记录 } @Scheduled(cron = "*/5 * * * *") @SchedulerLock(name = "some_other_scheduled_task", lockAtLeastFor = "PT15S", lockAtMostFor = "PT1M") public void scheduledTaskUsingSecondaryLockProvider() { // 本应在secondary_shedlock_table中创建锁记录 }
请问是否可以实现让不同定时任务使用对应LockProvider的需求?
解决方案
可以实现,核心思路是创建多组独立的ShedLock配置,每组配置对应自己的LockProvider,并通过方法过滤规则让不同的定时任务匹配到对应的锁配置。
1. 自定义标记注解(可选但直观)
创建两个自定义注解,用来标记不同组的定时任务,方便后续切面识别:
@Target(ElementType.METHOD) @Retention(RetentionPolicy.RUNTIME) public @interface UseMainLock { } @Target(ElementType.METHOD) @Retention(RetentionPolicy.RUNTIME) public @interface UseSecondaryLock { }
2. 配置多组ShedLock切面
针对每个LockProvider,单独构建ScheduledLockConfiguration,并通过withInterceptorMethodFilter指定该切面只处理对应标记的任务:
@Configuration public class MultipleShedLockConfig { // 主锁对应的ShedLock配置 @Bean public ScheduledLockConfiguration mainShedLockConfiguration( @Qualifier("mainLockProvider") LockProvider mainLockProvider, TaskScheduler taskScheduler) { return ScheduledLockConfigurationBuilder .withLockProvider(mainLockProvider) .withTaskScheduler(taskScheduler) // 只拦截带有@UseMainLock和@Scheduled的方法 .withInterceptorMethodFilter(method -> method.isAnnotationPresent(UseMainLock.class) && method.isAnnotationPresent(Scheduled.class)) .build(); } // 副锁对应的ShedLock配置 @Bean public ScheduledLockConfiguration secondaryShedLockConfiguration( @Qualifier("secondaryLockProvider") LockProvider secondaryLockProvider, TaskScheduler taskScheduler) { return ScheduledLockConfigurationBuilder .withLockProvider(secondaryLockProvider) .withTaskScheduler(taskScheduler) // 只拦截带有@UseSecondaryLock和@Scheduled的方法 .withInterceptorMethodFilter(method -> method.isAnnotationPresent(UseSecondaryLock.class) && method.isAnnotationPresent(Scheduled.class)) .build(); } }
3. 给定时任务添加标记注解
修改定时任务方法,加上对应的自定义注解,让任务匹配到指定的锁配置:
@Scheduled(cron = "*/5 * * * *") @UseMainLock @SchedulerLock(name = "some_scheduled_task", lockAtLeastFor = "PT15S", lockAtMostFor = "PT1M") public void scheduledTaskUsingMainLockProvider() { // 现在会使用main_shedlock_table中的锁记录 } @Scheduled(cron = "*/5 * * * *") @UseSecondaryLock @SchedulerLock(name = "some_other_scheduled_task", lockAtLeastFor = "PT15S", lockAtMostFor = "PT1M") public void scheduledTaskUsingSecondaryLockProvider() { // 现在会使用secondary_shedlock_table中的锁记录 }
补充说明
- 如果你不想用自定义注解,也可以通过方法的包路径、方法名前缀等规则来过滤任务,比如
method.getDeclaringClass().getPackageName().startsWith("com.xxx.main")。 - 确保两个
ScheduledLockConfigurationBean都被Spring正确扫描到,Spring会自动处理多个切面的逻辑,不会出现冲突。
内容的提问来源于stack exchange,提问作者Natty
相关产品推荐
相关产品推荐

