因为Spring翻车被鄙视?美团大佬公开分享解决方法,网友:我服了

2021-05-30 11:04

阅读:1169

标签:出现   没有   内存   etc   spring   tcl   ams   准备就绪   trie   

今日分享开始啦,请大家多多指教~

RestTemplate 采用 Java Servlet API,因而是阻塞式同步调用。相反,WebClient 是异步的,等待响应的同时不会阻塞正在执行的线程。只有在响应结果准备就绪时,才会发起通知。

RestTemplate 仍然有用武之地。非阻塞模式在某些场景下比阻塞方法占用系统资源要少得多,这时 WebClient 是一种更好的选择。

技术图片

一、WebClient

非阻塞式客户端。WebClient 使用 Spring Reactive Framework 所提供的异步非阻塞解决方案。

当 RestTemplate 为每个事件(HTTP 请求)创建一个新的线程时,WebClient 将为每个事件创建类似于“任务”。幕后,Reactive 框架将对这些 “任务” 进行排队,并仅在适当的响应可用时执行它们。

Reactive 框架使用事件驱动的体系结构。它提供了通过 Reactive Streams API 组合异步逻辑的方法。因此,与同步/阻塞方法相比,Reactive 可以使用更少的线程和系统资源来处理更多的逻辑。WebClient 是 Spring WebFlux 库的一部分。因此,我们还可以使用流畅的函数式 API 编写客户端代码,并将响应类型(Mono 和 Flux)作为声明来进行组合。

1.依赖

 
        org.springframework.boot
            spring-boot-starter-webflux
        

2.创建

    @Test
    void webclient01Create(){
        WebClient webClient = WebClient.create();
        Mono mono = webClient
                .get() //get请求
                .uri(BASE_URI + "/test/get01")
                .retrieve() //获取响应体
                .bodyToMono(String.class); //格式化响应数据
        log.info("{}",mono.block());
    }
    @Test
    void webclient02Create(){
        WebClient webClient = WebClient.create(BASE_URI);
        Mono mono = webClient
                .get()
                .uri("/test/get01")// uri 会自动拼接
                .retrieve()
                .bodyToMono(String.class);
        log.info("{}",mono.block());
    }
    @Test
    void webclient03Builder(){
        WebClient webClient = WebClient.builder()
                .baseUrl(BASE_URI)
                .build();
        Mono mono = webClient
                .get()
                .uri("/test/get01")
                .retrieve()
                .bodyToMono(String.class);
        log.info("{}",mono.block());
    }

3.使用

    @Test
    void webclient04(){
        WebClient webClient = WebClient.builder()
                .baseUrl(BASE_URI)
                .build();
        Mono mono = webClient
                .get()
                .uri(uriBuilder -> uriBuilder
                    .path("/test/get02")
                    .queryParam("pageSize",10)
                    .queryParam("pageNum",1)
                    .build())
                .retrieve()
                .bodyToMono(String.class);
        log.info("{}",mono.block());
    }
    @Test
    void webclient05(){
        WebClient webClient = WebClient.builder()
                .baseUrl(BASE_URI)
                .build();
        User user = User.builder().id("0000").password("0000").build();
        Mono userMono = webClient
                .post()
                .uri(uriBuilder -> uriBuilder.path("/test/post01").build())
                .contentType(MediaType.APPLICATION_JSON)
                .body(Mono.just(user),User.class)
//                .header()
                .retrieve()
                .bodyToMono(User.class);
        log.info("{}",userMono.block());
    }

二、RestTemplate

阻塞式客户端。RestTemplate 使用了基于每个请求对应一个线程模型(thread-per-request)的 Java Servlet API。这意味着,直到 Web 客户端收到响应之前,线程都将一直被阻塞下去。而阻塞代码带来的问题则是,每个线程都消耗了一定的内存和 CPU 周期。这些线程将耗尽线程池或占用所有可用内存。由于频繁的 CPU 上下文(线程)切换,我们还会遇到性能下降的问题。

1.小技巧

  • 方法名的第一部分表示HTTP请求类型,方法名的第二部分表示响应类型。例如:getForObject 表示执行GET请求并将响应转化成一个Object类型的对象。
  • 利用RestTemplate封装客户端发送HTTP请求时,如果出现异常就会抛出 RestClientException 类型的异常;可以通过在创建RestTemplate对象的时候指定一个ResponseErrorHandler类型的异常处理类来处理这个异常
  • exchange 和 excute 这两个方法是通用的HTTP请求方法,而且这两个方法还支持额外的HTTP请求类型
  • RestTemplate默认使用JDK提供的包去建立HTTP连接,当然,开发者也可以使用诸如 Apache HttpComponents, Netty, and OkHttp 去建立HTTP连
  • RestTemplate内部默认使用HttpMessageConverter来实现HTTTP messages 和 POJO 之间的转换,可以通过RestTemplate的成员方法 tMessageConverters(java.util.List>). 去修改默认的转换器
  • RestTemplate内部默认使用SimpleClientHttpRequestFactory创建HTTP连接可以通过HttpAccessor.setRequestFactory(org.springframework.http.client.ClientHttpRequestFactory)去做相应的修改
  • 每种方法都有3个重载方法,其中两个接收String类型的请求路径和响应类型、参数;另外一个接收URI类型的请求路径和响应类型
  • 使用String类型的请求路径时,RestTemplate会自动进行一次编码,所以为了避免重复编码问题最好使用URI类型的请求路径
  • getForObject 和 getForEntity 的区别:后者可以获取到更多的响应信息,前者这可以获取到响应体的数据

2.示例

使用@Configuration 提前注入的 RestTemplate

技术图片

2.1GET

  • 不带请求参数的GET请求

服务端代码

技术图片

客户端测试代码

技术图片
  • 带请求参数的GET请求

服务端代码

    /*
     * 带请求参数的get请求
     * @param pageSize
     * @param pageNum
     * @return String
     */
    @GetMapping("/get02")
    public String get02(@RequestParam Integer pageSize, @RequestParam Integer pageNum) {
        return "get02() ok,请求参数为:" + pageSize + "/" + pageNum;
    }

客户端测试代码

技术图片
  • 带路径参数的GET请求

服务端代码

    /*
     * 带路径参数的get请求
     * @param id
     * @return String
     */
    @GetMapping("/get03/{id}")
    public String get03(@PathVariable("id") String id) {
        return "get03() ok,路径参数为:" + id;
    }

客户端测试代码

技术图片
  • 带有请求参数和路径参数的GET请求

服务端代码

    /*
     * 带有请求参数和路径参数的get请求
     * @param score
     * @param id
     * @return String
     */
    @GetMapping("/get04/{id}")
    public String get04(@RequestParam String score, @PathVariable("id") String id) {
        return "get04() ok,请求参数为:" + score + ",路径参数为:" + id;
    }

客户端测试代码

技术图片
  • 带请求参数的GET请求,Map传递

服务端代码

    /*
     * 带请求参数的get请求
     * @param pageSize
     * @param pageNum
     * @return String
     */
    @GetMapping("/get02")
    public String get02(@RequestParam Integer pageSize, @RequestParam Integer pageNum) {
        return "get02() ok,请求参数为:" + pageSize + "/" + pageNum;
    }

客户端测试代码

    @Test
    void get05(){
        Map params = new HashMap();
        params.put("pageSize",10);
        params.put("pageNum",1);
        String uri = BASE_URI +"/test/get02?pageSize={pageSize}&pageNum={pageNum}";
        String result = restTemplate.getForObject(uri,String.class,params);
        log.info("{}",result);
    }
  • 不带请求参数的GET请求返回ResponseEntity

可以获取到完整的响应信息

服务端代码

    /*
     *  不带参数的get请求
     * @return String
     */
    @GetMapping("/get01")
    public String get01() {
        return "get01() ok";
    }

客户端测试代码

    @Test
    void get06(){
        String uri = BASE_URI + "/test/get01";
        ResponseEntity result = restTemplate.getForEntity(uri,String.class);
        log.info("{}",result);
    }
  • 携带请求参数的GET请求,返回List

服务器端代码

技术图片

客户端测试代码

    @Test
    void get07(){
        String uri = BASE_URI + "/test/get05?pageSize={1}&pageNum={2}";
        ResponseEntity result = restTemplate.getForEntity(uri,List.class,10,1);
        log.info("{}", result);
    }
  • 自定义请求头的GET请求

服务端代码

技术图片

客户端测试代码

@Test
    void get08(){
        String uri = BASE_URI + "/test/get05?pageSize={1}&pageNum={2}";
        HttpHeaders httpHeaders = new HttpHeaders();
        httpHeaders.add("token", UUID.randomUUID().toString());
        ResponseEntity result = restTemplate.exchange(
                uri,
                HttpMethod.GET,
                new HttpEntity(httpHeaders),
                List.class,
                10,1
        );
        log.info("{}",result);
    }

2.2 POST

  • 没有请求参数只有请求体的POST请求

服务端代码

    /*
     * 只有请求体的POST请求
     * @param user
     */
    @PostMapping("/post01")
    public User post01(@RequestBody User user){
        log.info("请求体:{}",user.toString());
        return user;
    }

客户端测试代码

技术图片
  • 含有请求体、请求参数、路径参数的POST请求

服务端代码

    /*
     * 带有请求体参数、请求参数、路径参数的POST请求
     * @param user
     * @param status
     * @param type
     * @return String
     */
    @PostMapping("/post02/{type}")
    public String post02(@RequestBody User user,
                         @RequestParam("status") Integer status,
                         @PathVariable("type") String type) {
        return "requestBody:" + user.toString() + ",requestParam:" + status + ",
      pathVariable:" + type + "";
    }

客户端测试代码

    @Test
    void post02() {
        String uri = BASE_URI + "/test/post02/++++/?status={1}";
        String result = restTemplate.postForObject(
                uri,
                User.builder().id("***").password("....").build(),
                String.class,
                0
        );
        log.info("{}",result);
    }
  • 使用HttpEntity自定义请求体和请求头发起POST请求

服务端代码

    /*
     * 带有请求体参数、请求参数、路径参数的POST请求
     * @param user
     * @param status
     * @param type
     * @return String
     */
    @PostMapping("/post02/{type}")
    public String post02(@RequestBody User user,
                         @RequestParam("status") Integer status,
                         @PathVariable("type") String type) {
        return "requestBody:" + user.toString() + ",requestParam:" + status + ",
      pathVariable:" + type + "";
    }

客户端测试代码

    @Test
    void post03(){
        String uri =   BASE_URI + "/test/post02/788787/?status={1}";
        User user = User.builder().id("6666").password("7777").build();
        HttpHeaders httpHeaders = new HttpHeaders();
        httpHeaders.setContentType(MediaType.APPLICATION_JSON);
        HttpEntity httpEntity = new HttpEntity(user,httpHeaders);
        String result = restTemplate.postForObject(uri,httpEntity,String.class,0);
        log.info("{}",result);
    }

小结

在Web应用中我们需要对其他服务进行HTTP调用。WebClient和RestTemplate是两个由Spring提供的客户端工具。

今日份分享已结束,请大家多多包涵和指点!

 

因为Spring翻车被鄙视?美团大佬公开分享解决方法,网友:我服了

标签:出现   没有   内存   etc   spring   tcl   ams   准备就绪   trie   

原文地址:https://www.cnblogs.com/pfx1995/p/14750762.html


评论


亲,登录后才可以留言!