shedLock now only executes the method of one instance at a time + redis implements distributed timing tasks

Original address: https://blog.csdn.net/qq_32182637/article/details/111871188

https://blog.csdn.net/qq_35913663/article/details/124910631

written in front

This article is only a record and summary of my own practice after referring to other articles recently. There are still many deficiencies in the scene and details, which need to be supplemented and corrected.

overview

ShedLock does only one thing. It ensures that scheduled tasks are executed at most once at a time. If a task is executing on one node, it acquires a lock which prevents execution of the same task from another node (or thread). Note that if a task is already executed on one node, execution on other nodes will not wait, it will just skip it.

Currently, Spring scheduled tasks coordinated via Mongo, JDBC databases, Redis, Hazelcast or ZooKeeper are supported. Additional timing and coordination mechanisms are expected in the future.

ShedLock needs to use the @SchedulerLock annotation to implement locks for a certain method. Distributed scenarios are the main and typical scenarios for its application, but this does not mean that ShedLock can only be used in distributed environments. It depends on your understanding of his principles. How to use it.

Brief Analysis of ShedLock Principle

The principle of ShedLock is very simple. First look at the @SchedulerLock annotation class:

@Target({ElementType.METHOD, ElementType.ANNOTATION_TYPE})

@Retention(RetentionPolicy.RUNTIME)

public @interface SchedulerLock {

    String name() default "";

    long lockAtMostFor() default -1L;

    String lockAtMostForString() default "";

    long lockAtLeastFor() default -1L;

    String lockAtLeastForString() default "";

}

1

2

3

4

5

6

7

8

9

10

11

12

13

According to the annotation class, we can know

Can be used on methods (ElementType.METHOD), which is a common way of annotation

It can be used on other annotation classes (ElementType.ANNOTATION_TYPE). I don't know how to apply this situation. I have tried it briefly, but it has no desired effect

The name attribute indicates the name of the lock, which is case-sensitive

lockAtMostFor takes the lock for the longest time, in milliseconds

lockAtMostForString The longest time to occupy the lock, character type, "PT1S" means 1 second, "PT1M" means 1 minute, if it is set at the same time as the lockAtMostFor attribute, lockAtMostFor shall prevail

lockAtLeastFor The minimum time for occupying the lock, in milliseconds

lockAtLeastForString The shortest time of occupying the lock, character type, "PT1S" means 1 second, "PT1M" means 1 minute, if it is set at the same time as the lockAtLeastFor attribute, lockAtLeastFor shall prevail

The implementation of ShedLock relies on external storage, including conventional databases, redis caches, etc. However, no matter which method is used, the stored data content is basically the same. Take the mysql database as an example:

Field name Description

name The name of the lock, unique value, case sensitive

lock_until The end time of lock occupation = locked_at +lockAtMostFor

locked_at starts to occupy the lock time

locked_by occupant, usually hostname

Observing the source code, it can be found that when a lock needs to be obtained, it will try to insert a record into the shedlock table, and name is used as the primary key. If there is no lock with the same name, the record will be inserted and the lock will be successfully occupied

INSERT INTO  shedlock(name, lock_until, locked_at, locked_by) VALUES(?, ?, ?, ?)

1

If there is already a lock with the same name, the unique constraint of the primary key will be violated, and the insertion will fail. At this time, it will try to filter out records with the same lock name in the shedlock table and have been released (lock_until<current time). If there are records that meet the conditions, update the records record, and successfully occupied the lock

UPDATE shedlock SET lock_until = ?, locked_at = ?, locked_by = ? WHERE name = ? AND lock_until <= ?

1

If no eligible records can be filtered out, it means that the acquisition of the lock has failed, and the execution task will be abandoned.

After the execution of the task method is completed, if the current time has not exceeded the lock_until time, the lock_until time of the updated record is locked_at+lockAtLeastFor, that is, the duration of occupying the lock is changed to the minimum duration

In addition, it should be noted that ShedLock is a time-based lock mechanism. In a distributed scenario, if different nodes are deployed on different hosts, the time of the host will be used by default. At this time, it is necessary to emphasize the time synchronization between the hosts. Of course In the higher version of the dependency package, it will provide the host time for setting the external storage, such as the database host time

For more details, please refer to another article "SchedulerLock Distributed Lock Principle"

ShedLock+Mysql

First you need to import dependencies

<!--This dependency is the shedlock core dependency package, which is in contact with spring, and sometimes the wrong version will cause it to fail-->

<dependency>

   <groupId>net.javacrumbs.shedlock</groupId>

    <artifactId>shedlock-spring</artifactId>

    <version>2.2.0</version>

</dependency>

<!--Required for database access-->

<dependency>

    <groupId>net.javacrumbs.shedlock</groupId>

    <artifactId>shedlock-provider-jdbc-template</artifactId>

    <version>2.2.0</version>

</dependency>

1

2

3

4

5

6

7

8

9

10

11

12

Create database tables

CREATE TABLE shedlock (

  name varchar(64) COLLATE utf8mb4_bin NOT NULL,

  lock_until timestamp(3) NOT NULL,

  locked_at timestamp(3) NOT NULL DEFAULT CURRENT_TIMESTAMP(3),

  locked_by varchar(255) COLLATE utf8mb4_bin NOT NULL,

  PRIMARY KEY (name)

) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_bin;

1

2

3

4

5

6

7

Implement a configuration class to provide a LockProvider

import net.javacrumbs.shedlock.core.LockProvider;

import net.javacrumbs.shedlock.provider.jdbctemplate.JdbcTemplateLockProvider;

import net.javacrumbs.shedlock.spring.annotation.EnableSchedulerLock;

import org.springframework.context.annotation.Bean;

import org.springframework.context.annotation.Configuration;

import org.springframework.jdbc.core.JdbcTemplate;

import org.springframework.scheduling.annotation.EnableScheduling;

import javax.sql.DataSource;

import static net.javacrumbs.shedlock.provider.jdbctemplate.JdbcTemplateLockProvider.Configuration.builder;

@Configuration

@EnableScheduling

@EnableSchedulerLock(defaultLockAtMostFor = "PT30S")

public class SchedulerConfiguration {

    @Bean

    public LockProvider lockProvider(DataSource dataSource) {

//You can customize the data source, which can be considered as a consideration, and generally do not use this

        org.apache.tomcat.jdbc.pool.DataSource dataSource1 = new org.apache.tomcat.jdbc.pool.DataSource();

        dataSource1.setUrl("jdbc:mysql://127.0.0.1:3601/ICSP?useUnicode=true&characterEncoding=utf8&useSSL=false");

        dataSource1.setUsername("tom");

        dataSource1.setPassword("tommy");

        LockProvider lockProvider= new JdbcTemplateLockProvider(builder()

        //Specify the table name

                .withTableName("shedlock")

                //Specify the data source, generally use dataSource instead of manually defined data source

                .withJdbcTemplate(new JdbcTemplate(dataSource1))

                //Specify the name of the table field, the number of fields is fixed, only the name can be changed, and only the higher version of shedlock-provider-jdbc-template dependency provides this configuration item

//                .withColumnNames(new JdbcTemplateLockProvider.ColumnNames("name","lock_until","locked_at","locked_by"))

//Use the database time, only the higher version of shedlock-provider-jdbc-template dependency provides this configuration item

//                .usingDbTime()

//The function is unknown, only the higher version shedlock-provider-jdbc-template dependency provides this configuration item

//                .withLockedByValue("myvalue")

//The function is unknown, only the higher version shedlock-provider-jdbc-template dependency provides this configuration item

//                .withIsolationLevel(1)

                .build());

        return  lockProvider;

    }

}

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

36

37

38

39

40

41

42

43

44

Regarding the above configuration, some supplementary instructions need to be made:

@EnableScheduling needs to be added only when using scheduled tasks, which is also the most typical application scenario of shedlock

The @EnableSchedulerLock annotation removes the possibility to set the default longest and shortest time for occupying the lock. There is also a mode parameter, and the values ​​​​that can be set are:

value description

EnableSchedulerLock.InterceptMode.PROXY_SCHEDULER is used with @Scheduled

EnableSchedulerLock.InterceptMode.PROXY_METHOD is used alone on the method, which means that as long as the method is called, it will try to occupy the lock, which is another usage scenario of shedlock

Shedlock-provider-jdbc-template version adds JdbcTemplateLockProvider.Configuration.builder method comparison

Version Add method

4.1.0 .withColumnNames

.withLockedByValue

4.9.0 .usingDbTime

4.27.0 .withIsolationLevel

4. If you use a higher shedlock-provider-jdbc-template dependent version, you also need to use a higher version of shedlock-spring dependent version, otherwise an error will be reported during execution. And if a higher shedlock-spring dependency version is used, it may also need to match the dependency version of the spring context (just a guess, which dependency has not been confirmed). It can be confirmed that the excessive shedlock-spring dependency The version will cause that when registering a Task, the task will not be set as LockabaleRunable, but an ordinary ScheduledMethodRunable. The direct impact is that the scheduled task shedlock will fail.

So I just found a problem here, but I don't know how to solve it. If you know that you want to use shedlock-provider-jdbc-template version 4.27.0 and above, you can use other dependent packages. Welcome inform

Then you can use the @SchedulerLock annotation

ShedLock+Redis

1. pom file

Redis must have:

<dependency>

    <groupId>org.springframework.boot</groupId>

    <artifactId>spring-boot-starter-data-redis</artifactId>

</dependency>

shedlock required items:

<dependency>

    <groupId>net.javacrumbs.shedlock</groupId>

    <artifactId>shedlock-spring</artifactId>

    <version>4.19.1</version>

</dependency>

Shedlock options (redis is used here, other databases can also be used, see the official website https://github.com/lukas-krecan/ShedLock for details):

<dependency>

    <groupId>net.javacrumbs.shedlock</groupId>

    <artifactId>shedlock-provider-redis-spring</artifactId>

    <version>2.5.0</version>

</dependency>

————————————————

@Configuration

@EnableScheduling

@EnableSchedulerLock(defaultLockAtMostFor = "10m")

public class ShedLockConfig {

    @Resource

    RedisTemplate<String, Object> redisTemplate;

    @Bean

    public LockProvider lockProvider() {

        return new RedisLockProvider(redisTemplate.getConnectionFactory());

    }

}

@Slf4j

@Component

public class TaskRun {

    @Scheduled(cron = "0 0 */1 * * ?")

    @SchedulerLock(name = "fylr")

    public void fylr() {

        System.out.println("run ...");

    }

}

ShedLock+Mongo

To be added

ShedLock+ZooKeeper

To be added

————————————————

Copyright statement: This article is an original article by CSDN blogger "Zhao Jiaen", following the CC 4.0 BY-SA copyright agreement, please attach the original source link and this statement for reprinting.

Original link: https://blog.csdn.net/qq_35913663/article/details/124910631

Guess you like

Origin blog.csdn.net/zhongguowangzhan/article/details/127312995