zoukankan      html  css  js  c++  java
  • Micrometer + Prometheus 监控 Feign 调用实战

    背景

    可观测性是系统架构的基石,准确详细的度量是工程师的重要决策来源。对于微服务系统,除了传统意义上系统边界层的监控指标,服务内部调用的情况也需引起重视,这回就来分享下笔者在实现Feign调用监控的实战经验。

    实现

    先看看我们的监控对象:调用次数,附带标签有:服务名、uri、计数、状态码,这些信息或跟请求有关,或跟响应有关。不难想出埋点的模式无非是拦截器、过滤器链和装饰器之类,需要对请求过程前后插入环绕代码的模式。

    笔者曾写过善用RequestInterceptor的文章:Feign Interceptor 拦截器实现全局请求参数 。RequestInterceptor是Feign暴露给使用者的拦截器,但只作用到请求之前,没法统计请求结果。除此之外还有改造Decoder、为@FeignClient类添加AOP环绕等手段,但最终笔者还是采用了装饰Client的方式来解决。

    Client在feign中是请求的实际发送者,通过控制Client实现,就能拿到完整的请求过程。

        @Slf4j
        @AllArgsConstructor
        public static class MetricsFeignClient implements Client {
    
            private final Client delegate;
            private final MeterRegistry meterRegistry;
    
            @Override
            public Response execute(Request request, Request.Options options) throws IOException {
    
                Response response = null;
                try {
                    response = delegate.execute(request, options);
                } finally {
                    try {
                        meterRegistry.counter("feign_execution",
                                "target", request.requestTemplate().feignTarget().name(),
                                "uri", URLUtil.getPath(request.url()),
                                "status", Optional.ofNullable(response).map(Response::status).orElse(-1).toString()
                        ).increment();
                    } catch (Exception e) {
                            log.error("error counting rpc invocation", e);
                    }
                }
                return response;
            }
        }
    

    直接实现Client,通过@Bean注入,当然也能实现目的,但万万不可这样实操。编写系统组件的重要原则,就是不能影响业务,如果接入了监控代码的业务服务中自行实现了Client用作己用(也可能是引入了带自定义Client的框架,如ribbon),要么Client之间相互覆盖导致失去重要功能,要么因Bean冲突而启动失败。所以笔者认为最好的方式就是对原有Client实例进行装饰,如上文代码的delegate字段。那么我们就需要监听到Client类创建完毕,然后用新类装饰,替换掉老的Client对象,笔者选择通过BeanPostProcessor实现。

    @Slf4j
    public class RpcMetricsExecutionProcessor implements BeanPostProcessor {
    
        private volatile boolean injected = false;
    
        @Override
        public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
            if (!injected && bean instanceof Client) {
                injected = true;
                log.info("rpc execution metrics decorator injected , bean name : {} , original bean type : {} ", beanName, bean.getClass().getSimpleName());
                return new MetricsFeignClient((Client) bean, registry);
            }
            return bean;
        }
    
    

    定义了BeanPostProcessor后,Bean创建完成就会回调上文中的方法,因为所有Bean创建都会回调,我们只需寻找我们需要的Client类即可。

    另外笔者推荐用配置类的方式加载监控逻辑,避免业务工程没有引入Feign依赖而报错。

    @Slf4j
    @ConditionalOnClass(name = "feign.Client")
    public class RpcMetricsExecutionConfiguration {
    
        @Bean
        public RpcMetricsExecutionProcessor rpcMetricsExecutionProcessor() {
            return new RpcMetricsExecutionProcessor();
        }
    
    }
    

    至此基本的Feign调用监控就完成了,因为掌控了请求过程,后续可以加上耗时、异常种类的统计等等。

  • 相关阅读:
    eclipse中的Invalid text string (xxx).
    在jsp文件中出现Unknown tag (c:out)
    eclipse 界面复原
    ecilpse 纠错插件
    Multiple annotations found at this line:- The superclass "javax.servlet.http.HttpServlet" was not found on the Java Build Path
    Port 8080 required by Tomcat v9.0 Server at localhost is already in use. The server may already be running in another process, or a system process may be using the port.
    调用第三方https接口
    调用第三方http接口
    创建带值枚举
    spring整合redis之Redis配置文件
  • 原文地址:https://www.cnblogs.com/notayeser/p/15780997.html
Copyright © 2011-2022 走看看