当前位置:网站首页>Implementing redis distributed locks using custom annotations
Implementing redis distributed locks using custom annotations
2022-06-29 15:06:00 【In a flash】
1、RedisLockServer
Definition Redis locked 、 Unlock implementation class
import lombok.extern.slf4j.Slf4j;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.data.redis.core.script.RedisScript;
import org.springframework.stereotype.Component;
import javax.annotation.Resource;
import java.util.Collections;
import java.util.List;
import java.util.concurrent.TimeUnit;
@Component
@Slf4j
public class RedisLockServer {
@Resource
private StringRedisTemplate stringRedisTemplate;
public Boolean setLock(String lockKey, String value, long time) {
if(time<=0){
// Do not set expiration time
return stringRedisTemplate.opsForValue().setIfAbsent(lockKey, value);
}
return stringRedisTemplate.opsForValue().setIfAbsent(lockKey, value, time, TimeUnit.MILLISECONDS);
}
public void deleteLock(String lockKey, String value) {
List<String> lockKeys = Collections.singletonList(lockKey);
String lua = "if redis.call('get', KEYS[1]) == ARGV[1] then redis.call('del', KEYS[1]) return 1 else return 0 end";
RedisScript<Long> luaScript = RedisScript.of(lua, Long.class);
// Delete lock
stringRedisTemplate.execute(luaScript, lockKeys, value);
}
}
2、RedisLock
Definition “ Custom annotation ”, It is used to realize the distributed lock
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface RedisLock {
/** * Locking method ,key The position of the parameter , Index from 0 Start * @return */
int keyNum();
/** * Lock duration , Default setting time * @return */
long lockTime() default 0;
/** * Try time , Try to acquire the lock by spin consensus within the set time , Default 0ms * @return */
long tryTime() default 0;
}
Field description :
- keyNum:
keyNumIt means to be@RedisLockThe way to decorate , The number of parameters indicates lockedkey, Default first parameter , You can also specify key The location of , And the name of the parameter must bekeyName
// key Is the first parameter value
void test(String keyName){
}
// key Is the value of the third parameter
@RedisLock(keyNum=2)
void test(int a,int b,String keyName){
}
- lockTime:
lockTimeIndicates the lock expiration time , The default is0, It means that no locking treatment will be carried out ;
public Boolean setLock(String lockKey, String value, long time) {
if(time<=0){
// Do not set expiration time
return stringRedisTemplate.opsForValue().setIfAbsent(lockKey, value);
}
return stringRedisTemplate.opsForValue().setIfAbsent(lockKey, value, time, TimeUnit.MILLISECONDS);
}
- tryTime:
tryTimeIndicates the time when the lock was attempted , When the set time , Always acquire the lock by spinning
3、RedisLockAspect
Facet class definition , The code is as follows :
import lhz.lx.config.RedisLockServer;
import lombok.extern.slf4j.Slf4j;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.Signature;
import org.aspectj.lang.annotation.*;
import org.aspectj.lang.reflect.CodeSignature;
import org.aspectj.lang.reflect.MethodSignature;
import org.springframework.stereotype.Component;
import javax.annotation.Resource;
import java.lang.reflect.Method;
import java.util.UUID;
@Aspect
@Component
@Slf4j
public class RedisLockAspect {
@Resource
private RedisLockServer redisLockServer;
private static final ThreadLocal<String> VALUE_THREAD = new ThreadLocal<>();
private static final ThreadLocal<String> KEY_THREAD = new ThreadLocal<>();
private static final ThreadLocal<Boolean> LOCK_THREAD = new ThreadLocal<>();
@Pointcut("@annotation(lhz.lx.aspect.RedisLock)")
public void lockPoint() {
}
/** * Surrounding the notification , Calling the target method */
@Around("lockPoint()")
public Object doAround(ProceedingJoinPoint proceedingJoinPoint) throws Throwable {
// Record the method execution start time
long startTime = System.currentTimeMillis();
Object[] args = proceedingJoinPoint.getArgs();
if(args.length<=0){
throw new RuntimeException("keyName non-existent !");
}
String[] argNames = ((CodeSignature) proceedingJoinPoint.getSignature()).getParameterNames();
Signature signature = proceedingJoinPoint.getSignature();
MethodSignature methodSignature = (MethodSignature) signature;
Method method = methodSignature.getMethod();
RedisLock lock = method.getAnnotation(RedisLock.class);
int keyNum = lock.keyNum();
if(!"keyName".equals(argNames[keyNum])){
throw new RuntimeException("keyName non-existent !");
}
String key = args[keyNum].toString();
long lockTime = lock.lockTime();
long tryTime = lock.tryTime();
String value = UUID.randomUUID().toString();
VALUE_THREAD.set(value);
KEY_THREAD.set(key);
log.info(" Distributed lock lock ,key:{},value:{},lockTime:{}",key,value,lockTime);
Boolean setLock = redisLockServer.setLock(key, value, lockTime);
while (!setLock){
// retry
setLock = redisLockServer.setLock(key, value, lockTime);
if(System.currentTimeMillis()-startTime>tryTime) {
LOCK_THREAD.set(false);
log.error(" Lock failed ");
throw new RuntimeException(" Lock failed !");
}
}
log.info(" The distributed lock is locked successfully ,key:{},value:{},lockTime:{}",key,value,lockTime);
LOCK_THREAD.set(true);
// Calling the target method
return proceedingJoinPoint.proceed();
}
/** * Execute... After processing the request * * @param joinPoint Tangent point */
@AfterReturning(value = "lockPoint()", returning = "jsonResult")
public void doAfterReturning(JoinPoint joinPoint, Object jsonResult) {
handleData();
}
/** * Intercept abnormal operations * * @param joinPoint Tangent point * @param e abnormal */
@AfterThrowing(value = "lockPoint()", throwing = "e")
public void doAfterThrowing(JoinPoint joinPoint, Exception e) {
handleData();
}
private void handleData() {
try {
String value = VALUE_THREAD.get();
String key = KEY_THREAD.get();
if(LOCK_THREAD.get()) {
log.info(" Distributed lock unlock ,key:{},value:{}", key, value);
redisLockServer.deleteLock(key, value);
}
} catch (Exception exception) {
exception.printStackTrace();
} finally {
VALUE_THREAD.remove();
KEY_THREAD.remove();
LOCK_THREAD.remove();
}
}
}
4、 Turn on auto agent
Because it means AOP Cannot intercept calls between methods inside a class , You need to add... To the startup class @EnableAspectJAutoProxy To configure , The code is as follows :
@SpringBootApplication
@EnableAspectJAutoProxy(exposeProxy = true)
public class RedisDemoApplication {
public static void main(String[] args) {
SpringApplication.run(RedisDemoApplication.class, args);
}
}
For example, direct method calls ,A Method call B Method , Use as follows :
public class RedisServiceImpl implements RedisService{
public void A() {
// Method class calls
RedisServiceImpl service = (RedisServiceImpl) AopContext.currentProxy();
service.B();
}
@RedisLock
public void B() {
}
}
5、 Use
Be careful : When invoking methods that use distributed locks , Need to enter try…catch…, And in catch Handle locking failure in ;
TestController:
@RestController
@RequestMapping
@Slf4j
public class TestController {
@Resource
private RedisService redisService;
@GetMapping(value = "/test")
public String test() {
redisService.test();
return "success";
}
@GetMapping(value = "/test2")
public String test2() {
String key = UUID.randomUUID().toString();
redisService.test2(key);
return "success";
}
}
RedisService :
public interface RedisService {
/** * Method internal calls use locks */
void test();
/** * Method is called directly to use the lock * @param keyName */
void test2(String keyName);
}
RedisServiceImpl:
@Service
@Slf4j
public class RedisServiceImpl implements RedisService{
/** * Method internal calls use locks */
@Override
public void test() {
log.info(" Method internal calls use locks ");
// Call internal method
RedisServiceImpl service = (RedisServiceImpl) AopContext.currentProxy();
// When invoking methods that use distributed locks , Need to enter try...catch..., And in catch Handle locking failure in
try {
service.testLock("test11");
}catch (Exception e){
e.printStackTrace();
}
}
/** * Method is called directly to use the lock */
@Override
@RedisLock(keyNum = 0)
public void test2(String keyName) {
log.info(" Method is called directly to use the lock ");
}
/** * Locked method , There must be a name "keyName" Parameters of * @param keyName */
@RedisLock
public void testLock(String keyName) throws InterruptedException {
log.info(keyName);
}
}
6、 test
Test one :
Normal locking conditions 
Test two : Set up tryTime, also tryTime Less than lockTime; When configured like this , When the first thread does not end , The second thread , exceed tryTime Lock failure will occur ;
Modify the code as follows :
@RedisLock(keyNum = 0,lockTime = 3000,tryTime = 2000)
public void testLock(String keyName) throws InterruptedException {
log.info(keyName);
TimeUnit.SECONDS.sleep(5);
}
Fast Ask twice Interface , The screenshot is as follows :
You can see through the screenshot , After the first thread is locked , After that 2000ms The prompt "locking failed" appears ;
Test three :
Set up tryTime, also tryTime Greater than lockTime; When configured like this , There will be no lock failure , And the second thread will wait until the first thread ends ;
Modify the code as follows :
@RedisLock(keyNum = 0,lockTime = 3000,tryTime = 4000)
public void testLock(String keyName) throws InterruptedException {
log.info(keyName);
TimeUnit.SECONDS.sleep(5);
}
Fast Ask twice Interface , The screenshot is as follows :
You can see through the screenshot , After the first thread is locked , More than the 3000ms after , The second thread starts locking successfully
边栏推荐
- Redis installation in windows and Linux Environment
- JS will have variable promotion and function promotion
- Lumiprobe 活性染料丨羧酸:Sulfo-Cyanine7.5羧酸
- 知乎热议:一个程序员的水平能差到什么程度?
- ROS 笔记(10)— launch 文件启动
- 威高血液净化冲刺香港:年营收29亿 净利降12.7%
- 异步神器CompletableFuture
- Digital IC code -- traffic lights
- 卫星运动的微分方程
- Hi, you have a code review strategy to check
猜你喜欢

Illustration of Ctrip quarterly report: net revenue of RMB 4.1 billion has been "halved" compared with that before the outbreak

第九章 APP项目测试(4) 测试工具

Ink drop typesetting

MySQL的json 数组操作 json_array_append、json_array_insert

威高血液净化冲刺香港:年营收29亿 净利降12.7%

CKS CKA CKAD 将终端更改为远程桌面

Solidworks零件图存放位置更改后装配图识别不出来的解决办法

Whitelabel error page access

The 5th China software open source innovation competition | opengauss track live training

携程季报图解:净营收41亿 与疫情前相比已被“腰斩”
随机推荐
知识点:PCB线路板布线都有哪些诀窍?
威高血液净化冲刺香港:年营收29亿 净利降12.7%
Chinese garbled code output from idea output station
Whitelabel error page access
Lumiprobe 活性染料丨杂染料:BDP FL 神经酰胺
MCS:离散随机变量——Poisson分布
Unity C# 基础复习27——委托示例(P448)
ROS 笔记(10)— launch 文件启动
CKS CKA CKAD 将终端更改为远程桌面
目前股票开户安全吗?可以直接网上开户吗
Is Guangzhou futures regular? If someone asks you to log in with your own mobile phone and help open an account, is it safe?
JS 会有变量提升和函数提升
Huashu high tech rushes to the scientific innovation board: the actual controller xuxiaoshu and his son, who plan to raise 660million yuan, are both American nationals
China arsenic trioxide industry research and future forecast report (2022 Edition)
I want to search the hundreds of nodes in the data warehouse. Can I check a table used in the SQL
word如何自动生成目录
Draw a slash on a plane coordinate
Lumiprobe 点击化学丨非荧光叠氮化物:叠氮化物-PEG3-OH
const用法精讲
如临现场的视觉感染力,NBA决赛直播还能这样看?