shedlock mysql_ShedLock

Spring 多个微服务做定时任务,如何做负载

基本思路

多个微服务,其业务的逻辑是一样的,自然包括定时任务。负载均衡在执行的时候,到达某个节点以后,定时任务都会执行,可以控制的思路就是使用队列的方式去操作。

如下有两种思路:

将负载均衡的定时任务,从原先的直接执行业务逻辑修改为先将业务逻辑请求到队列中,然后让空闲的微服务去队列中自动领取;

将负载均衡的定时任务,加锁进行操作。

ShedLock

ShedLock does one and only one thing. It makes sure your scheduled tasks are executed at most once at the same time. If a task is being executed on one node, it acquires a lock which prevents execution of the same task from another node (or thread). Please note, that if one task is already being executed on one node, execution on other nodes does not wait, it is simply skipped.

Currently, Spring scheduled tasks coordinated through Mongo, JDBC database, Redis, Hazelcast or ZooKeeper are supported. More scheduling and coordination mechanisms and expected in the future.

ShedLock is not a distributed scheduler

Please note that ShedLock is not and will never be full-fledged scheduler, it's just a lock. If you need a distributed scheduler, please use another project. ShedLock is designed to be used in situations where you have scheduled tasks that are not ready to be executed in parallel, but can be safely executed repeatedly. For example if the task is fetching records from a database, processing them and marking them as processed at the end without using any transaction. In such case ShedLock may be right for you.

By setting lockAtMostFor we make sure that the lock is released even if the node dies and by setting lockAtLeastFor we make sure it's not executed more than once in fifteen minutes. Please note that if the task takes longer than 15 minutes, it will be executed again.

个人理解,拿JDBCTemplate进行说明,就是当第一个微服务执行定时任务的时候,会将此定时任务进行锁操作,然后其他的定时任务就不会再执行,锁操作有一定的时长,超过这个时长以后,再一次,所有的定时任务进行争抢下一个定时任务的执行权利,如此循环。其中两个配置lockAtMostFor和lockAtLeastFor,保证了在一个定时任务的区间内只有一个定时任务在执行,同时也保证了即便是其中的一个定时任务挂掉了,到一定的时间以后,锁也会释放,其他的定时任务依旧会进行执行权的争夺,执行定时任务。

使用步骤

环境支持

Java 8 、slf4j-api、Spring Framework(可选)

依赖引入

net.javacrumbs.shedlock

shedlock-spring

0.18.2

定时任务的写法

import net.javacrumbs.shedlock.core.SchedulerLock;

...

@Scheduled(...)

@SchedulerLock(name = "scheduledTaskName")

public void scheduledTask() {

// do something

}

The @SchedulerLock annotation has several purposes. First of all, only annotated methods are locked, the library ignores all other scheduled tasks. You also have to specify the name for the lock. Only one tasks with the same name can be executed at the same time.

You can also set lockAtMostFor attribute which specifies how long the lock should be kept in case the executing node dies. This is just a fallback, under normal circumstances the lock is released as soon the tasks finishes.

Lastly, you can set lockAtLeastFor attribute which specifies minimum amount of time for which the lock should be kept. Its main purpose is to prevent execution from multiple nodes in case of really short tasks and clock difference between the nodes.

示例

Let's say you have a task which you execute every 15 minutes and which usually takes few minutes to run. Moreover, you want to execute it at most once per 15 minutes. In such case, you can configure it like this

import net.javacrumbs.shedlock.core.SchedulerLock;

...

private static final int FOURTEEN_MIN = 14 * 60 * 1000;

...

@Scheduled(cron = "0 */15 * * * *")

@SchedulerLock(name = "scheduledTaskName", lockAtMostFor = FOURTEEN_MIN, lockAtLeastFor = FOURTEEN_MIN)

public void scheduledTask() {

// do something

}

By setting lockAtMostFor we make sure that the lock is released even if the node dies and by setting lockAtLeastFor we make sure it's not executed more than once in fifteen minutes. Please note that if the task takes longer than 15 minutes, it will be executed again.

配置task

Now we need to integrate the library into Spring. It's done by wrapping standard Spring task scheduler.

import net.javacrumbs.shedlock.spring.SpringLockableTaskSchedulerFactory;

...

@Bean

public ScheduledLockConfiguration taskScheduler(LockProvider lockProvider) {

return ScheduledLockConfigurationBuilder

.withLockProvider(lockProvider)

.withPoolSize(10)

.withDefaultLockAtMostFor(Duration.ofMinutes(10))

.build();

}

Or if you already have an instance of ScheduledExecutorService

@Bean

public TaskScheduler taskScheduler(ScheduledExecutorService executorService, LockProvider lockProvider) {

return SpringLockableTaskSchedulerFactory.newLockableTaskScheduler(executorService, lockProvider, Duration.of(10, MINUTES));

}

配置锁

锁的配置有多种方式的,如下的。

Mongo

Import the project

net.javacrumbs.shedlock

shedlock-provider-mongo

0.18.2

Configure:

import net.javacrumbs.shedlock.provider.mongo.MongoLockProvider;

...

@Bean

public LockProvider lockProvider(MongoClient mongo) {

return new MongoLockProvider(mongo, "databaseName");

}

Please note that MongoDB integration requires Mongo >= 2.4 and mongo-java-driver >= 3.4.0

JdbcTemplate

Create the table

CREATE TABLE shedlock(

name VARCHAR(64),

lock_until TIMESTAMP(3) NULL,

locked_at TIMESTAMP(3) NULL,

locked_by VARCHAR(255),

PRIMARY KEY (name)

)

script for MS SQL is here

CREATE TABLE [dbo].[shedlock](

[name] [varchar](64) NOT NULL,

[lock_until] [datetime] NULL,

[locked_at] [datetime] NULL,

[locked_by] [varchar](255) NOT NULL,

CONSTRAINT [PK_shedlock] PRIMARY KEY CLUSTERED

(

[name] ASC

)WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ON [PRIMARY]

) ON [PRIMARY]

Add dependency

net.javacrumbs.shedlock

shedlock-provider-jdbc-template

0.18.2

Configure:

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

...

@Bean

public LockProvider lockProvider(DataSource dataSource) {

return new JdbcTemplateLockProvider(dataSource);

}

Tested with MySql, Postgres and HSQLDB

Plain JDBC

For those who do not want to use jdbc-template, there is plain JDBC lock provider. Just import

net.javacrumbs.shedlock

shedlock-provider-jdbc

0.18.2

and configure

import net.javacrumbs.shedlock.provider.jdbc.JdbcLockProvider;

...

@Bean

public LockProvider lockProvider(DataSource dataSource) {

return new JdbcLockProvider(dataSource);

}

the rest is the same as with JdbcTemplate lock provider.

Warning

Do not manually delete lock row or document from DB table or Mongo collection. ShedLock has an in-memory cache of existing locks so the row will NOT be automatically recreated until application restart. If you need to, you can edit the row/document, risking only that multiple locks will be held. Since 0.18.2 you can clean the cache by calling clearCache() on LockProvider.

ZooKeeper (using Curator)

Import

net.javacrumbs.shedlock

shedlock-provider-zookeeper-curator

0.18.2

and configure

import net.javacrumbs.shedlock.provider.zookeeper.curator.ZookeeperCuratorLockProvider;

...

@Bean

public LockProvider lockProvider(org.apache.curator.framework.CuratorFramework client) {

return new ZookeeperCuratorLockProvider(client);

}

By default, ephemeral nodes for locks will be created under /shedlock node.

Redis (using Spring RedisConnectionFactory)

Import

net.javacrumbs.shedlock

shedlock-provider-redis-spring

0.18.2

and configure

import net.javacrumbs.shedlock.provider.redis.spring.RedisLockProvider;

...

@Bean

public LockProvider lockProvider(JedisPool jedisPool) {

return new RedisLockProvider(connectionFactory, ENV);

}

Redis (using Jedis)

Import

net.javacrumbs.shedlock

shedlock-provider-redis-jedis

0.18.0

and configure

import net.javacrumbs.shedlock.provider.redis.jedis.JedisLockProvider;

...

@Bean

public LockProvider lockProvider(JedisPool jedisPool) {

return new JedisLockProvider(jedisPool, ENV);

}

Hazelcast

Import the project

net.javacrumbs.shedlock

shedlock-provider-hazelcast

0.18.0

Configure:

import net.javacrumbs.shedlock.provider.hazelcast.HazelcastLockProvider;

...

@Bean

public HazelcastLockProvider lockProvider(HazelcastInstance hazelcastInstance) {

return new HazelcastLockProvider(hazelcastInstance);

}

Spring XML configuration

If you are using Spring XML config, use this configuration

Annotate scheduler method(s)

@SchedulerLock(name = "taskName")

public void run() {

}

Running without Spring

It is possible to use ShedLock without Spring

LockingTaskExecutor executor = new DefaultLockingTaskExecutor(lockProvider);

...

Instant lockAtMostUntil = Instant.now().plusSeconds(600);

executor.executeWithLock(runnable, new LockConfiguration("lockName", lockAtMostUntil));

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值