com.jeesuite.common2.lock.redis.RedisLockCoordinator Maven / Gradle / Ivy
The newest version!
package com.jeesuite.common2.lock.redis;
import static com.jeesuite.cache.redis.JedisProviderFactory.getJedisCommands;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Vector;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicLong;
import javax.annotation.PreDestroy;
import org.apache.commons.lang3.RandomStringUtils;
import org.apache.commons.lang3.StringUtils;
import org.apache.commons.lang3.Validate;
import com.jeesuite.cache.redis.JedisProvider;
import com.jeesuite.cache.redis.JedisProviderFactory;
import com.jeesuite.cache.redis.sentinel.JedisSentinelProvider;
import com.jeesuite.cache.redis.standalone.JedisStandaloneProvider;
import com.jeesuite.common.util.ResourceUtils;
import redis.clients.jedis.BinaryJedis;
import redis.clients.jedis.Jedis;
import redis.clients.jedis.JedisPoolConfig;
import redis.clients.jedis.JedisPubSub;
/**
*
* @description
* @author vakin
* @date 2018年3月22日
*/
public class RedisLockCoordinator {
private static final String DLOCK_GROUP_NAME = "_redisLock";
private static final int HEALTH_CHECK_PERIOD = 30000;
private static final String REDIS_LOCK_ACTIVE_NODES_KEY = "RedisLockActiveNodes";
private static final long CLEAN_TIME = TimeUnit.SECONDS.toMillis(90);
private static final String SPLIT_STR = "$$";
private static final String EVENT_NODE_ID = RandomStringUtils.random(8, true, true);
private static String channelName = "redisLockCoordinator";
private AtomicLong eventIdSeq = new AtomicLong(0);
private List activeNodeIds = new ArrayList<>();
private ScheduledExecutorService checkerSchedule;
private Jedis subClient;
private Map> getLockEventIds = new ConcurrentHashMap<>();
public RedisLockCoordinator() {
String mode = ResourceUtils.getProperty("jeesuite.lock.redis.mode", ResourceUtils.getProperty("jeesuite.cache.mode","standalone"));
String server = ResourceUtils.getProperty("jeesuite.lock.redis.servers", ResourceUtils.getProperty("jeesuite.cache.servers"));
String datebase = ResourceUtils.getProperty("jeesuite.lock.redis.datebase", ResourceUtils.getProperty("jeesuite.cache.datebase","0"));
String password = ResourceUtils.getProperty("jeesuite.lock.redis.password", ResourceUtils.getProperty("jeesuite.cache.password"));
String maxPoolSize = ResourceUtils.getProperty("jeesuite.lock.redis.maxPoolSize", ResourceUtils.getProperty("jeesuite.cache.maxPoolSize","100"));
Validate.notBlank(server, "config[jeesuite.lock.redis.servers] not found");
JedisPoolConfig poolConfig = new JedisPoolConfig();
poolConfig.setMaxIdle(1);
poolConfig.setMinEvictableIdleTimeMillis(30*60*1000);
poolConfig.setMaxTotal(Integer.parseInt(maxPoolSize));
poolConfig.setMaxWaitMillis(5 * 1000);
String[] servers = server.split(";|,");
int timeout = 3000;
if("standalone".equals(mode)){
JedisProvider provider = new JedisStandaloneProvider(DLOCK_GROUP_NAME, poolConfig, servers, timeout, password, Integer.parseInt(datebase),null);
JedisProviderFactory.addProvider(provider);
}else if("sentinel".equals(mode)){
String masterName = ResourceUtils.getProperty("jeesuite.lock.redis.masterName", ResourceUtils.getProperty("jeesuite.cache.masterName"));
Validate.notBlank(masterName, "config[jeesuite.lock.redis.masterName] not found");
JedisSentinelProvider provider = new JedisSentinelProvider(DLOCK_GROUP_NAME, poolConfig, servers, timeout, password, Integer.parseInt(datebase), null, masterName);
JedisProviderFactory.addProvider(provider);
}
Jedis redisClient = getRedisClient();
redisClient.hset(REDIS_LOCK_ACTIVE_NODES_KEY, EVENT_NODE_ID, String.valueOf(System.currentTimeMillis()));
release(redisClient);
activeNodeIds.add(EVENT_NODE_ID);
// checkerSchedule = Executors.newScheduledThreadPool(1);
// checkerSchedule.scheduleAtFixedRate(new Runnable() {
// @Override
// public void run() {
// if(!subClient.isConnected()){
// subClient.connect();
// }
// //
// checkActiveNode();
// }
// }, 0, HEALTH_CHECK_PERIOD - 1000, TimeUnit.MILLISECONDS);
Thread thread = new Thread(new Runnable() {
@Override
public void run() {
subClient = getRedisClient();
subClient.subscribe(new LockStateListener(), new String[]{channelName});
}
}, channelName);
thread.setDaemon(true);
thread.start();
}
private void checkActiveNode(){
Jedis redisClient = getRedisClient();
try {
long currentTime = System.currentTimeMillis();
Map map = redisClient.hgetAll(REDIS_LOCK_ACTIVE_NODES_KEY);
Iterator> iterator = map.entrySet().iterator();
while(iterator.hasNext()){
Entry entry = iterator.next();
if(currentTime - Long.parseLong(entry.getValue()) > HEALTH_CHECK_PERIOD){
redisClient.hdel(REDIS_LOCK_ACTIVE_NODES_KEY, entry.getKey());
}else{
redisClient.hset(REDIS_LOCK_ACTIVE_NODES_KEY, entry.getKey(), String.valueOf(currentTime));
}
}
if(!map.containsKey(EVENT_NODE_ID)){
redisClient.hset(REDIS_LOCK_ACTIVE_NODES_KEY, EVENT_NODE_ID, String.valueOf(currentTime));
}
} finally {
release(redisClient);
}
}
public Jedis getRedisClient(){
Jedis jedis = (Jedis) JedisProviderFactory.getJedisProvider(DLOCK_GROUP_NAME).get();
if(!jedis.isConnected()){
jedis.connect();
}
return jedis;
}
public void release(Jedis jedis) {
JedisProviderFactory.getJedisProvider(DLOCK_GROUP_NAME).release();
}
public String buildEvenId(){
return new StringBuilder().append(EVENT_NODE_ID).append(System.currentTimeMillis()).append(eventIdSeq.incrementAndGet()).toString();
}
public void notifyNext(Jedis pubClient,String lockName){
long currentTimeMillis = System.currentTimeMillis();
String nextEventId;
while(true){
nextEventId = pubClient.rpop(lockName);
if(StringUtils.isBlank(nextEventId)){
return;
}
if(activeNodeIds.contains(nextEventId.substring(0,8))
&& currentTimeMillis - Long.parseLong(nextEventId.substring(8,21)) < CLEAN_TIME){
break;
}
}
pubClient.publish(channelName, lockName + SPLIT_STR + nextEventId);
}
public boolean await(String lockName,String eventId,long timeoutMills){
List eventIds = getGetLockEventIds(lockName);
long start = System.currentTimeMillis();
int deadLockCheckPoint = 0;
while (true) {
try {TimeUnit.MILLISECONDS.sleep(1);} catch (InterruptedException e) {}
if (eventIds.contains(eventId)){
eventIds.remove(eventId);
return true;
}
long useTime = System.currentTimeMillis() - start;
if (useTime > timeoutMills) {
return false;
}
//防止redis的锁数据丢失或者redis挂掉造成死锁,1秒钟检查一次
int useTimeSeconds = (int) (useTime / 1000);
if(useTimeSeconds > 0 && useTimeSeconds > deadLockCheckPoint){
deadLockCheckPoint = useTimeSeconds;
try {
if(getJedisCommands(DLOCK_GROUP_NAME).llen(lockName) == 0){
return false;
}
} catch (Exception e) {
return false;
}
}
}
}
private List getGetLockEventIds(String lockName){
List eventIds = getLockEventIds.get(lockName);
if(eventIds == null){
synchronized (getLockEventIds) {
eventIds = getLockEventIds.get(lockName);
if(eventIds == null){
eventIds = new Vector<>();
getLockEventIds.put(lockName, eventIds);
}
}
}
return eventIds;
}
@PreDestroy
public void close(){
try {subClient.close();} catch (Exception e) {}
if(checkerSchedule != null)checkerSchedule.shutdown();
}
private class LockStateListener extends JedisPubSub {
public LockStateListener() {}
@Override
public void onMessage(String channel, String message) {
super.onMessage(channel, message);
if(StringUtils.isBlank(message)){
return;
}
String[] parts = StringUtils.splitByWholeSeparator(message, SPLIT_STR);
String lockName = parts[0];
String nextEventId = parts[1];
if(!nextEventId.startsWith(EVENT_NODE_ID)){
return;
}
getGetLockEventIds(lockName).add(nextEventId);
}
}
public static void main(String[] args) {
System.out.println(1500 % 1000);
System.out.println(1500 / 1000);
}
}