如何使用反应性单声道环绕一系列非阻塞操作?

时间:2019-12-28 04:58:01

标签: spring spring-boot mono reactive-programming reactor

我有一组非阻塞操作,它们可以启动一个操作,检查它是否完成,并获得结果(如果完成)。问题是getResult@GetMapping("/some-mapping") public Mono<String> someops(@PathVariable String someParam) { int jobId = nonBlockingOps.start(someParam); // 1. start the op return Mono.defer( () -> { // wait for op to complete while (!nonBlockingOps.isDone(jobId)) { /// 2. check if the op finished try { Thread.sleep(1000); // This will block } catch (InterruptedException e) { return Mono.error(e); } } return Mono.just(nonBlockingOps.getResult(jobId)); // 3. return the result } ); } 不会立即一起出现,但是我需要返回结果而不会阻塞。下面的示例代码演示了我正在尝试做的事情。

defer

因此,即使单声道是Mono版本,订阅时它也可能会阻塞。

1。那么我该如何用@PostMapping("/job") public Mono<Integer> opsStart(@RequestParam(value="param") String param) { return Mono.just(nonBlockingOps.start(param)); } @GetMapping("/job/isDone/{id}") public Mono<Boolean> opsCheck(@PathVariable Integer id) { return Mono.just(nonBlockingOps.isDone(id)); } @GetMapping("/job/getResult/{id}") public Mono<String> opsGet(@PathVariable Integer id) { return Mono.just(nonBlockingOps.getResult(id)); } 包裹起来,以便Mono处理等待的部分?

或者(或另外),您可以考虑以下三种服务:

WebClient

2a。那么如何通过调用这些函数来达到相同的结果?

2b。那么,如何通过public function edit() { $data = selfcontect::find(1); return view('/admin/selfcontectedit',compact('data')); } 调用这些服务来达到相同的结果呢?

2 个答案:

答案 0 :(得分:1)

  1. 如果作业准备就绪,您可以使用Flux.interval定期进行破解:
@GetMapping("/some-mapping")
public Mono<String> someops(@RequestParam(value="param") String someParam) {
    return Mono.just(nonBlockingOps.start(someParam))
            .flatMapMany(jobId -> Flux.interval(Duration.ZERO, Duration.ofMillis(1000)).map(__ -> jobId))
            .filter(nonBlockingOps::isDone)
            .next()
            .map(nonBlockingOps::getResult);
}
  1. 使用WebClient调用三个服务的过程类似:
public class ApiClient {

    @Data
    @AllArgsConstructor
    private static class Status {
        private int jobId;
        private boolean done;
    }

    private final WebClient client;

    public ApiClient(String baseUrl) {
        client = WebClient.create(baseUrl);
    }

    public Mono<String> getJob(String param) {

        return submitJob(param)
                .flatMapMany(id -> Flux.interval(Duration.ZERO, Duration.ofMillis(1000)).map(__ -> id))
                .flatMap(this::getStatus)
                .filter(Status::isDone)
                .next()
                .flatMap(status -> getResult(status.getJobId(), String.class));
    }

    private Mono<Integer> submitJob(String param) {
        return client.post()
                .uri(builder -> builder.path("/job").queryParam("param", param).build())
                .retrieve().bodyToMono(Integer.class);
    }

    private Mono<Status> getStatus(int jobId) {
        return client.get()
                .uri(builder -> builder.path("/job/isDone/{id}").build(jobId))
                .retrieve().bodyToMono(Boolean.class)
                .map(status -> new Status(jobId, status));
    }

    private <T> Mono<T> getResult(int jobId, Class<T> clazz) {
        return client.get()
                .uri(builder -> builder.path("/job/getResult/{id}").build(jobId))
                .retrieve()
                .bodyToMono(clazz);
    }
}

说实话,我每秒都无法查看工作状态。我可能至少会返回作业进度并实现某种推送通知(使用WebSocket或服务器发送的事件)。

上面的代码未经测试,但显示了总体思路。可能有些事情需要调整(请求标头等)。

答案 1 :(得分:0)

假设作业不是长时间运行(,即最多需要几秒钟完成)恕我直言,可以通过使用DeferredResult来获得最终结果。

DeferredResult将帮助异步触发作业,等待其完成并在可用时返回结果。此处的附加优势是可以指定作业完成timeout以及相应的结果!

此外,您可以利用其他生命周期回调。 onCompletiononError等来处理各种情况。

典型的控制器如下所示

@GetMapping("/some-mapping")
public DeferredResult<String> someops(@RequestParam(value = "param") String someParam) {

    DeferredResult<String> output = new DeferredResult<>(); // optionally supply the timeout value

    CompletableFuture.supplyAsync(() -> {
        int jobId = nonBlockingOps.start(someParam); // 1. start the op
        // wait for op to complete
        while (!nonBlockingOps.isDone(jobId)) { /// 2. check if the op finished
            try {
                Thread.sleep(1000); // This will block
            } catch (InterruptedException e) {
                // do something
            }
        }
        return nonBlockingOps.getResult(jobId);
    }).whenCompleteAsync((result, throwable) -> output.setResult(result));

    return output;
}

如果需要更多信息,请在评论中告知。

P.S。:如Mafor在answer中所述,请考虑返回作业进度并实施某种推送通知(使用WebSocket或服务器发送的事件)。