java基于nacos手动分发任务给多个执行器实例

开发需求大致如下:

轮询策略可以自行更改,这个流程图是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接口实现以上功能

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值