GPU模块设计

This commit is contained in:
dc
2025-05-25 16:14:04 +08:00
parent 73388da706
commit c01e985256
9 changed files with 186 additions and 128 deletions

View File

@ -4,17 +4,21 @@ import com.bipt.intelligentapplicationorchestrationservice.gpu.dao.GpuResourceDa
import com.bipt.intelligentapplicationorchestrationservice.gpu.exception.CacheInitException;
import com.bipt.intelligentapplicationorchestrationservice.gpu.model.entity.GpuResource;
import jakarta.annotation.PostConstruct;
import org.slf4j.Logger;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.data.redis.RedisConnectionFailureException;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Component;
import org.springframework.transaction.annotation.Propagation;
import org.springframework.transaction.annotation.Transactional;
import java.time.LocalDateTime;
import java.util.List;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.locks.ReentrantLock;
@Transactional // 添加类级别事务管理
@Component
public class CacheManager {
@Autowired
@ -34,7 +38,9 @@ public class CacheManager {
@Value("${cache.init-batch-size:500}")
private int initBatchSize;
private static final Logger log = org.slf4j.LoggerFactory.getLogger(CacheManager.class);
// 全量加载(带分页和分布式锁)
@Transactional(propagation = Propagation.REQUIRED) // 方法级别覆盖
@PostConstruct
public void loadFullCache() {
if (tryLock()) {
@ -114,22 +120,29 @@ public class CacheManager {
}
}
// 带重试机制的缓存刷新
private void refreshWithRetry(GpuResource entity) {
public void refreshWithRetry(GpuResource entity) {
try {
setCacheWithTTL(entity);
} catch (RedisConnectionFailureException ex) {
// 3次重试逻辑
for (int i = 0; i < 3; i++) {
try {
log.info("重试第 {} 次", i + 1); // 添加日志
Thread.sleep(1000);
setCacheWithTTL(entity);
return;
} catch (InterruptedException e) {
if (i == 2) {
throw new CacheInitException("缓存刷新失败: " + entity.getGPUId().toString());
}
log.error("重试失败", e);
Thread.currentThread().interrupt();
}
}
throw new CacheInitException("缓存刷新失败: " + entity.getGPUId().toString());
}
}

View File

@ -7,15 +7,6 @@ import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.annotation.AfterReturning;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.data.redis.connection.RedisConnectionFactory;
import org.springframework.data.redis.connection.RedisPassword;
import org.springframework.data.redis.connection.RedisStandaloneConfiguration;
import org.springframework.data.redis.connection.lettuce.LettuceClientConfiguration;
import org.springframework.data.redis.connection.lettuce.LettuceConnectionFactory;
import org.springframework.stereotype.Component;
import org.springframework.transaction.support.TransactionSynchronization;
import org.springframework.transaction.support.TransactionSynchronizationManager;

View File

@ -1,6 +1,8 @@
package com.bipt.intelligentapplicationorchestrationservice.gpu.config;
import com.bipt.intelligentapplicationorchestrationservice.gpu.model.entity.GpuResource;
import io.lettuce.core.ClientOptions;
import io.lettuce.core.SocketOptions;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
@ -13,6 +15,8 @@ import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.serializer.GenericJackson2JsonRedisSerializer;
import org.springframework.data.redis.serializer.StringRedisSerializer;
import java.time.Duration;
@Configuration
public class RedisConfig {
@Value("${spring.data.redis.host}")
@ -21,7 +25,7 @@ public class RedisConfig {
@Value("${spring.data.redis.port}")
private int redisPort;
@Value("${spring.data.redis.username}") // 若无需用户名可删除
@Value("${spring.data.redis.username}")
private String redisUsername;
@Value("${spring.data.redis.password}")
@ -32,16 +36,35 @@ public class RedisConfig {
@Bean
public RedisConnectionFactory redisConnectionFactory() {
// 1. 创建 SocketOptions
SocketOptions socketOptions = SocketOptions.builder()
.connectTimeout(Duration.ofSeconds(15)) // 连接超时
.keepAlive(true) // 启用 TCP Keep-Alive
.build();
// 2. 构建 ClientOptions
ClientOptions clientOptions = ClientOptions.builder()
.socketOptions(socketOptions)
.autoReconnect(true) // 启用自动重连
.build();
// 3. 集成到 Lettuce 配置
LettuceClientConfiguration clientConfig = LettuceClientConfiguration.builder()
.clientOptions(clientOptions) // 注入 ClientOptions
.commandTimeout(Duration.ofSeconds(30)) // 全局命令超时
.build();
RedisStandaloneConfiguration config = new RedisStandaloneConfiguration();
config.setHostName(redisHost);
config.setPort(redisPort);
config.setUsername(redisUsername); // Redis 6.0+ 支持用户名
config.setPassword(RedisPassword.of(redisPassword));
LettuceClientConfiguration clientConfig = LettuceClientConfiguration.builder()
.useSsl() // 启用 SSL
.disablePeerVerification() // 跳过证书验证(仅测试环境)
.build();
// LettuceClientConfiguration clientConfig = LettuceClientConfiguration.builder()
// .commandTimeout(Duration.ofSeconds(30)) // 增加命令超时
// .socketOptions(SocketOptions.builder()
// .connectTimeout(Duration.ofSeconds(15)) // TCP连接超时
// .build())
// .build();
return new LettuceConnectionFactory(config, clientConfig);
}
@ -55,8 +78,9 @@ public class RedisConfig {
// }
@Bean
public RedisTemplate<String, GpuResource> redisTemplate(){
RedisTemplate<String, GpuResource> template = new RedisTemplate<>();
public RedisTemplate<String, Object> redisTemplate(){
RedisTemplate<String, Object> template = new RedisTemplate<>();
//RedisTemplate<String, GpuResource> template = new RedisTemplate<>();
template.setConnectionFactory(redisConnectionFactory());
template.setKeySerializer(new StringRedisSerializer());
template.setValueSerializer(new GenericJackson2JsonRedisSerializer());

View File

@ -41,8 +41,8 @@ public interface GpuResourceDao {
* @param offset 起始位置
* @param limit 每页数量
*/
@Select("SELECT * FROM ipz.gpu_resource " +
"ORDER BY GPUId ASC LIMIT #{limit} OFFSET #{offset}")
// @Select("SELECT * FROM ipz.gpu_resource " +
// "ORDER BY GPUId ASC LIMIT #{limit} OFFSET #{offset}")
List<GpuResource> findByPage(@Param("offset") int offset,
@Param("limit") int limit);
@ -50,22 +50,22 @@ public interface GpuResourceDao {
* 增量数据查询(缓存同步用)
* @param since 起始时间
*/
@Select("SELECT *, is_deleted FROM ipz.gpu_resource " +
"WHERE update_time > #{since} " +
"ORDER BY update_time ASC")
// @Select("SELECT *, is_deleted FROM ipz.gpu_resource " +
// "WHERE update_time > #{since} " +
// "ORDER BY update_time ASC")
List<GpuResource> findModifiedSince(@Param("since") LocalDateTime since);
/**
* 带锁查询(防缓存击穿)
*/
@Select("SELECT * FROM ipz.gpu_resource " +
"WHERE GPUId = #{gpuId} FOR UPDATE NOWAIT")
// @Select("SELECT * FROM ipz.gpu_resource " +
// "WHERE GPUId = #{gpuId} FOR UPDATE NOWAIT")
GpuResource selectByIdWithLock(@Param("gpuId") Long gpuId);
/**
* 动态条件查询(管理界面筛选用)
*/
@SelectProvider(type = GpuSqlBuilder.class, method = "buildDynamicQuery")
// @SelectProvider(type = GpuSqlBuilder.class, method = "buildDynamicQuery")
List<GpuResource> selectByFields(@Param("params") Map<String, Object> params);
}

View File

@ -1,39 +0,0 @@
package com.bipt.intelligentapplicationorchestrationservice.gpu.event;
import lombok.Getter;
import lombok.NoArgsConstructor;
import lombok.Setter;
import org.springframework.context.ApplicationEvent;
import java.time.Clock;
@Getter
@Setter
public class CacheUpdateEvent extends ApplicationEvent {
public enum OperationType{
FULL_SYNC,
INCREMENTAL_UPDATE,
INVALIDATE
}
private OperationType operationType;
private final String eventType;
private final Long resourceId;
private final Long timestamp;
// 基础构造函数
public CacheUpdateEvent(Object source, String eventType, Long resourceId) {
super(source); // 必须调用父类构造方法
this.eventType = eventType;
this.resourceId = resourceId;
this.timestamp = System.currentTimeMillis();
}
public CacheUpdateEvent(Object source, String eventType, Long resourceId, Clock clock) {
super(source);
this.eventType = eventType;
this.resourceId = resourceId;
this.timestamp = clock.millis(); // 允许外部控制时间戳
}
}

View File

@ -1,35 +1,74 @@
package com.bipt.intelligentapplicationorchestrationservice.gpu.model.entity;
import com.bipt.intelligentapplicationorchestrationservice.gpu.cache.RedisCacheService;
import jakarta.persistence.*;
import com.baomidou.mybatisplus.annotation.TableField;
import lombok.Data;
import lombok.Setter;
import org.hibernate.annotations.CreationTimestamp;
import jakarta.validation.constraints.Pattern;
import java.time.LocalDateTime;
import java.util.Date;
@Setter
@Entity
@Table(name = "Gpu_Resource")
@Data
//@Entity
//@Table(name = "Gpu_Resource")
public class GpuResource {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
public void setGPUId(Long GPUId) {
this.GPUId = GPUId;
}
public void setGPUModel(String GPUModel) {
this.GPUModel = GPUModel;
}
public void setGPUMemorySize(Integer GPUMemorySize) {
this.GPUMemorySize = GPUMemorySize;
}
public void setIsDeleted(Integer isDeleted) {
this.isDeleted = isDeleted;
}
public void setIp(String ip) {
Ip = ip;
}
public void setCreateTime(LocalDateTime createTime) {
CreateTime = createTime;
}
public void setUpdateTime(LocalDateTime updateTime) {
UpdateTime = updateTime;
}
@TableField("GPUId")
private Long GPUId;
@Column(nullable = false, length = 64)
@TableField("GPUModel")
private String GPUModel;
@Column(nullable = false)
@TableField("GPUMemorySize")
private Integer GPUMemorySize;
@Column(name = "is_deleted", nullable = false)
@TableField("is_deleted")
private Integer isDeleted = 0;
public @Pattern(regexp = "^((25[0-5]|2[0-4]\\d|[01]?\\d\\d?)\\.){3}(25[0-5]|2[0-4]\\d|[01]?\\d\\d?)$") String getIp() {
return Ip;
@TableField("Ip")
private String Ip;
@TableField("CreatedTime")
private LocalDateTime CreateTime;
@TableField("update_time")
private LocalDateTime UpdateTime;
public GpuResource(long l, String s, boolean b) {
this.GPUId = l;
this.GPUModel = s;
this.isDeleted = b ? 1 : 0;
}
// public @Pattern(regexp = "^((25[0-5]|2[0-4]\\d|[01]?\\d\\d?)\\.){3}(25[0-5]|2[0-4]\\d|[01]?\\d\\d?)$") String getIp() {
// return Ip;
// }
public Long getGPUId() {
return GPUId;
@ -51,13 +90,7 @@ public class GpuResource {
return isDeleted != 0;
}
@Column(nullable = false, length = 15)
@Pattern(regexp = "^((25[0-5]|2[0-4]\\d|[01]?\\d\\d?)\\.){3}(25[0-5]|2[0-4]\\d|[01]?\\d\\d?)$")
private String Ip;
@Column(updatable = false)
@CreationTimestamp
private LocalDateTime CreateTime;
public GpuResource(Long Id, String Model, Integer MemorySize, String ip, LocalDateTime create_time) {
this.GPUId = Id;

View File

@ -53,15 +53,7 @@ public class GpuManageServiceImpl implements GpuManageService {
GpuResource entity = gpuMapper.toEntity(dto);
gpuDao.updateById(entity);
}
// private GpuResponseDTO convertToResponseDTO(GpuResource entity) {
// return new GpuResponseDTO.Builder()
// .id(entity.getGPUId())
// .model(entity.getGPUModel())
// .memory(entity.getGPUMemorySize())
// .ip(entity.getIp())
// .createdTime(entity.getCreateTime())
// .build();
// }
@Override
//模糊匹配查询
public List<GpuResponseDTO> searchByCriteria(String model, Integer memorySize, String ip) {

View File

@ -1,27 +1,33 @@
spring.application.name=intelligent-application-orchestration-service
spring.datasource.url=jdbc:kingbase8://116.205.121.200:54321/Ipz
spring.datasource.username=system
spring.datasource.password=root
spring.datasource.driver-class-name=com.kingbase8.Driver
spring.jpa.database-platform=org.hibernate.dialect.Kingbase8Dialect
#spring.datasource.url=jdbc:mysql://localhost:3306/Ipz
#spring.datasource.username=root
#spring.datasource.password=zxc25864
#spring.datasource.driver-class-name=com.mysql.cj.jdbc.Driver
#spring.jpa.database-platform=org.hibernate.dialect.MySQL8Dialect
#spring.datasource.url=jdbc:kingbase8://116.205.121.200:54321/Ipz
#spring.datasource.username=system
#spring.datasource.password=root
#spring.datasource.driver-class-name=com.kingbase8.Driver
#spring.jpa.database-platform=org.hibernate.dialect.Kingbase8Dialect
spring.datasource.url=jdbc:mysql://localhost:3306/Ipz
spring.datasource.username=root
spring.datasource.password=zxc25864
spring.datasource.driver-class-name=com.mysql.cj.jdbc.Driver
spring.jpa.database-platform=org.hibernate.dialect.MySQL8Dialect
spring.datasource.hikari.maximum-pool-size=10
spring.datasource.hikari.minimum-idle=5
spring.datasource.hikari.connection-timeout=30000
# Redis ????
spring.data.redis.host=116.205.121.200
spring.data.redis.host=127.0.0.1
spring.data.redis.port=6379
#spring.data.redis.host=116.205.121.200
#spring.data.redis.port=6379
spring.data.redis.username=default
spring.data.redis.password=your_strong_password
spring.data.redis.ssl.enabled=true
mq.queue.cache-update=cache_update_queue
spring.data.redis.password=Jbjhhzstsl97@
spring.data.redis.ssl.enabled=false
#mq.queue.cache-update=cache_update_queue
mybatis-plus.mapper-locations=classpath:mapper/*.xml
mybatis-plus.configuration.map-underscore-to-camel-case=true
mybatis-plus.type-aliases-package=com.bipt.intelligentapplicationorchestrationservice.gpu.model.entity
#mybatis.mapper-locations=classpath:mapper/*.xml
#mybatis.type-aliases-package=com.bipt.intelligentapplicationorchestrationservice.gpu.model.entity

View File

@ -1,19 +1,57 @@
<!-- src/main/resources/mapper/GpuResourceMapper.xml -->
<select id="selectByCondition" resultType="GpuResource">
SELECT * FROM ipz.gpu_resource
<where>
<if test="gpuId != null">
GPUId = #{gpuId}
</if>
<if test="ipPattern != null and ipPattern != ''">
AND Ip LIKE CONCAT('%', #{ipPattern}, '%')
</if>
<if test="modelKeyword != null and modelKeyword != ''">
AND GPUModel LIKE CONCAT('%', #{modelKeyword}, '%')
</if>
<if test="startTime != null and endTime != null">
AND CreatedTime BETWEEN #{startTime} AND #{endTime}
</if>
</where>
ORDER BY GPUId DESC
</select>
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.bipt.intelligentapplicationorchestrationservice.gpu.dao.GpuResourceDao">
<!-- 动态条件查询 -->
<select id="selectByFields"
resultType="com.bipt.intelligentapplicationorchestrationservice.gpu.model.entity.GpuResource">
SELECT *
FROM ipz.gpu_resource
<where>
is_deleted = 0
<if test="params.model != null and params.model != ''">
AND GPUModel LIKE CONCAT('%', #{params.model}, '%')
</if>
<if test="params.memoryMin != null">
AND GPUMemorySize &gt;= #{params.memoryMin}
</if>
<if test="params.ip != null and params.ip != ''">
AND Ip = #{params.ip}
</if>
<if test="params.startTime != null and params.endTime != null">
AND update_time BETWEEN #{params.startTime} AND #{params.endTime}
</if>
</where>
ORDER BY GPUId DESC
</select>
<!-- 分页查询 -->
<select id="findByPage"
resultType="com.bipt.intelligentapplicationorchestrationservice.gpu.model.entity.GpuResource">
SELECT *
FROM ipz.gpu_resource
WHERE is_deleted = 0
ORDER BY GPUId ASC
LIMIT #{limit} OFFSET #{offset}
</select>
<!-- 增量同步查询 -->
<select id="findModifiedSince"
resultType="com.bipt.intelligentapplicationorchestrationservice.gpu.model.entity.GpuResource">
SELECT *, is_deleted
FROM ipz.gpu_resource
WHERE update_time &gt; #{since}
ORDER BY update_time ASC
</select>
<!-- 带锁查询 -->
<select id="selectByIdWithLock"
resultType="com.bipt.intelligentapplicationorchestrationservice.gpu.model.entity.GpuResource">
SELECT *
FROM ipz.gpu_resource
WHERE GPUId = #{gpuId}
FOR UPDATE NOWAIT
</select>
</mapper>