首页 > 解决方案 > 在没有冻结浏览器作为客户端的情况下,在 Spring 中使用 deferredResult 在后台运行任务

问题描述

我已经实现了一个简单Rest service的,我想从中deferredResult测试Spring。虽然我按该顺序收到文本:

  1. 测试
  2. 测试 1
  3. 延迟结果后的测试

我很感兴趣为什么在浏览器(客户端)中我需要等待 8 秒。那不deferedResult应该是非阻塞的并在后台运行任务吗?如果不是,如何创建一个非阻塞的休息服务并在后台运行任务而不使用Java 9和反应流?

@RestController("/")
public class Controller {

    @GetMapping
    public DeferredResult<Person> test() {
        System.out.println("TEST");
        DeferredResult<Person> result = new DeferredResult<>();
        CompletableFuture.supplyAsync(this::test1)
                .whenCompleteAsync((res, throwable) -> {
                    System.out.println("TEST AFTER DEFERRED RESULT");
                    result.setResult(res);
                });
        System.out.println("TEST 1");
        return result;
    }

    private Person test1() {
        try {
            Thread.sleep(8000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        return new Person("michal", 20);
    }
}

class Person implements Serializable {
    private String name;
    private int age;
}

标签: springmultithreadingblockingnonblockingdeferred

解决方案


DeferredResult是 a 的持有者,WebRequest允许服务线程释放并服务另一个传入的 HTTP 请求,而不是等待当前的结果。之后setResultorsetError方法将被调用 - Spring 将释放存储WebRequest的内容,您的客户端将收到响应。

DeferredResultholder 是用于非阻塞 IO 线程的 Spring Framework 抽象。延迟结果抽象与后台任务无关。在没有线程抽象的情况下调用它会导致预期的相同线程执行。您的test1方法正在后台运行,因为CompletableFuture.supplyAsync方法调用将执行提供给公共池。

结果在 8 秒内返回,因为whenCompleteAsync传递的回调只有在test1方法返回后才会被调用。

尽管您在后台执行“服务调用逻辑”需要 8 秒,但您无法立即收到结果。如果您想释放 HTTP 请求 - 只需返回一个可用的适当对象(它可能包含一个 UUID,例如,以便稍后获取创建的人员)或从控制器方法中返回任何内容。您可以在N秒后尝试GET创建的用户。有特定的 HTTP 响应代码(202 ACCEPTED),这意味着服务器端正在处理请求。最后只需获取您创建的对象。

第二种方法(如果您应该通知您的客户端 - 但如果这是唯一的原因,我不会建议您这样做) - 您可以使用 WebSockets 通知客户端并使用它发送消息。


推荐阅读