redis分布式锁、公平锁、spring、redis消息订阅

1-maven
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>
2-配置yml....省略

3-分布式公平锁注解

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.data.redis.connection.RedisConnectionFactory;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.data.redis.listener.RedisMessageListenerContainer;

@Configuration
public class RedisSubConfig {
/**
* redis消息监听器容器
* 可以添加多个监听不同话题的redis监听器,只需要把消息监听器和相应的消息订阅处理器绑定,该消息监听器
* 通过反射技术调用消息订阅处理器的相关方法进行一些业务处理
*/
@Bean
RedisMessageListenerContainer container(RedisConnectionFactory connectionFactory) {
RedisMessageListenerContainer container = new RedisMessageListenerContainer();
container.setConnectionFactory(connectionFactory);
//订阅了一个频道
return container;
}
// redis 读取内容的template
@Bean
StringRedisTemplate template(RedisConnectionFactory connectionFactory) {
return new StringRedisTemplate(connectionFactory);
}
}
 

package com.ruoyi.system.config;

import com.alibaba.fastjson2.JSON;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.aspectj.lang.reflect.MethodSignature;
import org.springframework.context.annotation.Bean;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.data.redis.core.script.DefaultRedisScript;
import org.springframework.data.redis.listener.PatternTopic;
import org.springframework.data.redis.listener.RedisMessageListenerContainer;
import org.springframework.data.redis.listener.adapter.MessageListenerAdapter;
import org.springframework.stereotype.Component;

import java.lang.annotation.*;
import java.util.*;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.locks.LockSupport;


@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)
@Documented
public @interface RedisLock {
  /** 锁的名称 */
String value() default "redis_lock_order";

/**
* 最大持有锁时间,如果超过该时间仍未主动释放,将自动释放锁。
* (这个时间要大于redis的超时断开链接的时间)
* @return 最大持有锁时间 秒
*/
int lockTime() default 10;

@Aspect
@Component
@Slf4j
@RequiredArgsConstructor
class RedisLockAspect {
// redis分布式锁的 消息订阅得服务器标识
public final static String lock_this_sys_name = java.util.UUID.randomUUID().toString().replace("-", "");
private final StringRedisTemplate stringRedisTemplate;
protected static boolean redisDeleteThread = false;
protected static final String lockname = "locknames:";
protected static final String startfu = "${";
protected static final String endfu = "}";

protected void deleteRedisLock() {
if (!redisDeleteThread) {
synchronized ("threadredisdelete") {
if (!redisDeleteThread) {
log.info("启动分布式锁检测程序");
deleteRedisLock1();
new Thread(() -> {
while (true) {
try {
Thread.sleep(1000l);
} catch (InterruptedException e) {
}
try {
deleteRedisLock1();
} catch (Exception e) {
}
}
}, "分布式锁检测程序").start();
redisDeleteThread = true;
}
}
}
}
protected void deleteRedisLock1() {
long redisTime = currentTimeMillis().longValue();
Set<String> keys = stringRedisTemplate.keys(lockname + "*");
for (String key : keys) {
List<String> vals = stringRedisTemplate.opsForList().range(key, 0, -1);
for (String val : vals) {
Map map = JSON.parseObject(val, HashMap.class);
long createTime = Long.parseLong(map.get("redisCreateTime").toString());
long lockTime = Long.parseLong(map.get("lockTime").toString());
if (createTime + (lockTime * 1000) < redisTime) {
Long remove = stringRedisTemplate.opsForList().remove(key, 1, val);
if (0 < remove) {
log.info("删除过期锁(" + key + "):" + val);
}
}
}
}
}

@Pointcut("@annotation(RedisLock)")
public void rlockaspect() {
}


@Around("rlockaspect()")
public Object aroundRemote(ProceedingJoinPoint jp) throws Throwable {
RedisLock rc = ((MethodSignature) jp.getSignature()).getMethod().getAnnotation(RedisLock.class);
int startIndex = rc.value().indexOf(startfu);
int lastIndexOf = rc.value().lastIndexOf(endfu);
String key = rc.value();
if (startIndex != -1 && lastIndexOf != -1) {
key = rc.value().substring(startIndex + startfu.length(), lastIndexOf);
String[] parameters = ((MethodSignature) jp.getSignature()).getParameterNames();
Object[] args = jp.getArgs();
boolean boo = true;
for (int i = 0; i < parameters.length; i++) {
if (parameters[i].equals(key)) {
key = String.valueOf(args[i]);
boo = false;
break;
}
}
if (boo) {
key = rc.value();
} else {
key = rc.value().substring(0, startIndex) + key;
}
}
deleteRedisLock();
String rediskey = lockname + key;
Map map = new HashMap();
map.put("lock_sys_name", lock_this_sys_name);
map.put("uid", UUID.randomUUID().toString().replaceAll("-", ""));
map.put("threadid", Thread.currentThread().getId());
map.put("redisCreateTime", currentTimeMillis());
map.put("lockTime", rc.lockTime());
String redis_values = JSON.toJSONString(map);
stringRedisTemplate.opsForList().rightPush(rediskey, redis_values);
boolean dengdai = true;
while (true) {
String redis_sysinfo = stringRedisTemplate.opsForList().index(rediskey, 0);
if (redis_values.equals(redis_sysinfo)) {
try {
return jp.proceed();
} finally {
stringRedisTemplate.opsForList().remove(rediskey, 1, redis_values);
String next_redis_msg = stringRedisTemplate.opsForList().index(rediskey, 0);
if (null != next_redis_msg) {
Map hashMap = JSON.parseObject(next_redis_msg, HashMap.class);
if (null != hashMap) {
Object o = hashMap.get("lock_sys_name");
if (null != o) {
stringRedisTemplate.convertAndSend(o.toString(), next_redis_msg);
}
}
}
}
} else {
if (dengdai) {
dengdai = false;
LockSupport.parkNanos(rc.lockTime() * 1000000000l);
} else {
break;
}
}
}
stringRedisTemplate.opsForList().remove(rediskey, 1, redis_values);
throw new RuntimeException(rediskey + "锁超时, 请稍等再试");
}

// 接收到redis 向所有人发送的 订阅消息
// 处理redis 锁消息
public void readRedisLockMsg(String message) {
Map hashMap = JSON.parseObject(message, HashMap.class);
Object o_lock_sys_name = hashMap.get("lock_sys_name");
if (null != o_lock_sys_name && o_lock_sys_name.toString().equals(lock_this_sys_name)) {
Object o_threadid = hashMap.get("threadid");
if (o_threadid != null) {
Thread thread = findThread(Long.parseLong(o_threadid.toString()));
if (null != thread) {
LockSupport.unpark(thread);
}
}
}
}

@Bean
RedisMessageListenerContainer setRedislockFactory(RedisMessageListenerContainer container, MessageListenerAdapter readRedisLockMsgBean) {
//listenerAdapter 参数名和 方法名相同
container.addMessageListener(readRedisLockMsgBean, new PatternTopic(lock_this_sys_name));
return container;
}
//监听分布式锁程序,此方法名字要和container.addMessageListener(名字,...) 此名字相同
@Bean
MessageListenerAdapter readRedisLockMsgBean(RedisLock.RedisLockAspect receiver) {
return new MessageListenerAdapter(receiver, "readRedisLockMsg");
}

protected static final String SCRIPT_TIME = "local a=redis.call('TIME'); return (a[1]*1000000+a[2])/1000";
/** 获取当前时间戳,13位 */
public Long currentTimeMillis() {
final DefaultRedisScript<Long> script = new DefaultRedisScript<>(SCRIPT_TIME, Long.class);
return stringRedisTemplate.execute(script, Collections.EMPTY_LIST);
}

/** 通过线程组获得线程 */
public static Thread findThread(long threadId) {
ThreadGroup group = Thread.currentThread().getThreadGroup();
while (group != null) {
Thread[] threads = new Thread[(int) (group.activeCount() * 1.2)];
int count = group.enumerate(threads, true);
for (int i = 0; i < count; i++) {
if (threadId == threads[i].getId()) {
return threads[i];
}
}
group = group.getParent();
}
return null;
}
}
}

 

4-锁的使用
/**
* 分布式锁使用方式1
* @param asd
* @return
*/
@PostMapping(value = "/test1", produces = {"application/json;charset=utf-8"})
public Resp test1(String asd) {
Map map = new HashMap();
map.put("dat1", System.currentTimeMillis());
Map maps = SpringUtils.getBean(this.getClass()).testtt1(map, asd);//经springboot容器调用
return new Resp<>(maps);
}
@RedisLock(value = "${asd}")//调用方必须经过spring容器调用
public Map testtt1(Map map, String asd) {
LockSupport.parkNanos(1000000000l);//线程延时1秒
long asdaa=System.currentTimeMillis();
map.put("dat2", asdaa-Long.parseLong(map.get("dat").toString()));
return map;
}

分布式锁使用方式2:
@Service
public class IndexServiceImpl implements IndexService {

@RedisLock//调用方必须经过spring容器调用
@Override
public Map test() {
LockSupport.parkNanos(1000000000l);
return null;
}
}
 


posted @ 2023-01-10 09:29  Mr·柯  阅读(189)  评论(0编辑  收藏  举报