开发需求大致如下:
轮询策略可以自行更改,这个流程图是chatGPT自动生成的,目前我的代码逻辑是优先分发任务数少的执行器
+---------------------+
| 监控器 |
+---------------------+
|
|1. 监控器接收到任务
|
v
+---------------------+
| 执行器1 |
+---------------------+
|
|2a. 执行器1有空闲任务槽
|2b. 执行器1任务槽已满
|
v
+---------------------+
| 执行器2 |
+---------------------+
|
|3a. 执行器2有空闲任务槽
|3b. 执行器2任务槽已满
|
v
+---------------------+
| 执行器3 |
+---------------------+
|
|4a. 执行器3有空闲任务槽
|4b. 执行器3任务槽已满
|
v
+---------------------+
| 进行下一次轮询 |
+---------------------+
开发框架
大致框架 springcloud,springboot
目前分为两个服务,一个监控服务-monitor,一个执行服务-actuator
两个服务可以公用一张表
表名:monitor_data
CREATE TABLE `monitor_data` (
`id` bigint(20) NOT NULL AUTO_INCREMENT,
`creat_time` datetime NULL DEFAULT NULL COMMENT '任务提交到管理中心时间',
`status` varchar(1) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL COMMENT '状态 0:pending 1:submit 2:running 3:success 4:failed',
`end_time` datetime NULL DEFAULT NULL COMMENT '停止时间',
`nacos_info` varchar(255) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL COMMENT 'nacos注册信息',
PRIMARY KEY (`id`) USING BTREE
) ENGINE = InnoDB AUTO_INCREMENT = 1 CHARACTER SET = utf8 COLLATE = utf8_general_ci COMMENT = '监控' ROW_FORMAT = DYNAMIC;
表中其他字段已经删除,可以自行添加
minotor服务主要负责其他服务提交任务,然后分配任务以及获取nacos注册的信息
提交任务相关代码
@ApiOperation("应用端提交单个任务到队列")
@PostMapping(value = "submit_task")
public Response runExecute(@RequestBody MonitorData task) {
Long id = monitorDataCleanerService.submitTask(saveTask);
return ResultWrap.ok(id);
}
/**
逻辑大概就是直接查表
*/
获取nacos信息主要代码
@Value("${spring.cloud.nacos.discovery.server-addr}")
private String serverAddr;
@Value("${spring.cloud.nacos.discovery.group}")
private String groupName;
@Value("${spring.cloud.nacos.discovery.namespace}")
private String namespaceName;
public List<Instance> getNacosList() throws NacosException {
Properties props = new Properties();
//连接信息
props.setProperty("serverAddr", serverAddr);
//命名空间
props.put(PropertyKeyConst.NAMESPACE, namespaceName);
NamingService namingService = NacosFactory.createNamingService(props);
if(null == groupName){
groupName = "DEFAULT_GROUP";
}
// 查询服务列表
//第一个参数为服务名称,第二个参数为服务分组名称,第三个参数为是否只查询健康的实例
List<Instance> instances = namingService.selectInstances("actuator", groupName, true);
return instances;
}
分配任务主要代码
@Component
public class JobRunnerStarter implements ApplicationRunner {
private static final Logger logger = LoggerFactory.getLogger(JobRunnerStarter.class);
@Autowired
private IMonitorDataService monitorDataService;
@Autowired
private INacosService nacosService;
@Value("${max-exec-count}")
private Integer maxExecCount;
@Override
public void run(ApplicationArguments args) throws Exception {
while (true) {
try {
//获取一条未运行的任务
// select * from monitor_data where status = #{status} limit 1
MonitorDataDO firstTask = monitorDataService.getTopTask(MonitorStatusEnum.PENDING.getCode());
//目前nacos所有存活的执行器信息
final List<Instance> nacosList = nacosService.getNacosList();
System.out.println("目前机器节点");
nacosList.forEach(System.out::println);
if(CollectionUtil.isNotEmpty(nacosList)){
//获取进行中的任务
List<MonitorDataCleanerDO> runningTaskList = monitorDataService.getRunningTask(MonitorStatusEnum.RUNNING.getCode());
Map<String, Integer> taskMap = runningTaskList.stream().collect(Collectors.toMap(p -> p.getNacosInfo(), s -> 1, Integer::sum));
final List<String> nacosInfo = nacosList.stream().map(p -> p.getIp() + ":" + p.getPort()).collect(Collectors.toList());
if (null != firstTask) {
//如果运行任务数 大于 最大可执行数与机器 乘积,代表暂时不需要分配任务
if (runningTaskList.size() < maxExecCount * nacosList.size()) {
// 找到任务最少的机器
String nextMachine = null;
int minTasks = Integer.MAX_VALUE;
for (String machine : nacosInfo) {
int tasks = taskMap.getOrDefault(machine, 0);
if (tasks < maxExecCount && tasks < minTasks) {
nextMachine = machine;
minTasks = tasks;
}
}
firstTask.setNacosInfo(nextMachine);
monitorDataService.update(firstTask);
//远程调用,调用之后开始执行任务,下面远程调用的包是hutool包
String url = "http://" + nextMachine + "/actuator/feign/exec_task";
Map<String, Object> paramMap = new HashMap<>();
paramMap.put("id", firstTask.getId());
HttpUtil.post(url, paramMap);
}
}
//检查目前正在运行中的任务执行器是否存在
final Map<String, List<MonitorDataDO>> runningTaskMap = runningTaskList.stream().collect(Collectors.groupingBy(p -> p.getNacosInfo()));
final Set<String> runNacosInfo = runningTaskMap.keySet();
for (String str : runNacosInfo) {
//所有的nacos信息不包含正在运行的nacos信息,直接让其重新分配
if (!nacosInfo.contains(str)) {
logger.info("任务执行器不存在,重新分配:{}", str);
{
final List<MonitorDataDO> monitorDataDOS = runningTaskMap.get(str);
for (MonitorDataDO monitorDataDO : monitorDataDOS) {
//todo 可能其他的属性也需要置空
// update monitor_data set status = '0',nacos_info = null where id = #{id}
monitorDataService.redistribution(monitorDataDO.getId());
}
}
}
}
}
} catch (Exception e) {
e.printStackTrace();
}
}
}
}
轮询算法可以根据实际要求自行修改
接下来是执行器相关代码
执行器代码
```java
@RestController
@RequestMapping("/actuator/feign")
@Api(tags = "data监控")
public class OutActuatorController {
@Autowired
private IActuatorDataService monitorDataService;
/**
* @param id
* @return
* @apiNote monitor 服务在使用此接口
*/
@ApiOperation("执行任务")
@PostMapping(value = "exec_task")
@InnerRequest
public Response execTask(Long id) {
System.out.println("任务开始");
monitorDataCleanerService.execTask(id);
return ResultWrap.ok();
}
}
以上就是大致代码,实际环境中,运行多个actuator代码,monitor可以进行轮询分发任务,下次更新如何使用feign接口实现以上功能