クラスター環境では、べき等でない操作のタイミングタスクは通常1回しか実行できません。ただし、プログラムは各サーバーに分散されて実行されます。このとき、redis分散ロックを使用して、タイミングタスクを1回だけ実行できるようにすることができます。以下はデモです。
ローカルで2つのプログラムを開始しました。各プログラムには、分散環境をシミュレートするためのまったく同じコンテンツのタイミングタスクが含まれています。
1分あたり1ラウンドの頻度で、タイミングタスクの各ラウンドに対して1つのプログラムのみが実行されていることがわかります。
特定のロジックは、タイミングタスクでビジネスロジックを実行するかどうかを決定する操作を追加することです。この判断の基準は、redis + keyによって達成されます。
ビジネスロジックを実行する前に、setIfAbsentメソッドを使用してredisにキーがあるかどうかを判断します。キーがない場合はキーが設定されます。設定が成功するとtrueが返されます。この時点でキーはロック解除された状態であり、キーが含まれている場合はfalseを返します。
タイミングタスクコードは次のとおりです。
import com.baoji.service.DistributeLocker;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.stereotype.Component;
import java.text.ParseException;
/**
* Created by dearx on 2021/01/21.
*/
@Component
public class TestGenerater1 {
private static final Logger LOGGER = LoggerFactory.getLogger(TestGenerater1.class);
@Autowired
private DistributeLocker distributeLocker;
//每隔1分钟执行一次
@Scheduled(cron = "0 */1 * * * ?")
private void configureTasks() throws ParseException {
boolean locked = distributeLocker.lock("test-DingShiRengWu", distributeLocker.LOCK_TIME_ONE_HOUR);
if(!locked){
LOGGER.info("定时任务aaa已经被执行");
distributeLocker.unlock("test-DingShiRengWu");
}else{
LOGGER.info("定时任务aaa开始执行");
distributeLocker.lock("test-DingShiRengWu");
try {
Thread.currentThread().sleep(5000);
/*业务逻辑处理*/
} catch (InterruptedException e) {
e.printStackTrace();
}
distributeLocker.unlock("test-DingShiRengWu");
LOGGER.info("定时任务aaa执行完毕");
}
LOGGER.info("----------------这一轮执行结束,开始下一轮-------------");
}
}
redis関連のコードは次のとおりです。
import java.util.concurrent.TimeUnit;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.BoundValueOperations;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Component;
@Component
public class DistributeLocker {
private static final Logger LOGGER = LoggerFactory.getLogger(DistributeLocker.class);
private static final String LOCK = "TRUE";
private static final long DEFAULT_LOCK_TIME = 60000L;
public static final long LOCK_TIME_ONE_HOUR = 60 * 60000L;
public static final long LOCK_TIME_HALF_HOUR = 30 * 60000L;
public static final long LOCK_TIME_FIVE_MINS = 5 * 60000L;
@Autowired
private RedisTemplate<String, String> stringRedisTemplate;
/**
* lock the operation with the default time, the default lock time is 60 seconds
* @param key the given key
* @return
*/
public boolean lock(String key) {
return lock(key, DEFAULT_LOCK_TIME);
}
/**
* lock the operation with the given key
* @param key the given key
* @param lockTime unit is milliseconds, the lock time should greater than estimated operation time
* @return true if lock success
*/
public boolean lock(String key, long lockTime) {
BoundValueOperations<String, String> operations = stringRedisTemplate.boundValueOps(key);
boolean lockSuccess = operations.setIfAbsent(LOCK);
if(lockSuccess) {
operations.expire(lockTime, TimeUnit.MILLISECONDS);
}
return lockSuccess;
// RedisConnection connection = null;
// try {
// connection = stringRedisTemplate.getConnectionFactory().getConnection();
// lockSuccess = connection.setNX(key.getBytes(Charset.forName("UTF-8")), LOCK.getBytes(Charset.forName("UTF-8")));
// if(lockSuccess) {
// connection.expire(key.getBytes(Charset.forName("UTF-8")), lockTime);
// }
// } finally {
// connection.close();
// }
}
/**
* unlock the operation with the given key
* @param key
*/
public void unlock(String key) {
stringRedisTemplate.delete(key);
}
}