• SpringCloud开发之OpenFeign timeout和压缩等问题


    在某些时候,我们希望某个同步调用执行更长的时间(异步暂时不考虑),这个时候,首先就是要设置OpenFeign的timeout设定。

    下面我们举例来说明,可以如何设定TimeOut参数。

    一、环境

      脱离环境说明问题就是流氓。

      cloud的版本为 2021.0.0

      spring-boot-starter-parent 本本是2.6.2

      通过官网和下载的jar包可以看到,OpenFeign有关的版本是3.1.0。

      于是我们取看下官网的文档。

      

    二、概述

       1.通过官网的timeout章节,我们可以看到设置timeout至少有两种方式:通过属性文件(properties/yml)和配置

        2.这个版本的openFeign设计至少是比较合理的

    三、代码

         分为四个部分:

        1.目标服务CustomerService的list接口

       

        @RequestMapping(value = "/list")
        @ResponseBody
        public Object list() throws InterruptedException { // TODO:
            Thread.sleep(25000);
            Map<String,Object> customer=new HashMap<String,Object>();
            customer.put("name","lzf");
            customer.put("sex","");
            customer.put("age","99");
            customer.put("address","中华");
            return customer;
        }
    以上代码故意演示25秒。

         2.测试服务ConfigServiceTest的属性文件

        

    feign:
      client:
        config:
          default:
            connectTimeout: 30000
            readTimeout: 30000

         connectTimeout--连接超时

         readTimeout--读取超时

         二者的区别在于:readTimeout只有成功连接后才会触发。所以如果连接总是没有问题,但是执行太久,那么就必须设定readTimeout。

         并非二者必须配对使用。

         --

         注意:default关键字标识对所有client生效,如果是想针对某个服务,可以直接写服务名称,例如这里可以写 CustomerService.

         3.测试服务ConfigServiceTest的java配置

        配置类

    public class CustomerServiceFeignInter {
        
        @Bean  
        public Integer connectTimeOut() {
            return 30000;
        }
    
        @Bean
        public Integer readTimeOut() {
            return 30000;
        }
    
        @Bean
        public ErrorDecoder feignErrorDecoder() {
            return new ErrorHandler();
        }
    
        @Bean
        public RequestInterceptor currentUserRequestInterceptor() {
            return (RequestTemplate template) -> {
                //Map<String, Collection<String>> header=template.request().headers();
                //System.out.println(JSON.toJSONString(header, true));
                String token = LoginCache.get();
                System.out.println(token);
                template.header("Authorization", token);
            };
        }
    
    }

         注:根据关方文档,config还有另外一种下发,如下:

       

    @Import(FeignClientsConfiguration.class)
    class FooController {
    
        private FooClient fooClient;
    
        private FooClient adminClient;
    
        @Autowired
        public FooController(Client client, Encoder encoder, Decoder decoder, Contract contract, MicrometerCapability micrometerCapability) {
            this.fooClient = Feign.builder().client(client)
                    .encoder(encoder)
                    .decoder(decoder)
                    .contract(contract)
                    .addCapability(micrometerCapability)
                    .requestInterceptor(new BasicAuthRequestInterceptor("user", "user"))
                    .target(FooClient.class, "https://PROD-SVC");
    
            this.adminClient = Feign.builder().client(client)
                    .encoder(encoder)
                    .decoder(decoder)
                    .contract(contract)
                    .addCapability(micrometerCapability)
                    .requestInterceptor(new BasicAuthRequestInterceptor("admin", "admin"))
                    .target(FooClient.class, "https://PROD-SVC");
        }
    }

         服务接口bean

      

    @FeignClient(value = "CustomerService",configuration= CustomerServiceFeignInter.class)
    @Component
    public interface CustomerServiceOpenFeignInterface {
       @RequestMapping(value = "/customer/list")
       public Object list();
    }

         注:根据spring官网的说明,如果有属性和java配置的时候,属性处于优先低位,java配置对应部分被无视。

         4.测试服务ConfigServiceTest的测试代码

        

        @RequestMapping(value = "/timeout")
        @ResponseBody
        public PublicReturn timeout() { // TODO:
            try{
                Object result=customerService.list();
                Map<String,Object> resultMap=new HashMap<>();
                resultMap.put("customer",result);
                PublicReturn re= PublicReturn.getSuccessful("ok",resultMap);
                return re;
            }
            catch (Exception e){
                return PublicReturn.getUnSuccessful(e.getMessage());
            }
        }

    四、测试和结论

         1.如果只是设置属性,那么可以生效,在超过25秒之后,可以看到返回结果。

    {
        "flag": 1,
        "message": "ok",
        "data": {
            "customer": {
                "address": "中华",
                "sex": "男",
                "name": "lzf",
                "age": "99"
            }
        }
    }

         2.如果只是设置java配置(即上文的CustomerServiceFeignInter),那么结果同1

         3.如果二者都有设置,且属性的timeOut是5秒,而配置还是30秒,那么结果是属性胜出,客户端5秒之后报告异常

    feign:
      client:
        config:
          default:
            connectTimeout: 5000
            readTimeout: 5000
      compression:
        request:
          enabled: true
          min-request-size: 2048
        response:
          enabled: true

       

        @RequestMapping(value = "/timeout")
        @ResponseBody
        public PublicReturn timeout() { // TODO:
            long start=System.currentTimeMillis();
            try{
                Object result=customerService.list();
                Map<String,Object> resultMap=new HashMap<>();
                resultMap.put("customer",result);
                PublicReturn re= PublicReturn.getSuccessful("ok",resultMap);
                return re;
            }
            catch (Exception e){
                long end=System.currentTimeMillis();
                return PublicReturn.getUnSuccessful("连接异常"+e.getMessage()+",当前耗费时间"+(end-start));
            }
        }

         结果:

    {
        "flag": 0,
        "message": "连接异常Read timed out executing GET http://CustomerService/customer/list,当前耗费时间5178",
        "data": {}
    }

       属性配置胜出!   

    五、相干问题-压缩

       如果觉得是因为没有压缩导致的timeout或者性能等考虑,那么可以启动压缩,尽量避免超时。

       具体设置见前文。

    六、相关问题-异步

       如果实在不想等太久,那么可以考虑采用异步的方式调用。

        关于异步的调用,可以参考https://www.jb51.net/article/212227.htm#_label2

        @Bean
        public CustomerServiceOpenFeignInterface originFeignClient(SpringEncoder springEncoder, SpringDecoder springDecoder) {
            return AsyncFeign.asyncBuilder()
                    .encoder(springEncoder)
                    .decoder(springDecoder)
                    .target(CustomerServiceOpenFeignInterface.class, "http://localhost.charlesproxy.com:8090");
        }


      测试代码

    @GetMapping("testApi")
    public String testAsyncClient() throws ExecutionException, InterruptedException {
        List<CompletableFuture<String>> results = new ArrayList<>();
        for(int i = 0; i < 10; i++) {
            results.add(originFeignClient.api(i+""));
        }
        Thread.sleep(3000);
        int index = 0;
        for (CompletableFuture<String> result : results) {
            String str = result.get();
            log.info(String.format("%d, result:%s, ", index, str));
            index++;
        }
        return "success";
    }

        或者官网的文档

    七、结论

       openFeign越来越完善的情况下,我们倾向于直接使用它的功能。

       不过openFeign的一些写法,我也不是很苟同--例如在破坏原有编码习惯的情况下,编写代码,例如下面的:

    public interface UserService {
    
        @RequestMapping(method = RequestMethod.GET, value ="/users/{id}")
        User getUser(@PathVariable("id") long id);
    }
    
    @RestController
    public class UserResource implements UserService {
    
    }
    
    package project.user;
    
    @FeignClient("users")
    public interface UserClient extends UserService {
    
    }

      看起来比较怪异,我个人不是很习惯和接受,已经强烈要求大伙不那么写。

  • 相关阅读:
    HDU1316 fib+高精度
    HDU1868
    HDU2586 LCA
    HDU1113 字符串处理
    HDU1115 几何+多边形重心
    HDU1124
    HDU1110 几何
    HDU1103
    HDU2670 DP
    linux 下查看机器是cpu是几核的
  • 原文地址:https://www.cnblogs.com/lzfhope/p/15947457.html
Copyright © 2020-2023  润新知