【问题标题】:Access URITemplate or RequestLine value in Feign RequestInterceptor / RequestTemplate在 Feign RequestInterceptor / RequestTemplate 中访问 URITemplate 或 RequestLine 值
【发布时间】:2019-08-27 03:12:58
【问题描述】:

我正在针对具有硬 api 速率限制的云应用程序开发应用程序。为了让我的团队了解我们在这些限制方面有多接近,我想以一种有意义的方式统计从我们的应用发出的所有 API 调用。

我们使用 Feign 作为访问层,我希望能够使用 RequestInterceptor 来统计我们调用的不同 API 端点:

RequestInterceptor ri = rq -> addStatistics(rq.url());

现在这不起作用,因为生成的 URL 之后几乎总是计数为“1”,因为它们已经包含所有解析的路径变量,所以我得到计数​​

1 - /something/id1valueverycryptic/get
1 - /something/anothercrypticidkey/get

等等。

我希望以某种方式访问​​ @ResuqestLine 映射值 (GET /something/{id}/get) 或至少 uri 模板预解析 (/somethine/{id}/get)

有没有办法做到这一点?

谢谢!

【问题讨论】:

  • 它是 Spring Boot 应用程序吗?
  • 是的 - 添加了标签来表明这一点。

标签: java spring-boot netflix-feign


【解决方案1】:

也许您可以尝试使用自定义 feign InvocationHandlerFactory。

我已经设法使用如下代码记录 RequestInterceptor:

  • 更改 EnableFeignClients 并添加 defaultConfiguration

    @EnableFeignClients(defaultConfiguration = FeignConfig.class)
    
  • 添加默认的 feign 配置

    @Configuration
    public class FeignConfig {
    
    @Bean
    @ConditionalOnMissingBean
    public Retryer feignRetryer() {
        return Retryer.NEVER_RETRY;
    }
    
    @Bean
    @Scope("prototype")
    @ConditionalOnMissingBean
    public Feign.Builder feignBuilder(Retryer retryer) {
        return Feign.builder()
                .retryer(retryer)
                .invocationHandlerFactory((target, dispatch) -> new CountingFeignInvocationHandler(target, dispatch));
    }
    
    }
    
  • 创建您的调用处理程序(基于 feign.ReflectiveFeign.FeignInvocationHandler 的代码)

    public class CountingFeignInvocationHandler implements InvocationHandler {
    
        private final Target target;
        private final Map<Method, MethodHandler> dispatch;
    
        public CountingFeignInvocationHandler(Target target, Map<Method, MethodHandler> dispatch) {
            this.target = checkNotNull(target, "target");
            this.dispatch = checkNotNull(dispatch, "dispatch for %s", target);
        }
    
        @Override
        public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
            if ("equals".equals(method.getName())) {
                try {
                    Object otherHandler =
                            args.length > 0 && args[0] != null ? Proxy.getInvocationHandler(args[0]) : null;
                    return equals(otherHandler);
                } catch (IllegalArgumentException e) {
                    return false;
                }
            } else if ("hashCode".equals(method.getName())) {
                return hashCode();
            } else if ("toString".equals(method.getName())) {
                return toString();
            }
    
            RequestLine requestLine = method.getAnnotation(RequestLine.class);
            addStatistics(requestLine.value());
    
            return dispatch.get(method).invoke(args);
        }
    
        @Override
        public boolean equals(Object obj) {
            if (obj instanceof CountingFeignInvocationHandler) {
                CountingFeignInvocationHandler other = (CountingFeignInvocationHandler) obj;
                return target.equals(other.target);
            }
            return false;
        }
    
        @Override
        public int hashCode() {
            return target.hashCode();
        }
    
        @Override
        public String toString() {
            return target.toString();
        }
    }
    

请小心检查您是否假装配置不是更复杂,在这种情况下根据需要扩展类。

【讨论】:

    【解决方案2】:
        If you are using spring-cloud-starter-openfeign ,  You could do something like below 
        
        add the a primary contract bean 
        @Bean("YourContract")
        @Primary
            public Contract springpringContract() {
                return (targetType) -> {
        
                    List<MethodMetadata> parseAndValidatateMetadata = new SpringMvcContract().parseAndValidatateMetadata(targetType);
                    parseAndValidatateMetadata.forEach(metadata -> {
                        RequestTemplate template = metadata.template();
                        template.header("unresolved_uri", template.path().replace("{", "[").replace("}", "]"));
        
                    });
                    return parseAndValidatateMetadata;
                };
            }
        
        Add the contract to the feign client builder 
        @Bean
         public <T> T feignBuilder(Class<T> feignInterface, String targetURL) {
                return Feign.builder().client(getClient())
                        .contract(contract)
                        .
                        .
        }
        
        Once you are done with the above you should be able to access the unresolved path in the RequestTemplate
    
    @component
    public class FeignRequestFilter  implements RequestInterceptor {
        @Override
            public void apply(RequestTemplate template) {
                String unresolvedUri = template.headers().getOrDefault("unresolved_uri", Collections.singleton(template.path()))
                        .iterator().next();
        }
    }
    

    【讨论】:

      猜你喜欢
      • 2016-04-15
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2010-12-10
      • 2016-06-25
      • 1970-01-01
      • 2016-03-07
      • 2016-07-01
      相关资源
      最近更新 更多