Warning: file_get_contents(/data/phpspider/zhask/data//catemap/2/csharp/314.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Java Springboot:如何使用WebClient而不是RestTemplate来执行非阻塞和异步调用_Java_Spring_Spring Boot_Spring Webflux - Fatal编程技术网

Java Springboot:如何使用WebClient而不是RestTemplate来执行非阻塞和异步调用

Java Springboot:如何使用WebClient而不是RestTemplate来执行非阻塞和异步调用,java,spring,spring-boot,spring-webflux,Java,Spring,Spring Boot,Spring Webflux,我有一个springboot项目,它使用springboot Resttemplate。我们已经从1.5.3迁移到springboot 2.0.1,我们正在努力 其余的调用通过使用WebClient进行异步调用。我们使用Resttemplate处理收到的字符串,如下所示。但WebClient只返回 Mono或Flux格式的数据。如何将数据获取为字符串。已尝试block()方法,但它执行异步调用 @Retryable(maxAttempts=4,value=java.net.ConnectExce

我有一个springboot项目,它使用springboot Resttemplate。我们已经从1.5.3迁移到springboot 2.0.1,我们正在努力 其余的调用通过使用WebClient进行异步调用。我们使用Resttemplate处理收到的字符串,如下所示。但WebClient只返回 Mono或Flux格式的数据。如何将数据获取为字符串。已尝试block()方法,但它执行异步调用

@Retryable(maxAttempts=4,value=java.net.ConnectException.class,
退避=@backoff(延迟=3000,乘数=2))
公共Mono getResponse(字符串url){
返回webClient.get().uri(urlForCurrent.accept)(应用程序\u JSON)
.retrieve()
.bodyToMono(String.class);
}
使用RestTemplate显示数据流

  • 控制器接收客户端呼叫
  • 提供程序以字符串格式获取数据
  • 提供者处理字符串
  • 数据被提供给控制器
  • Controller.java

    @RequestMapping(value=traffic/,method=RequestMethod.GET,
    products=MediaType.APPLICATION\u JSON\u值)
    公共字符串getTraffic(@RequestParam(“place”)字符串位置)引发InterruptedException,ExecutionException{
    String trafficJSON=Provider.getTrafficJSON(位置)
    返回流量;
    }
    
    Provider.java

    public String getTrafficJSON(String location) {
        String url = ----;
    
        ResponseEntity<String> response = dataFetcher.getResponse(url);
    
        /// RESPONSEBODY IS READ AS STRING AND IT NEEDS TO BE PROCESSED
        if (null != response {
            return parser.transformJSON(response.getBody(), params);
        }
    
        return null;
    }
    
    公共字符串getTrafficJSON(字符串位置){
    字符串url=---;
    ResponseEntity response=dataFetcher.getResponse(url);
    ///RESPONSEBODY被读取为字符串,需要对其进行处理
    if(null!=响应{
    返回parser.transformJSON(response.getBody(),params);
    }
    返回null;
    }
    
    DataFetcher.java

    @Retryable(最大尝试次数=4,
    value=java.net.ConnectException.class,
    退避=@backoff(延迟=3000,乘数=2))
    公共响应属性getResponse(字符串url){
    /* ----------------------- */
    返回getRestTemplate().getForEntity(urlForCurrent,String.class);
    }
    
    第一步是使用baseUrl构建
    WebClient
    对象

    WebClient webClient = WebClient.builder()
        .baseUrl("http://localhost:8080/api") //baseUrl
        .defaultHeader(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_JSON_VALUE)
        .build();
    
    然后选择方法并将路径与请求变量或正文负载一起追加

    ResponseSpec responseSpec = webClient
        .get()
        .uri(uriBuilder -> uriBuilder.path("/findById") //additional path
            .queryParam("id", id).build())
        .retrieve()
        .onStatus(HttpStatus::is4xxClientError, response -> Mono.error(new CustomRuntimeException("Error")));
    
    使用
    bodyToMono
    block()
    函数等待响应。如果希望响应为字符串,可以使用google的gson库进行转换

    Object response = responseSpec.bodyToMono(Object.class).block();
    Gson gson = new Gson();
    String str = gson.toJson(response);
    
    如果您不想知道api调用的状态,可以执行以下操作

    webClient
        .post()
        .uri(uri -> uri.path("/save").build())
        .body( BodyInserters.fromObject(payload) )
        .exchange().subscribe();
    

    首先要了解的是,如果您需要调用
    .block()
    ,您最好还是坚持使用
    restemplate
    ,使用WebClient将一无所获

    如果你想从使用WebClient中获益,你需要从被动的角度来思考。被动过程实际上只是一系列步骤,每个步骤的输入都是之前步骤的输出。当请求进来时,你的代码会创建一系列步骤并立即返回,释放http线程。然后框架使用当上一步的输入可用时,执行每个步骤的工作线程池

    这样做的好处是,在接受竞争请求的能力上获得了巨大的提升,而重新思考代码编写方式的成本很小。您的应用程序只需要一个非常小的http线程池和另一个非常小的工作线程池

    当您的控制器方法返回一个
    Mono
    Flux
    时,您已将其设置正确,无需调用
    block()

    最简单的形式是这样的:

    @GetMapping(value = "endpoint", produces = MediaType.TEXT_PLAIN_VALUE)
    @ResponseBody
    @ResponseStatus(OK)
    public Mono<String> controllerMethod() {
    
        final UriComponentsBuilder builder =
                UriComponentsBuilder.fromHttpUrl("http://base.url/" + "endpoint")
                        .queryParam("param1", "value");
    
        return webClient
                .get()
                .uri(builder.build().encode().toUri())
                .accept(APPLICATION_JSON_UTF8)
                .retrieve()
                .bodyToMono(String.class)
                .retry(4)
                .doOnError(e -> LOG.error("Boom!", e))
                .map(s -> {
    
                    // This is your transformation step. 
                    // Map is synchronous so will run in the thread that processed the response. 
                    // Alternatively use flatMap (asynchronous) if the step will be long running. 
                    // For example, if it needs to make a call out to the database to do the transformation.
    
                    return s.toLowerCase();
                });
    }
    
    @GetMapping(value=“endpoint”,products=MediaType.TEXT\u PLAIN\u value)
    @应答器
    @响应状态(OK)
    公共单控制器方法(){
    最终组件生成器生成器=
    UriComponentsBuilder.fromHttpUrl(“http://base.url/“+”端点“)
    .queryParam(“参数1”、“值”);
    返回网络客户端
    .get()
    .uri(builder.build().encode().tori())
    .accept(应用程序\u JSON\u UTF8)
    .retrieve()
    .bodyToMono(String.class)
    .重试(4)
    .doon错误(e->LOG.error(“Boom!”,e))
    .map(s->{
    //这是您的转换步骤。
    //映射是同步的,因此将在处理响应的线程中运行。
    //如果该步骤需要长时间运行,也可以使用flatMap(异步)。
    //例如,如果需要调用数据库来进行转换。
    返回s.toLowerCase();
    });
    }
    
    转向反应式思考是一个相当大的范式转变,但值得付出努力。坚持下去,只要你能在整个应用程序中完全没有阻塞代码,这其实并不难。构建步骤并返回它们。然后让框架管理步骤的执行

    如果有任何不清楚的地方,我们乐意提供更多的指导


    记住要玩得开心:)

    因为有很多误解,所以这里我要澄清一些事情

    Spring已经正式声明,他们将在将来不推荐使用
    RestTemplate
    ,因此如果可以的话,如果您希望尽可能成为未来的证明,请使用
    WebClient

    如合同中所述

    注意:从5.0开始,非阻塞、反应式的
    org.springframework.web.reactive.client.WebClient
    提供了一种现代的
    restemplate
    替代方案,可有效支持同步和异步以及流式场景。
    RestTemplate
    在将来的版本中将被弃用,并且今后不会添加主要的新功能。有关更多详细信息和示例代码,请参阅Spring框架参考文档的
    WebClient
    部分

    非反应性应用
    @Retryable(maxAttempts = 4,
           value = java.net.ConnectException.class,
           backoff = @Backoff(delay = 3000, multiplier = 2))
    public ResponseEntity<String> getResponse(String url) {
        return webClient.get()
                .uri(url)
                .exchange()
                .flatMap(response -> response.toEntity(String.class))
                .block();
    }