【问题标题】:How can we stop backend process?我们如何停止后端进程?
【发布时间】:2019-08-22 09:55:48
【问题描述】:

在我的 Spring Boot 应用程序中,我有以下 REST 服务

@GetMapping("/testThread")
public Map<String, Object> testThread(HttpServletRequest request){

    for (int i=0;i<100000;i++){
        System.out.println(i);
    }
    return null;
}

在这个网络服务中,我从 POSTMAN 工具调用,并且在这个网络服务循环中运行了 100 000 次。

我的要求是在从邮递员调用网络服务之后,我可以使用 Cancel Request 按钮取消该请求,然后在后端 循环也应该停止。

到目前为止,循环正在完成执行。

我们有什么选择吗?

【问题讨论】:

  • 您确实可以选择这样做。你试过什么? (另外,简单地说你抛出 InterruptedException 不会让它发生。你需要在循环中添加 if (Thread.currentThread().isInterrupted()) { throw new InterruptedException(); } 或类似的东西。)
  • @user2478398 我已经更新了有问题的代码,删除了未使用的 throws 语句,你能建议一些好的链接来满足这个要求吗?

标签: java multithreading spring-boot


【解决方案1】:

你有两个原型选项,第一个是请求线程以某种方式取消请求。当您在返回响应之前进行工作时,理论上您可以在每次迭代时将一些数据发送到管道中,例如,一个空格字符,然后刷新输出写入器。如果调用者关闭了管道,您将收到一个 IOException 并终止循环。不过这很hacky。

另一种方法是为工作线程返回某种标识符,以便可以发送取消命令来标识应该取消哪个线程。为此,您需要将工作线程推入新线程并立即返回包含工作标识符的响应。这意味着即使工作线程仍在工作,初始客户端调用也会立即得到响应。

请注意,以下示例仅适用于单个服务器或粘性负载平衡环境。当然,您可以将状态图扩展到数据库表以实现更复杂的架构。

您可能还想添加更多端点以获取当前状态或结果。

private enum State {
   Running,
   Cancel
}

private final Map<String, State> state = Collections.synchronizedMap(new HashMap<>());
private final ExecutorService executor = Executors.newCachedThreadPool();

@GetMapping("/cancelThread/{id}")
public Map<String, Object> testThread(@PathVariable String id) {
    state.computeIfPresent(id, (k, v) -> State.Cancel);
}

@GetMapping("/testThread")
public Map<String, Object> testThread(HttpServletRequest request) {
    String id = java.util.UUID.randomUUID().toString()
    state.put(id, State.Running);

    executor.submit(() -> doWork(id));

    Map<String, Object> response = new HashMap<>();
    response.put("transactionId", id);
    return response;
}

private void doWork(String id) {
    executor.submit(() -); 

    try {
        for (int i = 0; i < 100000; i++) {
          if (state.get(id) == State.Cancel) {
              break;
          }

          System.out.println(i);
        }
    } finally {
        state.remove(id);
    }
}

【讨论】:

  • 感谢您的回复.. 我需要一个建议,而不是状态图,如果我使用数据库表,所以性能方面不会像每次循环迭代一样好,我正在做一个 Db 查询来读取线程状态正确吗?,所以我认为我应该使用 map 而不是 db,这样我就可以将 state map 放在 servlet 上下文中,每当我需要访问 map 时,我都会从 servlet 上下文中获取它,并且在再次更改状态后会将 map 放在 servlet 上下文中。会好吗?
  • 加入某种集中式服务的要点是,如果您处于负载平衡的环境中,取消请求可能不一定会命中工作人员正在运行的同一服务。或者,您可以查看使用消息队列。但是,如果您只运行一个服务器,那么在同一个控制器中使用单个 Map 就可以了。
  • 在你回答之前,我正在寻找我在下面找到的解决方案.. 请看一下.. 在 testThread 方法中,我正在生成线程 id Thread.currentThread().getId() 我正在返回的 id resp 并且我在这里创建了另一个 rest killThread 我正在传递 threadId 并且在这个方法中我正在杀死那个特定的线程 Set setOfThread = Thread.getAllStackTraces().keySet(); for (Thread thread : setOfThread) { if (thread.getId()==threadId) { thread.stop(); } } 你能建议它好吗?
  • 依赖线程id的问题是你不知道你在杀什么。例如,如果我调用带有线程 id 的 killThread,但带有 id 的线程不再处理原始工作负载,而是因为线程本身或 id 已被重用而做一些完全不同的事情,会发生什么?
  • 我已经接受了这个答案.. 还有一个问题.. 如果假设我的 doWork 实现中没有任何循环,它将有数千行代码,那么这个解决方案将如何工作或任何其他选项?
猜你喜欢
  • 1970-01-01
  • 2014-12-23
  • 2015-04-29
  • 2010-09-22
  • 1970-01-01
  • 1970-01-01
  • 2011-02-08
  • 2018-08-10
相关资源
最近更新 更多