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

Spring 如何使用反应式单声道环绕一系列非阻塞操作?,spring,spring-boot,reactive-programming,reactor,Spring,Spring Boot,Reactive Programming,Reactor,我有一组非阻塞操作,它们可以启动一个操作,检查它是否完成,如果完成了就得到结果。问题是start和getResult不会立即同时发生,但我需要返回结果而不阻塞。下面的示例代码演示了我要做的事情 @GetMapping(“/some mapping”) 公共Mono someops(@RequestParam(value=“param”)字符串someParam){ int jobId=nonBlockingOps.start(someParam);//1.启动操作 返回Mono.defer(()

我有一组非阻塞操作,它们可以启动一个操作,检查它是否完成,如果完成了就得到结果。问题是
start
getResult
不会立即同时发生,但我需要返回结果而不阻塞。下面的示例代码演示了我要做的事情

@GetMapping(“/some mapping”)
公共Mono someops(@RequestParam(value=“param”)字符串someParam){
int jobId=nonBlockingOps.start(someParam);//1.启动操作
返回Mono.defer(()->{
//等待op完成
而(!nonBlockingOps.isDone(jobId)){///2.检查操作是否完成
试一试{
Thread.sleep(1000);//这将阻止
}捕捉(中断异常e){
返回单声道错误(e);
}
}
返回Mono.just(nonBlockingOps.getResult(jobId));//3.返回结果
} );
}
因此,即使mono是
延迟的
-ed,它也可能在订阅发生时阻塞

1。那么,我如何用
Mono
来包装这一点,以便Mono处理等待的部分呢?

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

@PostMapping(“/job”)
公共Mono opsStart(@RequestParam(value=“param”)字符串param)
{返回Mono.just(nonBlockingOps.start(param));}
@GetMapping(“/job/isDone/{id}”)
公共Mono opsCheck(@PathVariable整数id)
{返回Mono.just(nonBlockingOps.isDone(id));}
@GetMapping(“/job/getResult/{id}”)
公共Mono opsGet(@PathVariable整数id)
{返回Mono.just(nonBlockingOps.getResult(id));}
2a。那么,如何通过调用这些函数来实现相同的结果呢?

2b。那么,如何通过
WebClient
调用这些服务来实现相同的结果呢?

  • 您可以使用
    Flux.interval
    定期检查作业是否准备就绪:
  • @GetMapping(“/some mapping”)
    公共Mono someops(@RequestParam(value=“param”)字符串someParam){
    返回Mono.just(非阻塞ops.start(someParam))
    .flatMapMany(jobId->Flux.interval(Duration.ZERO,Duration.ofMillis(1000)).map(_u->jobId))
    .filter(非阻塞端口::isDone)
    .next()
    .map(非blockingops::getResult);
    }
    
  • 使用WebClient调用三个服务看起来很相似:
  • 公共类ApiClient{
    @资料
    @AllArgsConstructor
    私有静态类状态{
    私家侦探;
    私有布尔完成;
    }
    私人最终网络客户;
    公共ApiClient(字符串baseUrl){
    client=WebClient.create(baseUrl);
    }
    公共Mono getJob(字符串参数){
    返回提交作业(参数)
    .flatMapMany(id->Flux.interval(Duration.ZERO,Duration.ofMillis(1000)).map(_u->id))
    .flatMap(此::getStatus)
    .filter(状态::isDone)
    .next()
    .flatMap(status->getResult(status.getJobId(),String.class));
    }
    私有Mono submitJob(字符串参数){
    返回client.post()
    .uri(builder->builder.path(“/job”).queryParam(“param”,param.build())
    .retrieve().BodyToNo(Integer.class);
    }
    私有状态(int jobId){
    返回client.get()
    .uri(builder->builder.path(“/job/isDone/{id}”).build(jobId))
    .retrieve().BodyToNo(Boolean.class)
    .map(状态->新状态(作业ID,状态));
    }
    私有Mono getResult(int jobId,Class clazz){
    返回client.get()
    .uri(builder->builder.path(“/job/getResult/{id}”).build(jobId))
    .retrieve()
    .bodyToMono(clazz);
    }
    }
    
    老实说,我看不出每秒都在调查工作状态有多大意义。我可能至少会返回作业进度并实现某种推送通知(使用WebSocket或服务器发送事件)


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

    假设作业运行时间不长(即最多需要几秒钟才能完成),那么可以使用它来实现最终结果

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

    此外,您还可以利用其他生命周期回调,即<代码>完成,
    错误
    等,以处理各种情况

    典型的控制器如下所示

    @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;
    }
    
    @GetMapping(“/some mapping”)
    公共延迟结果someops(@RequestParam(value=“param”)字符串someParam){
    DeferredResult输出=新的DeferredResult();//可选地提供超时值
    CompletableFuture.SupplySync(()->{
    int jobId=nonBlockingOps.start(someParam);//1.启动操作
    //等待op完成
    而(!nonBlockingOps.isDone(jobId)){///2.检查操作是否完成
    试一试{
    Thread.sleep(1000);//这将阻止
    }捕捉(中断异常e){
    //做点什么
    }
    }
    返回nonBlockingOps.getResult(jobId);
    }).whenCompleteTasync((结果,可丢弃)->output.setResult(结果));
    返回输出;
    }
    
    如果需要更多信息,请在评论中告知

    <强> P.S.:如MAOF所指出的那样,考虑返回作业进度并执行某种推送通知(使用WebSoCor或服务器发送事件)。我寻找的是一个规范的解决方案,用于解决三个非阻塞调用到一个调用中而不阻塞任何线程的一般问题陈述。你的

    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);
        }
    }
    
    @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;
    }