V2EX = way to explore
V2EX 是一个关于分享和探索的地方
现在注册
已注册用户请  登录
codermax
V2EX  ›  Java

关于如何优雅的停止任务

  •  
  •   codermax · 274 天前 · 2405 次点击
    这是一个创建于 274 天前的主题,其中的信息可能已经有所发展或是发生改变。

    第一次发帖还是提问贴,如有不妥还请诸多包涵,多多谅解。

    目前有个接口任务,这个任务链比较长,里面包含了对外部系统的调用,异步查询外部任务的执行结果,文件上传,数据获取等,就是比较复杂,但是整个任务链属于一个任务,在数据库中有个对应的任务表维护任务的状态(运行中、超时、结束等)

    目前的需求是想通过另一个接口对上面这个任务进行操作,需要在任意时刻停止该任务,然后更新表中的任务时刻状态。然后反馈给前端。

    目前我的想法是在 redis 或者其他地方写入一个标识,在不同的耗时子任务后根据这个标识来停止任务,但是这么写比较繁杂,而且后续维护肯定也不太好,另一个想法(也是我请教另一个哥们的)是所有子任务维护到一个 map 中,做统一的判断启停,但是任务状态维护可能是个问题。

    所以想问问各位大佬们,有没有别的比较优雅的方法,不胜感激!!!

    16 条回复    2023-07-28 17:57:28 +08:00
    yazinnnn
        1
    yazinnnn  
       274 天前
    https://www.reactive-streams.org/

    随便找个 reactive streams 的实现, 比如 reactor 或者 rxjava, 把你的异步任务拆碎
    codermax
        2
    codermax  
    OP
       274 天前 via Android
    @yazinnnn 感谢回复,大概看了下,感觉改造成本比较大,还要引入别的依赖。
    sumarker
        3
    sumarker  
       274 天前
    执行中的任务要立刻停止的话,肯定是直接杀进程最快吧
    如果要等,那就维护一个队列,控制进入的入口,如果要停就 关闭入口,等队列都跑完了,再关进程
    hangszhang
        4
    hangszhang  
       274 天前
    任务进程化,然后管理进程
    hangszhang
        5
    hangszhang  
       274 天前
    比如用 java -jar 起一个进程,拿到这个进程的 pid ,终止的方式就是 kill 进程
    wushigejiajia01
        6
    wushigejiajia01  
       274 天前
    插眼学习下
    rekulas
        7
    rekulas  
       274 天前
    就加标识不就最简单有效的,为什么会觉得开发维护麻烦
    yazinnnn
        8
    yazinnnn  
       274 天前
    @codermax
    引入依赖问题不大, 一般 java 框架比如 spring quarkus 之类都集成了 reactive streams, 重点是怎么把任务拆了, 而且取消时产生的副作用怎么处理(比如数据库(这个可以加事务), 上传完成但业务上作废的文件)
    37Y37
        9
    37Y37  
       274 天前
    正好写过几乎完全一样的功能,你这里所说的断开就是执行中的强制结束吧,我是 python 写的,你可以参考下看看
    37Y37
        10
    37Y37  
       274 天前
    kanepan19
        11
    kanepan19  
       274 天前
    可以用 java 的 中断 ....... 然而并不优雅
    fantathat
        12
    fantathat  
       274 天前 via iPhone   ❤️ 1
    任务比较耗时吧,必须要有一个公用的状态来判断是否执行当前阶段的子任务
    burymme11
        13
    burymme11  
       274 天前
    任务中使用一个标志变量来指示任务是否应该停止虽然看起来会比较麻烦,但是这种方式会比较稳,直接杀线程可能会导致一些意外。我给个写法思路,你看看是否可以帮你。
    private Boolean handle0(Object... arg) {
    // Prcoess 任务抽象接口。在 list 中排好子任务的执行顺序,从 list 中取子任务的实现类
    Prcoess process = TaskProcessList.get(this.startIndex);
    boolean taskRes = process.doTask(arg);
    // 查询任务中断标识
    boolean interruptFlag = getInterruptFlag();
    // 子任务执行成功 && 非最后一个子任务 && 中断标识为 false -> 继续递归,执行下一个任务。
    if (taskRes && this.startIndex < TaskProcessList.size() - 1 && !interruptFlag) {
    this.startIndex = this.startIndex + 1;
    return handle0(arg);
    }
    // 收到任务中断信号
    if (interruptFlag) {
    // update task status 。根据下标也可以知道哪些子任务已经执行,哪些未执行。
    // todo
    // 收到任务中断信号算正常业务结束,返回 true 。
    return true;
    }
    // 某个子任务执行失败
    if (!taskRes) {
    // 上述同理。但是算业务异常,返回 false
    return false;
    }
    // 只有当最后一个子任务执行正常执行完成。才会返回 true 。如果中间过程某个子任务返回失败,递归中断,并且
    // 返回 false
    return taskRes && this.startIndex == TaskProcessList.size() - 1;
    }
    burymme11
        14
    burymme11  
       274 天前
    缺了一个全局变量。要补上。
    private Integer startIndex = 0;
    -----------------------------------------
    不对,等下。你说的任意时刻停止任务,是 shutDown 还是 shutDownNow ?
    NoSuchException
        15
    NoSuchException  
       274 天前
    OP 的意思是要等整个调用链完整的执行完之后在停止任务吧。在任务链开始的时候往 Redis 里面丢一个标识进去,比如自定义一个任务 Id 之类的,整个调用链完成后删除这个任务 Id 标识。

    停止任务的话可以另起一个接口,查看 Reids 中是否还存在任务,存在任务就暂时等待,不存在就给停止了。这个接口你可以用个 while 循环+Threed sleep 来做。
    kamalei
        16
    kamalei  
       274 天前
    不同语言编写的后端程序实现方式的 idioms 不一样。
    要不要考虑进程会强杀的情况?
    任务是分布式运行的吗?
    更多细节问题确认后就好搞了,我觉得其实不用 redis 这么麻烦。
    关于   ·   帮助文档   ·   博客   ·   API   ·   FAQ   ·   我们的愿景   ·   实用小工具   ·   3473 人在线   最高记录 6543   ·     Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 · 25ms · UTC 00:52 · PVG 08:52 · LAX 17:52 · JFK 20:52
    Developed with CodeLauncher
    ♥ Do have faith in what you're doing.