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調用監控就完成了,因為掌控了請求過程,后續可以加上耗時、異常種類的統計等等。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM