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调用监控就完成了,因为掌控了请求过程,后续可以加上耗时、异常种类的统计等等。

  • 相关阅读:
    七种常用的特征工程
    Linux发邮件
    git提交的问题
    怎么在工作中快速学习,获得晋升?——吴军得到直播实录
    曾李青总结的创业的观点
    python解析json数据
    【基础算法整理】
    【剑指offer】连续子数组的最大和
    【剑指offer】最小的K个数
    【剑指offer】数组中出现次数超过一半的数字
  • 原文地址:https://www.cnblogs.com/notayeser/p/15780997.html
Copyright © 2011-2022 走看看