详细讨论了ShedLock的原理、配置步骤以及在实际项目中的应用场景,为处理复杂的分布式系统任务提供了有力支持。
在当今的分布式计算环境中,协调多个节点之间的任务执行,确保它们在没有冲突或重复的情况下执行,面临着重大挑战。无论是管理周期性任务、批处理过程还是关键系统任务,保持同步和一致性对于无缝运行至关重要。
问题
假设我们需要按计划运行某些任务,无论是数据库清理任务还是某些数据生成任务。如果直接解决这个问题,你可以使用Spring Framework中包含的`@Schedules`注解来解决这个问题。该注解允许您按固定间隔或按cron计划运行代码。但是,如果我们的服务实例数量超过一个怎么办?在这种情况下,任务将在我们的每个服务实例上执行。
ShedLock
ShedLock确保您的定时任务在同一时间最多只执行一次。该库通过外部存储实现锁。如果一个任务在一个实例上执行,锁被设置,所有其他实例不等待,并跳过任务的执行。这实现了“最多执行一次”。外部存储可以是关系型数据库(PostgreSQL、MySQL、Oracle等)通过JDBC工作,NoSQL(Mongo、Redis、DynamoDB)以及许多其他存储(完整列表可以在项目页面上找到)。
让我们以使用PostgreSQL为例。首先,让我们使用Docker启动数据库:
docker run -d -p 5432:5432 --name db \ -e POSTGRES_USER=admin \ -e POSTGRES_PASSWORD=password \ -e POSTGRES_DB=demo \ postgres:alpine
现在需要创建一个锁表。在项目页面上,我们需要找到针对PostgreSQL的SQL脚本:
CREATE TABLE shedlock( name VARCHAR(64) NOT NULL, lock_until TIMESTAMP NOT NULL, locked_at TIMESTAMP NOT NULL, locked_by VARCHAR(255) NOT NULL, PRIMARY KEY (name) );
这里:
`name` - 锁的唯一标识符,通常表示被锁定的任务或资源
`lock_until` - 指示持有锁的结束时间的时间戳
`locked_at` - 指示获取锁的时间戳
`locked_by` - 获取锁的实体的标识符(例如,应用程序实例)
接下来,创建一个Spring Boot项目并在`build.gradle`中添加必要的依赖项:
implementation 'net.javacrumbs.shedlock:shedlock-spring:5.10.2' implementation 'net.javacrumbs.shedlock:shedlock-provider-jdbc-template:5.10.2'
现在描述配置:
@Configuration @EnableScheduling @EnableSchedulerLock(defaultLockAtMostFor = "10m") public class ShedLockConfig { @Bean public LockProvider lockProvider(DataSource dataSource) { return new JdbcTemplateLockProvider( JdbcTemplateLockProvider.Configuration.builder() .withJdbcTemplate(new JdbcTemplate(dataSource)) .usingDbTime() .build() ); } }
让我们创建一个`ExampleTask`,每分钟开始一次并执行一些耗时动作。为此,我们将使用`@Scheduled`注解:
@Service public class ExampleTask { @Scheduled(cron = "0 * * ? * *") @SchedulerLock(name = "exampleTask", lockAtMostFor = "50s", lockAtLeastFor = "20s") public void scheduledTask() throws InterruptedException { System.out.println("task scheduled!"); Thread.sleep(15000); System.out.println("task executed!"); } }
在这里,我们使用`Thread.sleep`模拟任务的执行时间为15秒。一旦应用程序启动并任务执行开始,将在数据库中插入一条记录。
如果同时,另一个应用程序尝试运行任务,它将无法获取锁并跳过任务执行:
2024-02-18 08:08:50.057 DEBUG 45988 --- [ scheduling-1] n.j.s.core.DefaultLockingTaskExecutor : Not executing 'exampleTask'. It's locked.
在第一个应用程序获取锁时,会在数据库中创建一条记录,该记录的锁时间等于锁设置中的`lockAtMostFor`。这个时间是必要的,以确保锁不会永远设置,以防应用程序崩溃或由于某种原因终止(例如,在Kubernetes中从一个节点驱逐Pod到另一个节点)。成功执行任务后,应用程序将更新数据库条目,并将锁定时间减少到当前时间,但如果任务执行时间非常短,则此值不能小于配置中的`lockAtLeastFor`。此值有助于最大程度地减少实例之间的时钟不同步。它确保您的定时任务只被同时执行一次。
结论
ShedLock是协调复杂Spring应用程序中任务的有用工具。它确保任务顺利运行且仅运行一次,即使跨多个实例也是如此。它易于设置,并为Spring应用程序提供了可靠的任务处理能力,使其成为处理分布式系统的任何人都很有价值的工具。文章来源:https://www.toymoban.com/diary/apps/714.html
项目代码可在GitHub上找到。https://github.com/vshago/shedlock-demo文章来源地址https://www.toymoban.com/diary/apps/714.html
到此这篇关于分布式任务同步:在Spring中利用ShedLock的文章就介绍到这了,更多相关内容可以在右上角搜索或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!