318 lines
12 KiB
Java
Raw Normal View History

2025-03-17 10:46:29 +08:00
package com.diagnose.common.service;
import com.alibaba.fastjson.JSONObject;
import com.diagnose.common.entity.ScheduleLog;
import com.hazelcast.collection.ISet;
import com.hazelcast.core.HazelcastInstance;
import com.hazelcast.crdt.pncounter.PNCounter;
import com.hazelcast.instance.impl.HazelcastInstanceProxy;
import com.hazelcast.map.IMap;
import com.diagnose.common.config.cache.CacheKey;
import com.diagnose.common.handler.BizException;
import com.diagnose.common.util.logger.LoggerUtil;
import org.apache.commons.lang3.exception.ExceptionUtils;
import org.springframework.stereotype.Component;
import javax.annotation.Nullable;
import javax.annotation.Resource;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.Callable;
import java.util.concurrent.TimeUnit;
import static com.diagnose.common.config.cache.CacheKey.ONLY_KEY_OBJ;
@Component
public class CacheService {
@Resource
private ScheduleLogService scheduleLogService;
@Resource
private HazelcastInstance hazelcastInstance;
private static final int UNLOCK_SLEEP_TIME = 5000;
private static final Set<String> loadedSet = new HashSet<>();
/**
* 从缓存加载数据,未找到则执行load回调
*
* @param mapName cacheMap的名字
* @param key cacheKey
* @param load 加载数据回调
* @param <T> 数据类型
* @return
*/
public <T> T get(String mapName, String key, Callable<T> load) {
Map<String, Object> cacheMap = hazelcastInstance.getMap(mapName);
return this.get(cacheMap, key, load);
}
/**
* 从缓存加载数据,未找到则执行load回调
*
* @param cacheMap cacheMap
* @param key cacheKey
* @param load 加载数据回调
* @param <T> 数据类型
* @return
*/
public <T> T get(Map<String, Object> cacheMap, String key, Callable<T> load) {
Object obj = cacheMap.get(key);
if (obj == null) {
T result;
try {
result = load.call();
} catch (BizException e) {
throw e;
} catch (Exception e) {
throw new RuntimeException(e);
}
if (result == null) {
// 防止缓存穿透
cacheMap.put(key, ONLY_KEY_OBJ);
return null;
}
cacheMap.put(key, result);
return result;
} else if (ONLY_KEY_OBJ.equals(obj)) {
return null;
}
return (T) obj;
}
public <T> ISet<T> getSet(String key, Callable<Set<T>> load) {
ISet<T> iSet = hazelcastInstance.getSet(key);
if (loadedSet.contains(key) || !iSet.isEmpty()) {
loadedSet.add(key);
return iSet;
}
simpleLock("task-getSet-" + key,
0, TimeUnit.SECONDS,
10, TimeUnit.SECONDS,
() -> {
try {
Set<T> set = load.call();
iSet.addAll(set);
loadedSet.add(key);
} catch (BizException e) {
throw e;
} catch (Exception e) {
throw new RuntimeException(e);
}
});
return iSet;
}
public <K, V> IMap<K, V> getMap(String key, Callable<Map<K, V>> load) {
IMap<K, V> iMap = hazelcastInstance.getMap(key);
if (loadedSet.contains(key) || !iMap.isEmpty()) {
loadedSet.add(key);
return iMap;
}
simpleLock("task-getMap-" + key,
0, TimeUnit.SECONDS,
10, TimeUnit.SECONDS,
() -> {
try {
Map<K, V> map = load.call();
iMap.clear();
iMap.putAll(map);
loadedSet.add(key);
} catch (BizException e) {
throw e;
} catch (Exception e) {
throw new RuntimeException(e);
}
});
return iMap;
}
public Integer getLong(String key, Callable<Integer> load, int delta) {
PNCounter counter = hazelcastInstance.getPNCounter(key);
long v = counter.get();
if (loadedSet.contains(key) || v != 0) {
loadedSet.add(key);
if (delta == 0) {
return (int) v;
}
return (int) counter.addAndGet(delta);
}
simpleLock("task-getLong-" + key,
0, TimeUnit.SECONDS,
10, TimeUnit.SECONDS,
() -> {
try {
int value = load.call();
counter.reset();
counter.addAndGet(value + delta);
loadedSet.add(key);
} catch (BizException e) {
throw e;
} catch (Exception e) {
throw new RuntimeException(e);
}
});
return (int) counter.get();
}
/**
* 如果key不存在则执行load回调并返回执行结果如果存在返回默认值(一般用于防止缓存穿透)
*
* @param cacheMap cacheMap
* @param key cacheKey
* @param load 回调方法
* @param defaultValue 默认值
* @param <T> 数据类型
* @return
*/
public <T> T runNx(Map<String, Object> cacheMap, String key, Callable<T> load, T defaultValue) {
Object obj = cacheMap.get(key);
if (obj == null) {
T result;
try {
result = load.call();
} catch (Exception e) {
throw new RuntimeException(e);
}
// 防止缓存穿透
cacheMap.put(key, ONLY_KEY_OBJ);
return result;
}
return defaultValue;
}
/**
* 多线程锁
*
* @param taskName 任务名字
* @param time 加锁时间值
* @param timeunit 加锁时间类型
* @param leaseTime 自动释放时间值
* @param leaseTimeunit 自动释放时间类型
* @param fn 加锁成功后执行的方法
* @param <T> 类型
*/
public <T> void lock(String taskName,
long time, @Nullable TimeUnit timeunit,
long leaseTime, @Nullable TimeUnit leaseTimeunit,
Callable<T> fn) {
try {
IMap<String, Integer> map = hazelcastInstance.getMap(CacheKey.DISTRIBUTED_LOCK);
boolean lock = map.tryLock(taskName, time, timeunit, leaseTime, leaseTimeunit);
if (lock) {
String host = ((HazelcastInstanceProxy) hazelcastInstance).getOriginal().getLocalEndpoint().getAddress().getHost();
Integer logId = null;
long runTime = 0;
try {
logId = scheduleLogService.save(ScheduleLog.start(taskName, host));
long startTime = System.currentTimeMillis();
LoggerUtil.data.info(taskName + "-开始");
T result = fn.call();
runTime = System.currentTimeMillis() - startTime;
String resultJSON = JSONObject.toJSONString(result);
LoggerUtil.data.info(taskName + "-结束:" + runTime, resultJSON);
scheduleLogService.save(ScheduleLog.success(logId, result == null ? null : resultJSON));
} catch (Exception e) {
LoggerUtil.error.error(taskName + "-异常:" + ExceptionUtils.getStackTrace(e));
scheduleLogService.save(ScheduleLog.error(logId, ExceptionUtils.getStackTrace(e)));
} finally {
if (runTime >= UNLOCK_SLEEP_TIME) {
map.forceUnlock(taskName);
} else if (lock) {
TimeUnit.MILLISECONDS.sleep(UNLOCK_SLEEP_TIME - runTime);
if (map.isLocked(taskName)) {
map.unlock(taskName);
}
}
}
}
} catch (Exception e) {
LoggerUtil.error.error(taskName + "-异常:" + ExceptionUtils.getStackTrace(e));
}
}
/**
* 多线程锁
*
* @param taskName 任务名字
* @param time 加锁时间值
* @param timeunit 加锁时间类型
* @param leaseTime 自动释放时间值
* @param leaseTimeunit 自动释放时间类型
* @param fn 加锁成功后执行的方法
*/
public void lock(String taskName,
long time, @Nullable TimeUnit timeunit,
long leaseTime, @Nullable TimeUnit leaseTimeunit,
Runnable fn) {
try {
IMap<String, Integer> map = hazelcastInstance.getMap(CacheKey.DISTRIBUTED_LOCK);
long runTime = 0;
boolean lock = map.tryLock(taskName, time, timeunit, leaseTime, leaseTimeunit);
if (lock) {
String host = ((HazelcastInstanceProxy) hazelcastInstance).getOriginal().getLocalEndpoint().getAddress().getHost();
Integer logId = null;
try {
logId = scheduleLogService.save(ScheduleLog.start(taskName, host));
long startTime = System.currentTimeMillis();
LoggerUtil.data.info(taskName + "-开始");
fn.run();
runTime = System.currentTimeMillis() - startTime;
LoggerUtil.data.info(taskName + "-结束:" + runTime);
scheduleLogService.save(ScheduleLog.success(logId, null));
} catch (Exception e) {
LoggerUtil.error.error(taskName + "-异常:" + ExceptionUtils.getStackTrace(e));
scheduleLogService.save(ScheduleLog.error(logId, ExceptionUtils.getStackTrace(e)));
} finally {
if (runTime >= UNLOCK_SLEEP_TIME) {
map.forceUnlock(taskName);
} else if (lock) {
TimeUnit.MILLISECONDS.sleep(UNLOCK_SLEEP_TIME - runTime);
if (map.isLocked(taskName)) {
map.unlock(taskName);
}
}
}
}
} catch (Exception e) {
LoggerUtil.error.error(taskName + "-异常:" + ExceptionUtils.getStackTrace(e));
}
}
public void simpleLock(String key, long time, @Nullable TimeUnit timeunit, long leaseTime, @Nullable TimeUnit leaseTimeunit, Runnable fn) {
IMap<String, Integer> map = hazelcastInstance.getMap(CacheKey.DISTRIBUTED_LOCK);
boolean lock = false;
try {
lock = map.tryLock(key, time, timeunit, leaseTime, leaseTimeunit);
if (lock) {
fn.run();
}
} catch (InterruptedException e) {
throw new RuntimeException(e);
} finally {
if (lock && map.isLocked(key)) {
map.unlock(key);
}
}
}
public Integer getLong(String cacheKey, int delta) {
PNCounter counter = hazelcastInstance.getPNCounter(cacheKey);
return (int) counter.addAndGet(delta);
}
public void clearCache(String mapName, List<String> cacheKeys) {
Map<String, Object> cacheMap = hazelcastInstance.getMap(mapName);
cacheKeys.forEach(cacheMap::remove);
}
public void clearCache(String mapName, String cacheKey) {
Map<String, Object> cacheMap = hazelcastInstance.getMap(mapName);
cacheMap.remove(cacheKey);
}
}