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

View File

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

View File

@ -41,8 +41,8 @@ public interface GpuResourceDao {
* @param offset 起始位置 * @param offset 起始位置
* @param limit 每页数量 * @param limit 每页数量
*/ */
@Select("SELECT * FROM ipz.gpu_resource " + // @Select("SELECT * FROM ipz.gpu_resource " +
"ORDER BY GPUId ASC LIMIT #{limit} OFFSET #{offset}") // "ORDER BY GPUId ASC LIMIT #{limit} OFFSET #{offset}")
List<GpuResource> findByPage(@Param("offset") int offset, List<GpuResource> findByPage(@Param("offset") int offset,
@Param("limit") int limit); @Param("limit") int limit);
@ -50,22 +50,22 @@ public interface GpuResourceDao {
* 增量数据查询(缓存同步用) * 增量数据查询(缓存同步用)
* @param since 起始时间 * @param since 起始时间
*/ */
@Select("SELECT *, is_deleted FROM ipz.gpu_resource " + // @Select("SELECT *, is_deleted FROM ipz.gpu_resource " +
"WHERE update_time > #{since} " + // "WHERE update_time > #{since} " +
"ORDER BY update_time ASC") // "ORDER BY update_time ASC")
List<GpuResource> findModifiedSince(@Param("since") LocalDateTime since); List<GpuResource> findModifiedSince(@Param("since") LocalDateTime since);
/** /**
* 带锁查询(防缓存击穿) * 带锁查询(防缓存击穿)
*/ */
@Select("SELECT * FROM ipz.gpu_resource " + // @Select("SELECT * FROM ipz.gpu_resource " +
"WHERE GPUId = #{gpuId} FOR UPDATE NOWAIT") // "WHERE GPUId = #{gpuId} FOR UPDATE NOWAIT")
GpuResource selectByIdWithLock(@Param("gpuId") Long gpuId); 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); 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; package com.bipt.intelligentapplicationorchestrationservice.gpu.model.entity;
import com.bipt.intelligentapplicationorchestrationservice.gpu.cache.RedisCacheService; import com.baomidou.mybatisplus.annotation.TableField;
import jakarta.persistence.*; import lombok.Data;
import lombok.Setter; import lombok.Setter;
import org.hibernate.annotations.CreationTimestamp;
import jakarta.validation.constraints.Pattern;
import java.time.LocalDateTime; import java.time.LocalDateTime;
import java.util.Date;
@Setter @Setter
@Entity @Data
@Table(name = "Gpu_Resource") //@Entity
//@Table(name = "Gpu_Resource")
public class GpuResource { public class GpuResource {
@Id public void setGPUId(Long GPUId) {
@GeneratedValue(strategy = GenerationType.IDENTITY) 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; private Long GPUId;
@Column(nullable = false, length = 64) @TableField("GPUModel")
private String GPUModel; private String GPUModel;
@Column(nullable = false) @TableField("GPUMemorySize")
private Integer GPUMemorySize; private Integer GPUMemorySize;
@Column(name = "is_deleted", nullable = false) @TableField("is_deleted")
private Integer isDeleted = 0; 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() { @TableField("Ip")
return 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() { public Long getGPUId() {
return GPUId; return GPUId;
@ -51,13 +90,7 @@ public class GpuResource {
return isDeleted != 0; 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) { public GpuResource(Long Id, String Model, Integer MemorySize, String ip, LocalDateTime create_time) {
this.GPUId = Id; this.GPUId = Id;

View File

@ -53,15 +53,7 @@ public class GpuManageServiceImpl implements GpuManageService {
GpuResource entity = gpuMapper.toEntity(dto); GpuResource entity = gpuMapper.toEntity(dto);
gpuDao.updateById(entity); 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 @Override
//模糊匹配查询 //模糊匹配查询
public List<GpuResponseDTO> searchByCriteria(String model, Integer memorySize, String ip) { public List<GpuResponseDTO> searchByCriteria(String model, Integer memorySize, String ip) {

View File

@ -1,27 +1,33 @@
spring.application.name=intelligent-application-orchestration-service spring.application.name=intelligent-application-orchestration-service
spring.datasource.url=jdbc:kingbase8://116.205.121.200:54321/Ipz #spring.datasource.url=jdbc:kingbase8://116.205.121.200:54321/Ipz
spring.datasource.username=system #spring.datasource.username=system
spring.datasource.password=root #spring.datasource.password=root
spring.datasource.driver-class-name=com.kingbase8.Driver #spring.datasource.driver-class-name=com.kingbase8.Driver
spring.jpa.database-platform=org.hibernate.dialect.Kingbase8Dialect #spring.jpa.database-platform=org.hibernate.dialect.Kingbase8Dialect
#spring.datasource.url=jdbc:mysql://localhost:3306/Ipz spring.datasource.url=jdbc:mysql://localhost:3306/Ipz
#spring.datasource.username=root spring.datasource.username=root
#spring.datasource.password=zxc25864 spring.datasource.password=zxc25864
#spring.datasource.driver-class-name=com.mysql.cj.jdbc.Driver spring.datasource.driver-class-name=com.mysql.cj.jdbc.Driver
#spring.jpa.database-platform=org.hibernate.dialect.MySQL8Dialect spring.jpa.database-platform=org.hibernate.dialect.MySQL8Dialect
spring.datasource.hikari.maximum-pool-size=10 spring.datasource.hikari.maximum-pool-size=10
spring.datasource.hikari.minimum-idle=5 spring.datasource.hikari.minimum-idle=5
spring.datasource.hikari.connection-timeout=30000 spring.datasource.hikari.connection-timeout=30000
# Redis ???? # 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.port=6379
#spring.data.redis.host=116.205.121.200
#spring.data.redis.port=6379
spring.data.redis.username=default spring.data.redis.username=default
spring.data.redis.password=your_strong_password spring.data.redis.password=Jbjhhzstsl97@
spring.data.redis.ssl.enabled=true spring.data.redis.ssl.enabled=false
mq.queue.cache-update=cache_update_queue
#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 --> <?xml version="1.0" encoding="UTF-8"?>
<select id="selectByCondition" resultType="GpuResource"> <!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
SELECT * FROM ipz.gpu_resource "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<where> <mapper namespace="com.bipt.intelligentapplicationorchestrationservice.gpu.dao.GpuResourceDao">
<if test="gpuId != null">
GPUId = #{gpuId} <!-- 动态条件查询 -->
</if> <select id="selectByFields"
<if test="ipPattern != null and ipPattern != ''"> resultType="com.bipt.intelligentapplicationorchestrationservice.gpu.model.entity.GpuResource">
AND Ip LIKE CONCAT('%', #{ipPattern}, '%') SELECT *
</if> FROM ipz.gpu_resource
<if test="modelKeyword != null and modelKeyword != ''"> <where>
AND GPUModel LIKE CONCAT('%', #{modelKeyword}, '%') is_deleted = 0
</if> <if test="params.model != null and params.model != ''">
<if test="startTime != null and endTime != null"> AND GPUModel LIKE CONCAT('%', #{params.model}, '%')
AND CreatedTime BETWEEN #{startTime} AND #{endTime} </if>
</if> <if test="params.memoryMin != null">
</where> AND GPUMemorySize &gt;= #{params.memoryMin}
ORDER BY GPUId DESC </if>
</select> <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>