Compare commits
6 Commits
c24d640de4
...
4a1e6013b3
Author | SHA1 | Date | |
---|---|---|---|
4a1e6013b3 | |||
2aaf3d96f0 | |||
77fb43e95d | |||
5692cca3e7 | |||
60a71a121c | |||
28b5ca1dfc |
@ -90,17 +90,17 @@ redis的服务器配置
|
|||||||
### 📅 明日计划
|
### 📅 明日计划
|
||||||
完成拦截器功能
|
完成拦截器功能
|
||||||
|
|
||||||
## 2025年5月26日
|
## 2025年5月25日
|
||||||
### ✅ 今日完成
|
### ✅ 今日完成
|
||||||
服务调度
|
服务发布可注册到nacos上
|
||||||
|
|
||||||
### 🚧 进行中
|
### 🚧 进行中
|
||||||
无
|
拦截器开发
|
||||||
|
|
||||||
### ⚠️ 问题/障碍
|
### ⚠️ 问题/障碍
|
||||||
暂无
|
模型api请求不知道是什么
|
||||||
|
|
||||||
### 📅 明日计划
|
### 📅 明日计划
|
||||||
已完成
|
开发拦截器功能
|
||||||
|
|
||||||
|
|
||||||
|
86
pom.xml
86
pom.xml
@ -2,78 +2,110 @@
|
|||||||
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||||
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
|
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
|
||||||
<modelVersion>4.0.0</modelVersion>
|
<modelVersion>4.0.0</modelVersion>
|
||||||
|
|
||||||
<parent>
|
<parent>
|
||||||
<groupId>org.springframework.boot</groupId>
|
<groupId>org.springframework.boot</groupId>
|
||||||
<artifactId>spring-boot-starter-parent</artifactId>
|
<artifactId>spring-boot-starter-parent</artifactId>
|
||||||
<version>3.2.5</version>
|
<version>3.1.5</version>
|
||||||
<relativePath/> <!-- lookup parent from repository -->
|
<relativePath/> <!-- lookup parent from repository -->
|
||||||
</parent>
|
</parent>
|
||||||
|
|
||||||
<groupId>com.bipt</groupId>
|
<groupId>com.bipt</groupId>
|
||||||
<artifactId>intelligent-application-orchestration-service</artifactId>
|
<artifactId>intelligent-application-orchestration-service</artifactId>
|
||||||
<version>0.0.1-SNAPSHOT</version>
|
<version>0.0.1-SNAPSHOT</version>
|
||||||
<name>intelligent-application-orchestration-service</name>
|
<name>intelligent-application-orchestration-service</name>
|
||||||
<description>intelligent-application-orchestration-service</description>
|
<description>intelligent-application-orchestration-service</description>
|
||||||
<url/>
|
<url/>
|
||||||
<licenses>
|
|
||||||
<license/>
|
|
||||||
</licenses>
|
|
||||||
<developers>
|
|
||||||
<developer/>
|
|
||||||
</developers>
|
|
||||||
<scm>
|
|
||||||
<connection/>
|
|
||||||
<developerConnection/>
|
|
||||||
<tag/>
|
|
||||||
<url/>
|
|
||||||
</scm>
|
|
||||||
<properties>
|
<properties>
|
||||||
<java.version>21</java.version>
|
<java.version>21</java.version>
|
||||||
|
<!-- 添加 Spring Cloud 版本控制 -->
|
||||||
|
<spring-cloud.version>2022.0.4</spring-cloud.version>
|
||||||
</properties>
|
</properties>
|
||||||
|
|
||||||
|
<!-- 依赖管理:统一控制 Spring Cloud 和 Alibaba 版本 -->
|
||||||
|
<dependencyManagement>
|
||||||
|
<dependencies>
|
||||||
|
<!-- Spring Cloud 依赖 -->
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.springframework.cloud</groupId>
|
||||||
|
<artifactId>spring-cloud-dependencies</artifactId>
|
||||||
|
<version>${spring-cloud.version}</version>
|
||||||
|
<type>pom</type>
|
||||||
|
<scope>import</scope>
|
||||||
|
</dependency>
|
||||||
|
<!-- Spring Cloud Alibaba 依赖 -->
|
||||||
|
<dependency>
|
||||||
|
<groupId>com.alibaba.cloud</groupId>
|
||||||
|
<artifactId>spring-cloud-alibaba-dependencies</artifactId>
|
||||||
|
<version>2022.0.0.0</version> <!-- 适配 Spring Boot 3.1.x 的正确版本 -->
|
||||||
|
<type>pom</type>
|
||||||
|
<scope>import</scope>
|
||||||
|
</dependency>
|
||||||
|
</dependencies>
|
||||||
|
</dependencyManagement>
|
||||||
|
|
||||||
<dependencies>
|
<dependencies>
|
||||||
|
<!-- Spring Boot 基础依赖 -->
|
||||||
<dependency>
|
<dependency>
|
||||||
<groupId>org.springframework.boot</groupId>
|
<groupId>org.springframework.boot</groupId>
|
||||||
<artifactId>spring-boot-starter-jdbc</artifactId>
|
<artifactId>spring-boot-starter-jdbc</artifactId>
|
||||||
</dependency>
|
</dependency>
|
||||||
<dependency>
|
<!-- <dependency>
|
||||||
<groupId>org.springframework.boot</groupId>
|
<groupId>org.springframework.boot</groupId>
|
||||||
<artifactId>spring-boot-starter-web</artifactId>
|
<artifactId>spring-boot-starter-web</artifactId>
|
||||||
</dependency>
|
</dependency>-->
|
||||||
|
|
||||||
<dependency>
|
<dependency>
|
||||||
<groupId>org.springframework.boot</groupId>
|
<groupId>org.springframework.boot</groupId>
|
||||||
<artifactId>spring-boot-starter-data-redis</artifactId>
|
<artifactId>spring-boot-starter-data-redis</artifactId>
|
||||||
</dependency>
|
</dependency>
|
||||||
|
|
||||||
|
<!-- 数据库驱动 -->
|
||||||
<dependency>
|
<dependency>
|
||||||
<groupId>org.postgresql</groupId>
|
<groupId>org.postgresql</groupId>
|
||||||
<artifactId>postgresql</artifactId>
|
<artifactId>postgresql</artifactId>
|
||||||
<scope>runtime</scope>
|
<scope>runtime</scope>
|
||||||
</dependency>
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>com.kingbase8</groupId>
|
||||||
|
<artifactId>kingbase8</artifactId>
|
||||||
|
<version>9.0.0</version>
|
||||||
|
</dependency>
|
||||||
|
|
||||||
|
<!-- 工具类依赖 -->
|
||||||
<dependency>
|
<dependency>
|
||||||
<groupId>org.projectlombok</groupId>
|
<groupId>org.projectlombok</groupId>
|
||||||
<artifactId>lombok</artifactId>
|
<artifactId>lombok</artifactId>
|
||||||
<version>1.18.38</version>
|
<version>1.18.38</version>
|
||||||
<optional>true</optional>
|
<optional>true</optional>
|
||||||
</dependency>
|
</dependency>
|
||||||
|
|
||||||
<!--分页查询-->
|
|
||||||
<dependency>
|
<dependency>
|
||||||
<groupId>com.github.pagehelper</groupId>
|
<groupId>com.github.pagehelper</groupId>
|
||||||
<artifactId>pagehelper-spring-boot-starter</artifactId>
|
<artifactId>pagehelper-spring-boot-starter</artifactId>
|
||||||
<version>1.4.7</version>
|
<version>1.4.7</version>
|
||||||
</dependency>
|
</dependency>
|
||||||
<!--swagger相关依赖,生成接口文档-->
|
|
||||||
<dependency>
|
<dependency>
|
||||||
<groupId>org.springdoc</groupId>
|
<groupId>org.springdoc</groupId>
|
||||||
<artifactId>springdoc-openapi-starter-webmvc-ui</artifactId>
|
<artifactId>springdoc-openapi-starter-webmvc-ui</artifactId>
|
||||||
<version>2.3.0</version> <!-- 最新稳定版 -->
|
<version>2.3.0</version>
|
||||||
</dependency>
|
</dependency>
|
||||||
|
|
||||||
<!--KingbaseES V8/V9 数据库 JDBC 驱动-->
|
<!-- Nacos 配置依赖(移除手动版本,由上方依赖管理控制) -->
|
||||||
<dependency>
|
<dependency>
|
||||||
<groupId>com.kingbase8</groupId>
|
<groupId>com.alibaba.cloud</groupId>
|
||||||
<artifactId>kingbase8</artifactId>
|
<artifactId>spring-cloud-starter-alibaba-nacos-config</artifactId>
|
||||||
<version>9.0.0</version>
|
|
||||||
</dependency>
|
</dependency>
|
||||||
|
<!-- Spring Boot 3.x 必须的 Bootstrap 依赖(无需版本号) -->
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.springframework.cloud</groupId>
|
||||||
|
<artifactId>spring-cloud-starter-bootstrap</artifactId>
|
||||||
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.springframework.cloud</groupId>
|
||||||
|
<artifactId>spring-cloud-starter-gateway</artifactId>
|
||||||
|
</dependency>
|
||||||
|
|
||||||
|
<!-- 测试依赖 -->
|
||||||
<dependency>
|
<dependency>
|
||||||
<groupId>org.springframework.boot</groupId>
|
<groupId>org.springframework.boot</groupId>
|
||||||
<artifactId>spring-boot-starter-test</artifactId>
|
<artifactId>spring-boot-starter-test</artifactId>
|
||||||
@ -98,6 +130,9 @@
|
|||||||
<groupId>org.apache.maven.plugins</groupId>
|
<groupId>org.apache.maven.plugins</groupId>
|
||||||
<artifactId>maven-compiler-plugin</artifactId>
|
<artifactId>maven-compiler-plugin</artifactId>
|
||||||
<configuration>
|
<configuration>
|
||||||
|
<!-- 显式指定 Java 21 编译 -->
|
||||||
|
<source>${java.version}</source>
|
||||||
|
<target>${java.version}</target>
|
||||||
<annotationProcessorPaths>
|
<annotationProcessorPaths>
|
||||||
<path>
|
<path>
|
||||||
<groupId>org.projectlombok</groupId>
|
<groupId>org.projectlombok</groupId>
|
||||||
@ -121,5 +156,4 @@
|
|||||||
</plugin>
|
</plugin>
|
||||||
</plugins>
|
</plugins>
|
||||||
</build>
|
</build>
|
||||||
|
</project>
|
||||||
</project>
|
|
@ -4,11 +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.cloud.client.discovery.EnableDiscoveryClient;
|
||||||
import org.springframework.transaction.annotation.EnableTransactionManagement;
|
import org.springframework.transaction.annotation.EnableTransactionManagement;
|
||||||
|
|
||||||
@MapperScan("com.bipt.intelligentapplicationorchestrationservice.mapper")//指定扫描Mapper接口的包
|
@MapperScan("com.bipt.intelligentapplicationorchestrationservice.mapper")//指定扫描Mapper接口的包
|
||||||
@SpringBootApplication
|
@SpringBootApplication
|
||||||
@EnableTransactionManagement
|
@EnableTransactionManagement
|
||||||
|
@EnableDiscoveryClient
|
||||||
@Slf4j
|
@Slf4j
|
||||||
public class IntelligentApplicationOrchestrationServiceApplication {
|
public class IntelligentApplicationOrchestrationServiceApplication {
|
||||||
|
|
||||||
|
@ -4,20 +4,18 @@ import io.swagger.v3.oas.models.OpenAPI;
|
|||||||
import io.swagger.v3.oas.models.info.Info;
|
import io.swagger.v3.oas.models.info.Info;
|
||||||
import io.swagger.v3.oas.models.servers.Server;
|
import io.swagger.v3.oas.models.servers.Server;
|
||||||
import lombok.extern.slf4j.Slf4j;
|
import lombok.extern.slf4j.Slf4j;
|
||||||
|
import org.springframework.beans.factory.annotation.Autowired;
|
||||||
import org.springframework.context.annotation.Bean;
|
import org.springframework.context.annotation.Bean;
|
||||||
import org.springframework.context.annotation.Configuration;
|
import org.springframework.context.annotation.Configuration;
|
||||||
import org.springframework.web.servlet.config.annotation.ResourceHandlerRegistry;
|
import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
|
||||||
import org.springframework.web.servlet.config.annotation.WebMvcConfigurationSupport;
|
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
|
||||||
|
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
/**
|
|
||||||
* 配置类,注册web层相关组件
|
|
||||||
*/
|
|
||||||
@Configuration
|
@Configuration
|
||||||
@Slf4j
|
@Slf4j
|
||||||
public class WebMvcConfiguration{
|
public class WebMvcConfiguration implements WebMvcConfigurer {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 配置OpenAPI信息
|
* 配置OpenAPI信息
|
||||||
@ -36,12 +34,4 @@ public class WebMvcConfiguration{
|
|||||||
.servers(servers);
|
.servers(servers);
|
||||||
}
|
}
|
||||||
|
|
||||||
// /**
|
|
||||||
// * 设置静态资源映射(Springdoc不需要特殊配置,保留可能的其他资源映射)
|
|
||||||
// */
|
|
||||||
// @Override
|
|
||||||
// protected void addResourceHandlers(ResourceHandlerRegistry registry) {
|
|
||||||
// // 保留其他静态资源映射(如果有)
|
|
||||||
// // registry.addResourceHandler("/static/**").addResourceLocations("classpath:/static/");
|
|
||||||
// }
|
|
||||||
}
|
}
|
@ -0,0 +1,75 @@
|
|||||||
|
package com.bipt.intelligentapplicationorchestrationservice.controller;
|
||||||
|
|
||||||
|
import com.bipt.intelligentapplicationorchestrationservice.pojo.*;
|
||||||
|
import com.bipt.intelligentapplicationorchestrationservice.service.PublishService;
|
||||||
|
import com.bipt.intelligentapplicationorchestrationservice.util.NacosServiceUtil;
|
||||||
|
import io.swagger.v3.oas.annotations.Operation;
|
||||||
|
import io.swagger.v3.oas.annotations.tags.Tag;
|
||||||
|
import lombok.extern.slf4j.Slf4j;
|
||||||
|
import org.springframework.beans.factory.annotation.Autowired;
|
||||||
|
import org.springframework.data.redis.core.RedisTemplate;
|
||||||
|
import org.springframework.transaction.annotation.Transactional;
|
||||||
|
import org.springframework.web.bind.annotation.*;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
@Tag(name ="服务发布相关接口")
|
||||||
|
@RestController
|
||||||
|
@RequestMapping("/publish")
|
||||||
|
@Slf4j
|
||||||
|
public class PublishController {
|
||||||
|
@Autowired
|
||||||
|
private PublishService publishService;
|
||||||
|
|
||||||
|
@Autowired
|
||||||
|
private RedisTemplate redisTemplate;
|
||||||
|
@Autowired
|
||||||
|
private NacosServiceUtil nacosServiceUtil;
|
||||||
|
|
||||||
|
@PostMapping
|
||||||
|
@Operation(summary ="新增发布请求")
|
||||||
|
@Transactional
|
||||||
|
public OptResult<List<ServicePublishVO>> save(@RequestBody ServicePublishDTO servicePublishDTO) {
|
||||||
|
log.info("模型发布请求:{}", servicePublishDTO);
|
||||||
|
publishService.save(servicePublishDTO);
|
||||||
|
//todo 调用模型部署
|
||||||
|
|
||||||
|
// 获取前端传来的IP字符串
|
||||||
|
String ipListStr = servicePublishDTO.getIp();
|
||||||
|
if (ipListStr == null || ipListStr.trim().isEmpty()) {
|
||||||
|
log.warn("IP列表为空,不进行Nacos注册");
|
||||||
|
return OptResult.success();
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
// 使用逗号分割IP字符串
|
||||||
|
String[] ipArray = ipListStr.split(",");
|
||||||
|
// 循环注册每个IP到Nacos
|
||||||
|
for (String ip : ipArray) {
|
||||||
|
String trimmedIp = ip.trim();
|
||||||
|
if (!trimmedIp.isEmpty()) {
|
||||||
|
nacosServiceUtil.registerService(
|
||||||
|
servicePublishDTO.getModelId().toString(),
|
||||||
|
trimmedIp,
|
||||||
|
8080,
|
||||||
|
servicePublishDTO.getApiUrl()
|
||||||
|
);
|
||||||
|
log.info("Nacos服务注册成功: {}", trimmedIp);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} catch (Exception e) {
|
||||||
|
log.error("Nacos服务注册失败", e);
|
||||||
|
return OptResult.error("Nacos服务注册失败"); // 根据业务需求返回错误
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
return OptResult.success();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
}
|
@ -0,0 +1,178 @@
|
|||||||
|
package com.bipt.intelligentapplicationorchestrationservice.controller;
|
||||||
|
|
||||||
|
|
||||||
|
import com.bipt.intelligentapplicationorchestrationservice.pojo.OptResult;
|
||||||
|
import com.bipt.intelligentapplicationorchestrationservice.service.ServiceAPIService;
|
||||||
|
import com.bipt.intelligentapplicationorchestrationservice.util.NacosServiceUtil;
|
||||||
|
import io.swagger.v3.oas.annotations.Operation;
|
||||||
|
import io.swagger.v3.oas.annotations.tags.Tag;
|
||||||
|
import io.swagger.v3.oas.models.security.SecurityScheme;
|
||||||
|
import lombok.extern.slf4j.Slf4j;
|
||||||
|
import org.springframework.beans.factory.annotation.Autowired;
|
||||||
|
import org.springframework.data.redis.core.RedisTemplate;
|
||||||
|
import org.springframework.transaction.annotation.Transactional;
|
||||||
|
import org.springframework.web.bind.annotation.PathVariable;
|
||||||
|
import org.springframework.web.bind.annotation.PostMapping;
|
||||||
|
import org.springframework.web.bind.annotation.RequestMapping;
|
||||||
|
import org.springframework.web.bind.annotation.RestController;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Set;
|
||||||
|
import java.util.concurrent.TimeUnit;
|
||||||
|
|
||||||
|
@Tag(name ="服务API相关接口")
|
||||||
|
@RestController
|
||||||
|
@RequestMapping("/API")
|
||||||
|
@Slf4j
|
||||||
|
public class ServiceAPIController {
|
||||||
|
|
||||||
|
@Autowired
|
||||||
|
private ServiceAPIService serviceAPIService;
|
||||||
|
|
||||||
|
@Autowired
|
||||||
|
private NacosServiceUtil nacosServiceUtil;
|
||||||
|
|
||||||
|
@Autowired
|
||||||
|
private RedisTemplate<String, Object> redisTemplate;
|
||||||
|
@PostMapping("/release")
|
||||||
|
@Operation(summary = "结束访问")
|
||||||
|
@Transactional
|
||||||
|
public OptResult releaseResource(@PathVariable Long modelId) {
|
||||||
|
String key = "modelId:" + modelId;
|
||||||
|
String modelConfig = (String) redisTemplate.opsForValue().get(key);
|
||||||
|
int userMemorySize = parseGpuMemorySize(modelConfig);
|
||||||
|
List<String> instanceIps;
|
||||||
|
try {
|
||||||
|
instanceIps = nacosServiceUtil.getServiceInstances(modelId.toString());
|
||||||
|
} catch (Exception e) {
|
||||||
|
log.error("获取Nacos实例失败", e);
|
||||||
|
return OptResult.error("获取实例失败");
|
||||||
|
}
|
||||||
|
int memorySize;
|
||||||
|
for (String ip : instanceIps) {
|
||||||
|
String ipKey = "ip:" + ip;
|
||||||
|
Integer nowMemorySizeOBJ = (Integer) redisTemplate.opsForValue().get(ipKey);
|
||||||
|
int nowMemorySize = nowMemorySizeOBJ;
|
||||||
|
memorySize = nowMemorySize + userMemorySize;
|
||||||
|
// 更新IP对应的资源值
|
||||||
|
redisTemplate.opsForValue().set(ipKey, memorySize);
|
||||||
|
// 设置缓存过期时间(3600秒)
|
||||||
|
redisTemplate.expire(ipKey, 3600, TimeUnit.SECONDS);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 处理等待队列(先来先服务)
|
||||||
|
String waitQueueKey = "waitQueue:" + modelId;
|
||||||
|
// 取出队列头部的任务(最早加入的)
|
||||||
|
Long waitModelId = (Long) redisTemplate.opsForList().leftPop(waitQueueKey);
|
||||||
|
if (waitModelId != null) {
|
||||||
|
log.info("检测到等待队列任务,尝试调度模型ID: {}", waitModelId);
|
||||||
|
return schedule(waitModelId); // 重新调度最早的任务
|
||||||
|
} else {
|
||||||
|
log.info("等待队列为空,无任务需要处理");
|
||||||
|
}
|
||||||
|
|
||||||
|
return OptResult.success("资源释放成功");
|
||||||
|
}
|
||||||
|
|
||||||
|
@PostMapping("/request")
|
||||||
|
@Operation(summary = "请求调度")
|
||||||
|
@Transactional
|
||||||
|
public OptResult schedule(@PathVariable Long modelId) {
|
||||||
|
// 1. 存储modelConfig到缓存
|
||||||
|
String modelConfig = serviceAPIService.getByModelId(modelId);
|
||||||
|
int requestMemorySize = parseGpuMemorySize(modelConfig);
|
||||||
|
String modelConfigKey = "modelConfig:" + modelId;
|
||||||
|
redisTemplate.opsForValue().set(modelConfigKey, modelConfig);
|
||||||
|
// 2. 获取Nacos实例IP列表
|
||||||
|
List<String> instanceIps;
|
||||||
|
try {
|
||||||
|
instanceIps = nacosServiceUtil.getServiceInstances(modelId.toString());
|
||||||
|
} catch (Exception e) {
|
||||||
|
log.error("获取Nacos实例失败", e);
|
||||||
|
return OptResult.error("获取实例失败");
|
||||||
|
}
|
||||||
|
Set<String> gpuKeys = redisTemplate.keys("gpu:*");
|
||||||
|
//根据IP列表查找资源
|
||||||
|
for (String instanceIp : instanceIps) {
|
||||||
|
for (String gpuKey : gpuKeys) {
|
||||||
|
String GPUConfig = (String) redisTemplate.opsForValue().get(gpuKey);
|
||||||
|
if (GPUConfig != null) {
|
||||||
|
// 分割键值对
|
||||||
|
String[] pairs = GPUConfig.split(",");
|
||||||
|
String ip = null;
|
||||||
|
int memorySize = 0;
|
||||||
|
for (String pair : pairs) {
|
||||||
|
String[] keyValue = pair.split(":", 2);
|
||||||
|
if (keyValue.length == 2) {
|
||||||
|
String key = keyValue[0].trim();
|
||||||
|
String value = keyValue[1].trim();
|
||||||
|
if ("IP".equalsIgnoreCase(key)) {
|
||||||
|
ip = value;
|
||||||
|
} else if ("GPUMemorySize".equalsIgnoreCase(key)) {
|
||||||
|
memorySize = Integer.parseInt(value);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// 检查解析出的 IP 是否在 Nacos 实例列表中
|
||||||
|
if (instanceIp.equals(ip)) {
|
||||||
|
log.info("找到 IP {} 对应的 GPU 内存: {} ", ip, memorySize);
|
||||||
|
if (memorySize>=requestMemorySize){
|
||||||
|
int newMemorySize = memorySize - requestMemorySize;
|
||||||
|
String ipKey = "ip:" + ip;
|
||||||
|
redisTemplate.opsForValue().set(ipKey,newMemorySize);
|
||||||
|
//访问请求最大时间为3600s
|
||||||
|
redisTemplate.expire(ipKey, 3600, TimeUnit.SECONDS);
|
||||||
|
}
|
||||||
|
return OptResult.success("资源分配成功,使用ip:" + ip);
|
||||||
|
}else {
|
||||||
|
log.info("资源不足");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// 所有实例检查完毕未找到足够资源
|
||||||
|
String waitQueueKey = "waitQueue:" + modelId;
|
||||||
|
// 改为右插入,保证队列顺序为FIFO(最早的任务在列表头部)
|
||||||
|
redisTemplate.opsForList().rightPush(waitQueueKey, modelId);
|
||||||
|
log.info("未找到足够资源,任务 {} 加入等待队列", modelId);
|
||||||
|
return OptResult.error("资源不足,等待中");
|
||||||
|
}
|
||||||
|
/**
|
||||||
|
* 从模型配置字符串中解析GPU内存需求
|
||||||
|
* @param modelConfig 模型配置字符串,格式如 "GPUMemorySize:8000,version:1"
|
||||||
|
* @return 解析到的GPU内存大小(MB),若解析失败返回-1
|
||||||
|
*/
|
||||||
|
private int parseGpuMemorySize(String modelConfig) {
|
||||||
|
if (modelConfig == null || modelConfig.isEmpty()) {
|
||||||
|
log.error("模型配置为空,无法解析GPU内存需求");
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
int requestMemorySize = 0;
|
||||||
|
String[] config = modelConfig.split(",");
|
||||||
|
for (String pair : config) {
|
||||||
|
// 按冒号分割键值对
|
||||||
|
String[] keyValue = pair.split(":", 2);
|
||||||
|
if (keyValue.length == 2) {
|
||||||
|
String key = keyValue[0].trim();
|
||||||
|
String value = keyValue[1].trim();
|
||||||
|
// 匹配 GPUMemorySize 字段(忽略大小写)
|
||||||
|
if ("GPUMemorySize".equalsIgnoreCase(key)) {
|
||||||
|
try {
|
||||||
|
requestMemorySize = Integer.parseInt(value);
|
||||||
|
log.info("模型GPU内存: {} MB", requestMemorySize);
|
||||||
|
break; // 找到后即可退出循环
|
||||||
|
} catch (NumberFormatException e) {
|
||||||
|
log.error("解析GPUMemorySize失败,值: {}", value, e);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (requestMemorySize <= 0) {
|
||||||
|
log.error("模型需求GPU内存未配置或无效");
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
return requestMemorySize;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -1,112 +0,0 @@
|
|||||||
package com.bipt.intelligentapplicationorchestrationservice.controller;
|
|
||||||
|
|
||||||
import com.bipt.intelligentapplicationorchestrationservice.config.RedisConfiguration;
|
|
||||||
import com.bipt.intelligentapplicationorchestrationservice.pojo.*;
|
|
||||||
import com.bipt.intelligentapplicationorchestrationservice.service.PublishService;
|
|
||||||
import io.swagger.v3.oas.annotations.Operation;
|
|
||||||
import io.swagger.v3.oas.annotations.tags.Tag;
|
|
||||||
import lombok.extern.slf4j.Slf4j;
|
|
||||||
import org.springframework.beans.factory.annotation.Autowired;
|
|
||||||
import org.springframework.data.redis.core.RedisTemplate;
|
|
||||||
import org.springframework.transaction.annotation.Transactional;
|
|
||||||
import org.springframework.web.bind.annotation.*;
|
|
||||||
|
|
||||||
import java.util.ArrayList;
|
|
||||||
import java.util.List;
|
|
||||||
|
|
||||||
@Tag(name ="服务发布相关接口")
|
|
||||||
@RestController
|
|
||||||
@RequestMapping("/publish")
|
|
||||||
@Slf4j
|
|
||||||
public class publishController {
|
|
||||||
@Autowired
|
|
||||||
private PublishService publishService;
|
|
||||||
|
|
||||||
@Autowired
|
|
||||||
private RedisTemplate redisTemplate;
|
|
||||||
/**
|
|
||||||
* 新增请求发布
|
|
||||||
* @param servicePublishDTO
|
|
||||||
* @return
|
|
||||||
*/
|
|
||||||
@PostMapping
|
|
||||||
@Operation(summary ="新增发布请求")
|
|
||||||
@Transactional
|
|
||||||
public OptResult<List<ServicePublishVO>> save(@RequestBody ServicePublishDTO servicePublishDTO) {
|
|
||||||
log.info("模型发布请求:{}", servicePublishDTO);
|
|
||||||
publishService.save(servicePublishDTO);
|
|
||||||
Long modelId = servicePublishDTO.getModelId();
|
|
||||||
String key = "Model_" + modelId;
|
|
||||||
//查询redis是否存在GPU相关资源数据
|
|
||||||
List<ServicePublishVO> list;
|
|
||||||
list = (List<ServicePublishVO>) redisTemplate.opsForValue().get(key);
|
|
||||||
//如果存在,直接返回,无须查询数据库
|
|
||||||
if (list != null) {
|
|
||||||
return OptResult.success(list);
|
|
||||||
}else {
|
|
||||||
list = new ArrayList<>();
|
|
||||||
}
|
|
||||||
String modelConfig = publishService.getByModelId(modelId);
|
|
||||||
if (modelConfig == null) {
|
|
||||||
log.error("模型配置为空,modelId={}", modelId);
|
|
||||||
}
|
|
||||||
String[] keyValuePairs = modelConfig.split("\\|");
|
|
||||||
String GPUMemorySize = null;
|
|
||||||
String GPUModel = null;
|
|
||||||
for (String pair : keyValuePairs) {
|
|
||||||
pair = pair.trim();
|
|
||||||
if (pair.startsWith("GPU")) {
|
|
||||||
GPUModel = pair.split(";", 2)[1];
|
|
||||||
} else if (pair.startsWith("Memory:")) {
|
|
||||||
GPUMemorySize = pair.split(":", 2)[1];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
ServicePublishVO servicePublishVO = new ServicePublishVO();
|
|
||||||
servicePublishVO.setIp(servicePublishDTO.getIp());
|
|
||||||
servicePublishVO.setModelId(servicePublishDTO.getModelId());
|
|
||||||
servicePublishVO.setGPUMemorySize(GPUMemorySize);
|
|
||||||
servicePublishVO.setGPUModel(GPUModel);
|
|
||||||
//todo 调用模型部署,传递信息
|
|
||||||
|
|
||||||
servicePublishVO.setApiUrl(servicePublishDTO.getApiUrl());
|
|
||||||
list.add(servicePublishVO);
|
|
||||||
redisTemplate.opsForValue().set(key,list);
|
|
||||||
//一个ip上有多个机器
|
|
||||||
// 假设从 Redis 获取的列表元素是 MachineInfo 类型
|
|
||||||
String ip = servicePublishVO.getIp();
|
|
||||||
String key1 = ip;
|
|
||||||
List<MachineInfo> machineList = (List<MachineInfo>) redisTemplate.opsForValue().get(key1);
|
|
||||||
|
|
||||||
|
|
||||||
// 模型所需的 GPU 资源
|
|
||||||
String requiredGPUModel = servicePublishVO.getGPUModel();
|
|
||||||
Integer requiredGPUMemory = Integer.valueOf(servicePublishVO.getGPUMemorySize());
|
|
||||||
|
|
||||||
if (machineList != null) {
|
|
||||||
for (MachineInfo machine : machineList) {
|
|
||||||
// 获取机器的 GPU 资源
|
|
||||||
String machineGPUModel = machine.getGPUModel();
|
|
||||||
Integer machineGPUMemory = machine.getGPUMemorySize();
|
|
||||||
|
|
||||||
// 判断机器是否满足模型需求
|
|
||||||
if (requiredGPUModel.equals(machineGPUModel) &&
|
|
||||||
machineGPUMemory >= requiredGPUMemory) {
|
|
||||||
return OptResult.success(list);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
String key3 = "wait_queue";
|
|
||||||
redisTemplate.opsForValue().set(key3,list);
|
|
||||||
//todo资源释放时候优先分配等待队列中任务
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
return OptResult.success(list);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
}
|
|
@ -0,0 +1,36 @@
|
|||||||
|
package com.bipt.intelligentapplicationorchestrationservice.filter;
|
||||||
|
|
||||||
|
import org.springframework.cloud.gateway.filter.GatewayFilterChain;
|
||||||
|
import org.springframework.cloud.gateway.filter.GlobalFilter;
|
||||||
|
import org.springframework.core.Ordered;
|
||||||
|
import org.springframework.http.HttpMethod;
|
||||||
|
import org.springframework.http.server.reactive.ServerHttpRequest;
|
||||||
|
import org.springframework.stereotype.Component;
|
||||||
|
import org.springframework.web.server.ServerWebExchange;
|
||||||
|
import reactor.core.publisher.Mono;
|
||||||
|
|
||||||
|
@Component
|
||||||
|
public class ApiRequestGlobalFilter implements GlobalFilter, Ordered {
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
|
||||||
|
ServerHttpRequest request = exchange.getRequest();
|
||||||
|
|
||||||
|
// 检查请求路径和方法
|
||||||
|
if (request.getURI().getPath().equals("/request") &&
|
||||||
|
request.getMethod() == HttpMethod.POST) {
|
||||||
|
|
||||||
|
// 在此处添加拦截逻辑
|
||||||
|
System.out.println("拦截到POST /request请求");
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
// 继续处理请求
|
||||||
|
return chain.filter(exchange);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int getOrder() {
|
||||||
|
return 1; // 过滤器执行顺序,数值越小越先执行
|
||||||
|
}
|
||||||
|
}
|
@ -14,7 +14,6 @@ public interface PublishMapper {
|
|||||||
void insert(ServicePublishDTO servicePublishDTO);
|
void insert(ServicePublishDTO servicePublishDTO);
|
||||||
|
|
||||||
Long getByApiUrl(String apiUrl);
|
Long getByApiUrl(String apiUrl);
|
||||||
@Select("select model_config from model_version where model_id=#{modelId} and status = 1")
|
|
||||||
String getById(Long modelId);
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -0,0 +1,11 @@
|
|||||||
|
package com.bipt.intelligentapplicationorchestrationservice.mapper;
|
||||||
|
|
||||||
|
|
||||||
|
import org.apache.ibatis.annotations.Mapper;
|
||||||
|
import org.apache.ibatis.annotations.Select;
|
||||||
|
|
||||||
|
@Mapper
|
||||||
|
public interface ServiceAPIMapper {
|
||||||
|
@Select("select model_config from model_version where model_id=#{modelId} and status = 1")
|
||||||
|
String getById(Long modelId);
|
||||||
|
}
|
@ -29,18 +29,12 @@ public class PublishServiceImpl implements PublishService {
|
|||||||
if (id != null){
|
if (id != null){
|
||||||
throw new IllegalArgumentException("请求已存在: " + apiUrl);
|
throw new IllegalArgumentException("请求已存在: " + apiUrl);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//todo调用服务部署
|
||||||
|
|
||||||
publishMapper.insert(servicePublishDTO);
|
publishMapper.insert(servicePublishDTO);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* 根据id查找配置信息
|
|
||||||
* @param modelId
|
|
||||||
* @return
|
|
||||||
*/
|
|
||||||
@Override
|
|
||||||
public String getByModelId(Long modelId) {
|
|
||||||
return publishMapper.getById(modelId);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -0,0 +1,23 @@
|
|||||||
|
package com.bipt.intelligentapplicationorchestrationservice.service.Impl;
|
||||||
|
|
||||||
|
import com.bipt.intelligentapplicationorchestrationservice.mapper.ServiceAPIMapper;
|
||||||
|
import com.bipt.intelligentapplicationorchestrationservice.service.ServiceAPIService;
|
||||||
|
import lombok.extern.slf4j.Slf4j;
|
||||||
|
import org.springframework.beans.factory.annotation.Autowired;
|
||||||
|
import org.springframework.stereotype.Service;
|
||||||
|
|
||||||
|
@Service
|
||||||
|
@Slf4j
|
||||||
|
public class ServiceAPIImpl implements ServiceAPIService {
|
||||||
|
@Autowired
|
||||||
|
private ServiceAPIMapper serviceAPIMapper;
|
||||||
|
/**
|
||||||
|
* 根据id查找配置信息
|
||||||
|
* @param modelId
|
||||||
|
* @return
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public String getByModelId(Long modelId) {
|
||||||
|
return serviceAPIMapper.getById(modelId);
|
||||||
|
};
|
||||||
|
}
|
@ -9,5 +9,4 @@ public interface PublishService {
|
|||||||
|
|
||||||
|
|
||||||
|
|
||||||
String getByModelId(Long modelId);
|
|
||||||
}
|
}
|
||||||
|
@ -0,0 +1,5 @@
|
|||||||
|
package com.bipt.intelligentapplicationorchestrationservice.service;
|
||||||
|
|
||||||
|
public interface ServiceAPIService {
|
||||||
|
String getByModelId(Long modelId);
|
||||||
|
}
|
@ -0,0 +1,42 @@
|
|||||||
|
package com.bipt.intelligentapplicationorchestrationservice.util;
|
||||||
|
|
||||||
|
import com.alibaba.nacos.api.naming.NamingFactory;
|
||||||
|
import com.alibaba.nacos.api.naming.NamingService;
|
||||||
|
import com.alibaba.nacos.api.naming.pojo.Instance;
|
||||||
|
import org.springframework.beans.factory.annotation.Value;
|
||||||
|
import org.springframework.stereotype.Component;
|
||||||
|
|
||||||
|
import java.util.HashMap;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Map;
|
||||||
|
import java.util.stream.Collectors;
|
||||||
|
|
||||||
|
@Component
|
||||||
|
public class NacosServiceUtil {
|
||||||
|
|
||||||
|
@Value("${spring.cloud.nacos.discovery.server-addr}")
|
||||||
|
private String nacosServerAddr;
|
||||||
|
|
||||||
|
public void registerService(String serviceName, String ip, int port, String url) throws Exception { // 新增url参数
|
||||||
|
NamingService naming = NamingFactory.createNamingService(nacosServerAddr);
|
||||||
|
Instance instance = new Instance();
|
||||||
|
instance.setIp(ip);
|
||||||
|
instance.setPort(port);
|
||||||
|
// 添加元数据存储URL
|
||||||
|
Map<String, String> metadata = new HashMap<>();
|
||||||
|
metadata.put("url", url); // 将URL存入元数据
|
||||||
|
instance.setMetadata(metadata);
|
||||||
|
naming.registerInstance(serviceName, instance);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取服务所有实例IP
|
||||||
|
*/
|
||||||
|
public List<String> getServiceInstances(String serviceName) throws Exception {
|
||||||
|
NamingService naming = NamingFactory.createNamingService(nacosServerAddr);
|
||||||
|
List<Instance> instances = naming.getAllInstances(serviceName);
|
||||||
|
return instances.stream()
|
||||||
|
.map(Instance::getIp)
|
||||||
|
.collect(Collectors.toList());
|
||||||
|
}
|
||||||
|
}
|
@ -10,23 +10,23 @@ 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
|
||||||
|
|
||||||
# SQL映射文件路径配置
|
# MyBatis配置
|
||||||
mybatis.mapper-locations=classpath:mapper/*.xml
|
mybatis.mapper-locations=classpath:mapper/*.xml
|
||||||
# 配置实体类别名所在包
|
|
||||||
mybatis.type-aliases-package=com.bipt.intelligentapplicationorchestrationservice.pojo
|
mybatis.type-aliases-package=com.bipt.intelligentapplicationorchestrationservice.pojo
|
||||||
# 开启驼峰命名转换
|
|
||||||
mybatis.configuration.map-underscore-to-camel-case=true
|
mybatis.configuration.map-underscore-to-camel-case=true
|
||||||
|
|
||||||
# Redis服务器地址
|
# Redis配置
|
||||||
spring.data.redis.host=116.205.121.200
|
spring.data.redis.host=116.205.121.200
|
||||||
# Redis服务器端口
|
|
||||||
spring.data.redis.port=6379
|
spring.data.redis.port=6379
|
||||||
# Redis密码(如果有)
|
|
||||||
spring.data.redis.password=Jbjhhzstsl97@
|
spring.data.redis.password=Jbjhhzstsl97@
|
||||||
# Redis数据库索引(默认为0)
|
spring.data.redis.database=0
|
||||||
spring.data.redis.database = 0
|
spring.data.redis.timeout=3000
|
||||||
# 连接超时时间(毫秒)
|
|
||||||
spring.data.redis.timeout = 3000
|
# 服务路由配置
|
||||||
|
spring.cloud.gateway.routes[0].id=request-service-route
|
||||||
|
spring.cloud.gateway.routes[0].uri=lb://intelligent-application-orchestration-service
|
||||||
|
spring.cloud.gateway.routes[0].predicates[0]=Path=/request
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
#SQL 语句日志输出配置
|
#SQL 语句日志输出配置
|
||||||
|
10
src/main/resources/bootstrap.properties
Normal file
10
src/main/resources/bootstrap.properties
Normal file
@ -0,0 +1,10 @@
|
|||||||
|
# 应用名称(必须与Nacos配置的dataId前缀一致)
|
||||||
|
spring.application.name=intelligent-application-orchestration-service
|
||||||
|
|
||||||
|
# Nacos配置中心地址(引导阶段加载配置)
|
||||||
|
spring.cloud.nacos.config.server-addr=192.168.100.1:8848
|
||||||
|
spring.cloud.nacos.config.data-id=${spring.application.name}.properties
|
||||||
|
spring.cloud.nacos.config.group=DEFAULT_GROUP
|
||||||
|
|
||||||
|
# Nacos服务注册地址(引导阶段注册服务)
|
||||||
|
spring.cloud.nacos.discovery.server-addr=192.168.100.1:8848
|
Reference in New Issue
Block a user