Optimize caching related content

This commit is contained in:
longguancheng 2026-03-04 11:35:38 +08:00
parent 90d8311840
commit 29a81bea9b
15 changed files with 74 additions and 198 deletions

View File

@ -4,18 +4,13 @@ import lombok.extern.slf4j.Slf4j;
import org.mybatis.spring.annotation.MapperScan; import org.mybatis.spring.annotation.MapperScan;
import org.springframework.boot.SpringApplication; import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.scheduling.annotation.EnableScheduling; import org.springframework.scheduling.annotation.EnableScheduling;
import java.io.File; import java.io.File;
@EnableScheduling @EnableScheduling
@MapperScan("com.zhangy.skyeye.**.mapper") @MapperScan("com.zhangy.skyeye.**.mapper")
@SpringBootApplication(scanBasePackages = "com.zhangy.**") @SpringBootApplication(scanBasePackages = "com.zhangy")
@ComponentScan(basePackages = {
"com.zhangy.skyeye", // 主包
"com.zhangy.skyeye.publics.config" // 明确包含 CacheConfig
})
@Slf4j @Slf4j
public class SEApplication { public class SEApplication {

View File

@ -133,7 +133,7 @@ public class PayloadServiceImpl implements IPayloadService {
// 筛选出未在任务中且已连接的雷达 // 筛选出未在任务中且已连接的雷达
List<SkyeyePayload> payloadList = sarList.stream() List<SkyeyePayload> payloadList = sarList.stream()
.filter(sar -> !jobSarSet.contains(sar.getId()) .filter(sar -> !jobSarSet.contains(sar.getId())
&& CacheUtil.get(sarShortCache, CacheKey.getSarConnect(sar.getIp()), JmSarStatusDTO.class) != null) && CacheUtil.get(sarShortCache, CacheKey.getSarStatus(sar.getIp()), JmSarStatusDTO.class) != null)
.collect(Collectors.toList()); .collect(Collectors.toList());
return payloadList; return payloadList;
} }
@ -224,7 +224,7 @@ public class PayloadServiceImpl implements IPayloadService {
@Override @Override
public JmSarStatusDTO getLastStatus(String payloadIp) { public JmSarStatusDTO getLastStatus(String payloadIp) {
String statusKey = CacheKey.getSarConnect(payloadIp); String statusKey = CacheKey.getSarStatus(payloadIp);
JmSarStatusDTO status = CacheUtil.get(sarShortCache, statusKey, JmSarStatusDTO.class); JmSarStatusDTO status = CacheUtil.get(sarShortCache, statusKey, JmSarStatusDTO.class);
if (status == null) { if (status == null) {
try { try {

View File

@ -40,9 +40,9 @@ public class UavServiceImpl implements IUavService {
@PostConstruct @PostConstruct
public void initCache() { public void initCache() {
permanentCache = cacheManager.getCache("sar-payload-permanent"); permanentCache = cacheManager.getCache("sar-permanent");
if (permanentCache == null) { if (permanentCache == null) {
throw new IllegalStateException("永久缓存 sar-payload-permanent 未找到,请检查 CacheConfig 配置"); throw new IllegalStateException("永久缓存 sar-permanent 未找到,请检查 CacheConfig 配置");
} }
} }

View File

@ -49,8 +49,8 @@ public class JmTaskScheduler {
@PostConstruct @PostConstruct
public void initCaches() { public void initCaches() {
sarPermanentCache = cacheManager.getCache("sar-payload-permanent"); sarPermanentCache = cacheManager.getCache("sar-permanent");
sarShortCache = cacheManager.getCache("sar-short-lived"); sarShortCache = cacheManager.getCache("sar-short");
} }
/** /**
@ -118,7 +118,7 @@ public class JmTaskScheduler {
sarList.forEach(sar -> { sarList.forEach(sar -> {
String ip = sar.getIp(); String ip = sar.getIp();
// 判断是否已连接 shortCache 里是否有该 ip 的状态最近有状态包 // 判断是否已连接 shortCache 里是否有该 ip 的状态最近有状态包
boolean hasStatus = CacheUtil.get(sarShortCache, CacheKey.getSarConnect(ip), JmSarStatusDTO.class) != null; boolean hasStatus = CacheUtil.get(sarShortCache, CacheKey.getSarStatus(ip), JmSarStatusDTO.class) != null;
// 判断是否已执行连接指令 permanentCache 里是否有该 ip 的连接标志 // 判断是否已执行连接指令 permanentCache 里是否有该 ip 的连接标志
boolean hasConnectedFlag = sarPermanentCache.get(ip) != null; boolean hasConnectedFlag = sarPermanentCache.get(ip) != null;
// 如果缺少状态 缺少连接标志则尝试连接 // 如果缺少状态 缺少连接标志则尝试连接

View File

@ -40,18 +40,18 @@ public class CacheConfig {
.maximumSize(5000) // 预计活跃用户数 .maximumSize(5000) // 预计活跃用户数
.build()); .build());
// 永久缓存SAR 载荷专用不设过期 // 永久缓存SAR 载荷专用不设过期
cacheManager.registerCustomCache("sar-payload-permanent", Caffeine.newBuilder() cacheManager.registerCustomCache("sar-permanent", Caffeine.newBuilder()
.maximumSize(5000) // 根据 SAR 载荷数量调整 .maximumSize(5000) // 根据 SAR 载荷数量调整
.build()); .build());
// 短时状态/连接已存在确认过期时间
cacheManager.registerCustomCache("sar-short", Caffeine.newBuilder()
.expireAfterWrite(3, TimeUnit.SECONDS) // 建议 3~5 防边界丢失
.maximumSize(2000)
.build());
// 永久缓存UAV 专用不设过期 // 永久缓存UAV 专用不设过期
cacheManager.registerCustomCache("uav-permanent", Caffeine.newBuilder() cacheManager.registerCustomCache("uav-permanent", Caffeine.newBuilder()
.maximumSize(5000) // 根据无人机数量调整 .maximumSize(5000) // 根据无人机数量调整
.build()); .build());
// 短时状态/连接已存在确认过期时间
cacheManager.registerCustomCache("sar-short-lived", Caffeine.newBuilder()
.expireAfterWrite(3, TimeUnit.SECONDS) // 建议 3~5 防边界丢失
.maximumSize(2000)
.build());
cacheManager.registerCustomCache("uav-status", Caffeine.newBuilder() cacheManager.registerCustomCache("uav-status", Caffeine.newBuilder()
.expireAfterWrite(10, TimeUnit.MINUTES) // 与原 CACHE_EXPIRE_SECONDS 一致 .expireAfterWrite(10, TimeUnit.MINUTES) // 与原 CACHE_EXPIRE_SECONDS 一致
.maximumSize(1000) // 按设备/IP 数量预估 .maximumSize(1000) // 按设备/IP 数量预估

View File

@ -2,109 +2,57 @@ package com.zhangy.skyeye.publics.consts;
public class CacheKey { public class CacheKey {
/*========================================== /*==================== publics 模块 ====================*/
publics 模块用 skyeye: 前缀 // 用户token30分钟
==========================================*/
/**
* 用户token30分钟
*/
private static final String USER_TOEKN = "skyeye:user:token:%s@%s"; private static final String USER_TOEKN = "skyeye:user:token:%s@%s";
/** // 获取token key
* 获取token key public static String getToken(Long userId, String userName) {
*/
public static String getToekn(Long userId, String userName) {
return String.format(CacheKey.USER_TOEKN, userId, userName); return String.format(CacheKey.USER_TOEKN, userId, userName);
} }
/*========================================== /*==================== 分布式锁 ====================*/
分布式锁用 skyeye:lock: 前缀 // 上传sar图像锁
==========================================*/
/**
* 上传sar图像锁
*/
public static final String SAR_IMAGE_UPLOAD_LOCK = "skyeye:lock:image"; public static final String SAR_IMAGE_UPLOAD_LOCK = "skyeye:lock:image";
/*========================================== /*==================== SAR 模块 ====================*/
sar 模块用 skyeye:sar: 前缀 // 控制回包1秒
==========================================*/
/**
* 控制回包1秒
*/
private static final String SAR_CONTROL_BACK = "skyeye:sar:control"; private static final String SAR_CONTROL_BACK = "skyeye:sar:control";
// sar状态1秒
/** private static final String SAR_STATUS = "skyeye:sar:status";
* 获取控制回包key加ip // 已执行连接命令的sar永久
*/
public static String getSarControlBack(String ip) {
return SAR_CONTROL_BACK + ip;
}
/**
* sar状态1秒
*/
private static final String SAR_STATUS = "skyeye:sar:status:";
/**
* 获取状态key加ip
*/
public static String getSarConnect(String ip) {
return SAR_STATUS + ip;
}
/**
* 已执行连接命令的sar永久
*/
public static final String SAR_CONNECTED = "skyeye:sar:connected"; public static final String SAR_CONNECTED = "skyeye:sar:connected";
/*========================================== // 获取控制回包key加ip
device 模块用 skyeye:device: 前缀 public static String getSarControlBack(String ip) {
==========================================*/ return SAR_CONTROL_BACK + ":" + ip;
}
// 获取状态key加ip
public static String getSarStatus(String ip) {
return SAR_STATUS + ":" + ip;
}
// 获取已执行连接命令的sar-key加ip
public static String getSarConnected(String ip) {
return SAR_CONNECTED + ":" + ip;
}
/** /*==================== Device 模块 ====================*/
* sar载荷永久 // sar载荷永久
*/
public static final String DEVICE_SAR = "skyeye:device:sar"; public static final String DEVICE_SAR = "skyeye:device:sar";
// 无人机永久
/**
* 无人机永久
*/
public static final String DEVICE_UAV = "skyeye:device:uav"; public static final String DEVICE_UAV = "skyeye:device:uav";
/*========================================== /*==================== SMP 模块 ====================*/
smp 模块用 skyeye:smp: 前缀 // 运动规划响应1秒
==========================================*/
/**
* 运动规划响应1秒
*/
public static final String SMP_WAYPOINT_RES = "skyeye:smp:waypoint:"; public static final String SMP_WAYPOINT_RES = "skyeye:smp:waypoint:";
// 飞行控制响应1秒
/**
* 飞行控制响应1秒
*/
public static final String SMP_FLIGHT_RES = "skyeye:smp:flight:"; public static final String SMP_FLIGHT_RES = "skyeye:smp:flight:";
// 云台控制响应1秒
/**
* 云台控制响应1秒
*/
public static final String SMP_GIMBALMGR_RES = "skyeye:smp:gimbalmgr:"; public static final String SMP_GIMBALMGR_RES = "skyeye:smp:gimbalmgr:";
// 数据订阅响应1秒
/**
* 数据订阅响应1秒
*/
public static final String SMP_SUBSCRIPT_RES = "skyeye:smp:subscript:"; public static final String SMP_SUBSCRIPT_RES = "skyeye:smp:subscript:";
// 数据订阅回传数据1秒
/**
* 数据订阅回传数据1秒
*/
public static final String SMP_SUBSCRIPT_DATA = "skyeye:smp:subscript:"; public static final String SMP_SUBSCRIPT_DATA = "skyeye:smp:subscript:";
// 相机视频流响应1秒
/**
* 相机视频流响应1秒
*/
public static final String SMP_VIDEO_RES = "skyeye:smp:video:"; public static final String SMP_VIDEO_RES = "skyeye:smp:video:";
} }

View File

@ -94,7 +94,7 @@ public class SysUserController {
public Result logout() { public Result logout() {
UserDTO userDTO = SecurityUtil.getUser(); UserDTO userDTO = SecurityUtil.getUser();
if (userDTO != null) { if (userDTO != null) {
String key = CacheKey.getToekn(userDTO.getId(), userDTO.getAccount()); String key = CacheKey.getToken(userDTO.getId(), userDTO.getAccount());
Objects.requireNonNull(cacheManager.getCache("user-tokens")).evict(key); Objects.requireNonNull(cacheManager.getCache("user-tokens")).evict(key);
} }
return Result.status(true); return Result.status(true);

View File

@ -41,6 +41,6 @@ public class UserDTO {
* @return * @return
*/ */
public String getTokenKey() { public String getTokenKey() {
return CacheKey.getToekn(id, account); return CacheKey.getToken(id, account);
} }
} }

View File

@ -1,5 +1,6 @@
package com.zhangy.skyeye.sar.control; package com.zhangy.skyeye.sar.control;
import com.zhangy.skyeye.sar.cache.SarCache;
import com.zhangy.skyeye.sar.dto.SarControlDTO; import com.zhangy.skyeye.sar.dto.SarControlDTO;
import com.zhangy.skyeye.sar.dto.SarControlParamDTO; import com.zhangy.skyeye.sar.dto.SarControlParamDTO;
import com.zhangy.skyeye.sar.enums.SarControlTypeEnum; import com.zhangy.skyeye.sar.enums.SarControlTypeEnum;
@ -32,7 +33,7 @@ public class SarControlConnectStrategy implements ISarControlStrategy {
@PostConstruct @PostConstruct
public void initCache() { public void initCache() {
this.permanentCache = cacheManager.getCache("sar-payload-permanent"); this.permanentCache = cacheManager.getCache("sar-permanent");
if (this.permanentCache == null) { if (this.permanentCache == null) {
log.error("永久缓存 sar-payload-permanent 未找到,请检查 CacheConfig 配置"); log.error("永久缓存 sar-payload-permanent 未找到,请检查 CacheConfig 配置");
} }

View File

@ -170,9 +170,9 @@ public class SarControlContext {
* 轮询等待缓存中的回执带超时 * 轮询等待缓存中的回执带超时
*/ */
private SarErrorDTO waitForResponse(String cacheKey, String ip, SarControlTypeEnum controlType) { private SarErrorDTO waitForResponse(String cacheKey, String ip, SarControlTypeEnum controlType) {
Cache sarShortCache = cacheManager.getCache("sar-short-lived"); Cache sarShortCache = cacheManager.getCache("sar-short");
if (sarShortCache == null) { if (sarShortCache == null) {
log.error("无法获取 sar-short-lived cache"); log.error("无法获取 sar-short cache");
return null; return null;
} }
long start = System.currentTimeMillis(); long start = System.currentTimeMillis();

View File

@ -31,8 +31,8 @@ public class SarControlDisconnectStrategy implements ISarControlStrategy {
@PostConstruct @PostConstruct
public void init() { public void init() {
this.sarShortCache = cacheManager.getCache("sar-short-lived"); this.sarShortCache = cacheManager.getCache("sar-short");
this.sarPermanentCache = cacheManager.getCache("sar-payload-permanent"); this.sarPermanentCache = cacheManager.getCache("sar-permanent");
} }
@Override @Override
@ -50,7 +50,7 @@ public class SarControlDisconnectStrategy implements ISarControlStrategy {
@Override @Override
public void sendPost(SarControlDTO sar) { public void sendPost(SarControlDTO sar) {
String connectKey = CacheKey.getSarConnect(sar.getIp()); String connectKey = CacheKey.getSarStatus(sar.getIp());
if (sarShortCache != null) { if (sarShortCache != null) {
sarShortCache.evict(connectKey); sarShortCache.evict(connectKey);
} }

View File

@ -41,14 +41,12 @@ public class SarControlController {
*/ */
@RequestMapping("/turnon") @RequestMapping("/turnon")
public Result turnOn(@RequestBody String ip) { public Result turnOn(@RequestBody String ip) {
String ipVal = ""; String ipVal;
try { try {
// 1. 使用 ObjectMapper JSON 字符串解析成一个 JsonNode // 1. 使用 ObjectMapper JSON 字符串解析成一个 JsonNode
JsonNode rootNode = objectMapper.readTree(ip); JsonNode rootNode = objectMapper.readTree(ip);
// 2. 从树中获取 "payloadId" 节点并将其值转换为文本 // 2. 从树中获取 "payloadId" 节点并将其值转换为文本
ipVal = rootNode.path("payloadId").asText(); ipVal = rootNode.path("payloadId").asText();
// 检查是否成功获取 // 检查是否成功获取
if (ipVal == null || ipVal.isEmpty()) { if (ipVal == null || ipVal.isEmpty()) {
// 根据你的业务逻辑返回错误例如 // 根据你的业务逻辑返回错误例如

View File

@ -82,10 +82,10 @@ public class SarStatusListener extends SarAbstractListener {
log.warn("状态包校验失败,已丢弃。"); log.warn("状态包校验失败,已丢弃。");
return; return;
} }
Cache sarShortCache = cacheManager.getCache("sar-short-lived"); Cache sarShortCache = cacheManager.getCache("sar-short");
if (sarShortCache == null) { if (sarShortCache == null) {
log.error("sar-short-lived 缓存未找到!请检查 CacheConfig"); log.error("sar-short 缓存未找到!请检查 CacheConfig");
throw new IllegalStateException("sar-short-lived 缓存未找到"); throw new IllegalStateException("sar-short 缓存未找到");
} }
// 错误包 回执 // 错误包 回执
int errorStatus = packDTO.getErrorPacketStatus(); int errorStatus = packDTO.getErrorPacketStatus();
@ -103,7 +103,7 @@ public class SarStatusListener extends SarAbstractListener {
JmSarStatusDTO info = packDTO.getDeviceStatusInfo(); JmSarStatusDTO info = packDTO.getDeviceStatusInfo();
log.debug("sar开机状态{}", info.getIsBoot()); log.debug("sar开机状态{}", info.getIsBoot());
sarJobStatusService.update(ip, info); sarJobStatusService.update(ip, info);
String connectKey = CacheKey.getSarConnect(ip); String connectKey = CacheKey.getSarStatus(ip);
sarShortCache.put(connectKey, info); // 写入自动过期 sarShortCache.put(connectKey, info); // 写入自动过期
} }
log.debug("----------------------状态包解析完毕"); log.debug("----------------------状态包解析完毕");

View File

@ -18,6 +18,7 @@ import org.springframework.cache.CacheManager;
import org.springframework.stereotype.Service; import org.springframework.stereotype.Service;
import javax.annotation.PostConstruct; import javax.annotation.PostConstruct;
import java.util.Objects;
import java.util.Optional; import java.util.Optional;
import static com.zhangy.skyeye.jm.consts.JmJobModeEnum.CRUISE; import static com.zhangy.skyeye.jm.consts.JmJobModeEnum.CRUISE;
@ -31,16 +32,19 @@ import static com.zhangy.skyeye.jm.consts.JmJobModeEnum.CRUISE;
public class SarControlServiceImpl implements ISarControlService { public class SarControlServiceImpl implements ISarControlService {
private final SarControlContext udpSendContext; private final SarControlContext udpSendContext;
private final CacheManager cacheManager; // final 字段 private final CacheManager cacheManager; // final 字段
private Cache sarShortCache; private Cache sarShortCache;
private Cache sarPermanentCache;
@PostConstruct @PostConstruct
public void init() { public void init() {
this.sarShortCache = cacheManager.getCache("sar-short-lived"); this.sarPermanentCache = cacheManager.getCache("sar-permanent");
this.sarShortCache = cacheManager.getCache("sar-short");
if (sarShortCache == null) { if (sarShortCache == null) {
log.error("sar-short-lived cache 未找到!"); log.error("sar-short cache 未找到!");
}
if (sarPermanentCache == null) {
log.error("sar-permanent cache 未找到!");
} }
} }
@ -96,27 +100,27 @@ public class SarControlServiceImpl implements ISarControlService {
@Override @Override
public void turnOn(String ip) { public void turnOn(String ip) {
if (!redisUtil.hHasKey(CacheKey.SAR_CONNECTED, ip) || !redisUtil.hasKey(CacheKey.getSarConnect(ip))) { if (Objects.nonNull(sarPermanentCache.get(CacheKey.getSarConnected(ip))) &&
Objects.nonNull(sarPermanentCache.get(CacheKey.getSarStatus(ip)))) {
throw new ServiceException("请先加电并连接sar"); throw new ServiceException("请先加电并连接sar");
} }
SarControlParamDTO param = new SarControlParamDTO(ip, SarControlTypeEnum.TURNON); SarControlParamDTO param = new SarControlParamDTO(ip, SarControlTypeEnum.TURNON);
udpSendContext.execute(param); udpSendContext.execute(param);
} }
@Override @Override
public void endAll(String ip) { public void endAll(String ip) {
if (!redisUtil.hHasKey(CacheKey.SAR_CONNECTED, ip) || !redisUtil.hasKey(CacheKey.getSarConnect(ip))) { if (Objects.nonNull(sarPermanentCache.get(CacheKey.getSarConnected(ip))) &&
Objects.nonNull(sarPermanentCache.get(CacheKey.getSarStatus(ip)))) {
throw new ServiceException("请先加电并连接sar"); throw new ServiceException("请先加电并连接sar");
} }
SarControlParamDTO param = new SarControlParamDTO(ip, SarControlTypeEnum.ENDALL); SarControlParamDTO param = new SarControlParamDTO(ip, SarControlTypeEnum.ENDALL);
udpSendContext.execute(param); udpSendContext.execute(param);
} }
@Override @Override
public JmSarStatusDTO getLatestStatus(String ip) { public JmSarStatusDTO getLatestStatus(String ip) {
String connectKey = CacheKey.getSarConnect(ip); String connectKey = CacheKey.getSarStatus(ip);
return CacheUtil.get(sarShortCache, connectKey, JmSarStatusDTO.class); return CacheUtil.get(sarShortCache, connectKey, JmSarStatusDTO.class);
} }
} }

View File

@ -1,70 +0,0 @@
//package com.zhangy.skyeye.redis.config;
//
//import com.zhangy.skyeye.redis.utils.RedisUtil;
//import org.springframework.boot.autoconfigure.AutoConfigureBefore;
//import org.springframework.boot.autoconfigure.condition.ConditionalOnBean;
//import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
//import org.springframework.boot.autoconfigure.data.redis.RedisAutoConfiguration;
//import org.springframework.cache.CacheManager;
//import org.springframework.cache.annotation.EnableCaching;
//import org.springframework.context.annotation.Bean;
//import org.springframework.context.annotation.Configuration;
//import org.springframework.data.redis.cache.RedisCacheConfiguration;
//import org.springframework.data.redis.cache.RedisCacheManager;
//import org.springframework.data.redis.cache.RedisCacheWriter;
//import org.springframework.data.redis.connection.RedisConnectionFactory;
//import org.springframework.data.redis.core.RedisTemplate;
//import org.springframework.data.redis.serializer.JdkSerializationRedisSerializer;
//import org.springframework.data.redis.serializer.RedisSerializer;
//
//import java.time.Duration;
//
///**
// * Redis 配置
// */
//@EnableCaching
//@Configuration(proxyBeanMethods = false)
//@AutoConfigureBefore(RedisAutoConfiguration.class)
//public class DTRedisTemplateConfiguration {
//
// /**
// * value 序列化
// * @return RedisSerializer
// */
// @Bean
// @ConditionalOnMissingBean(RedisSerializer.class)
// public RedisSerializer<Object> redisSerializer() {
// return new JdkSerializationRedisSerializer();
// }
//
// @Bean(name = "redisTemplate")
// @ConditionalOnMissingBean(RedisTemplate.class)
// public RedisTemplate<String, Object> redisTemplate(RedisConnectionFactory redisConnectionFactory, RedisSerializer<Object> redisSerializer) {
// RedisTemplate<String, Object> redisTemplate = new RedisTemplate<>();
// RedisKeySerializer redisKeySerializer = new RedisKeySerializer();
// // key 序列化
// redisTemplate.setKeySerializer(redisKeySerializer);
// redisTemplate.setHashKeySerializer(redisKeySerializer);
// // value 序列化
// redisTemplate.setValueSerializer(redisSerializer);
// redisTemplate.setHashValueSerializer(redisSerializer);
// redisTemplate.setConnectionFactory(redisConnectionFactory);
// return redisTemplate;
// }
//
// @Bean
// public CacheManager cacheManager(RedisConnectionFactory redisConnectionFactory) {
// RedisCacheConfiguration redisCacheConfiguration = RedisCacheConfiguration.defaultCacheConfig()
// .entryTtl(Duration.ofHours(1));
// return RedisCacheManager
// .builder(RedisCacheWriter.nonLockingRedisCacheWriter(redisConnectionFactory))
// .cacheDefaults(redisCacheConfiguration).build();
// }
//
// @Bean(name = "redisUtil")
// @ConditionalOnBean(RedisTemplate.class)
// public RedisUtil redisUtils(RedisTemplate<String, Object> redisTemplate) {
// return new RedisUtil(redisTemplate);
// }
//
//}