1. 程式人生 > >Dubbo自定義日誌攔截器

Dubbo自定義日誌攔截器

前言

上一篇文章 Spring aop+自定義註解統一記錄使用者行為日誌 記錄了 web層中通過自定義註解配合Spring aop自動記錄使用者行為日誌的過程。那麼按照分散式架構中Dubbo服務層的呼叫過程是否也可以實現統一記錄日誌?自定義日誌攔截器可以實現這個需求。

需求場景

在使用Dubbo搭建的分散式專案中,服務層程式碼呼叫是這樣的:

     @GetMapping(value = "/info")
2    public BaseResult userInfo() {
3        //rpc遠端呼叫使用者服務
4        BaseResult result = mUserService.userInfo();
6
return result; 7 } 複製程式碼

這裡的使用者服務位於另外一個服務程序,由服務提供者暴露出來,讓web層遠端呼叫,需要記錄服務結果的呼叫過程,便於跟蹤定位bug.

自定義日誌攔截器

翻看下Dubbo官方文件,可以看到如下內容:

簡要說明:

  • Dubbo 中所有的攔截器全部繼承自org.apache.dubbo.rpc.Filter介面,我們自己也可以自行擴充套件,只要繼承該介面即可.
  • 使用者自定義 filter 預設在內建 filter 之後執行

新增 DubboServiceFilter 攔截器如下:

public
class DubboServiceFilter implements Filter { private static final Logger LOGGER = LoggerFactory.getLogger(DubboServiceFilter.class); @Override public Result invoke(Invoker<?> invoker, Invocation invocation) throws RpcException { //外部日誌開關預設關閉 String logSwitch = StringUtils.equals(RedisUtil.get(BaseConstants.CACHE_SERVICE_LOG_SWITCH), BaseConstants.YES) ? BaseConstants.YES : BaseConstants.NO; if
(StringUtils.equals(BaseConstants.YES, logSwitch)) { //列印入參日誌 DubboServiceRequest serviceRequest = new DubboServiceRequest(); serviceRequest.setInterfaceName(invocation.getInvoker().getInterface().getName()); serviceRequest.setMethodName(invocation.getMethodName()); serviceRequest.setArgs(invocation.getArguments()); LOGGER.info("dubbo服務介面入參: " + JSON.toJSONString(serviceRequest)); } //開始時間 long startTime = System.currentTimeMillis(); //執行介面呼叫邏輯 Result result = invoker.invoke(invocation); //呼叫耗時 long elapsed = System.currentTimeMillis() - startTime; //如果發生異常 則列印異常日誌 if (result.hasException() && invoker.getInterface() != GenericService.class) { LOGGER.error("dubbo執行異常: ", result.getException()); } else { if (StringUtils.equals(BaseConstants.YES, logSwitch)) { //列印響應日誌 DubboServiceResponse serviceResponse = new DubboServiceResponse(); serviceResponse.setMethodName(invocation.getMethodName()); serviceResponse.setInterfaceName(invocation.getInvoker().getInterface().getName()); serviceResponse.setArgs(invocation.getArguments()); serviceResponse.setResult(new Object[]{result.getValue()}); serviceResponse.setSpendTime(elapsed); LOGGER.info("dubbo服務響應成功,返回資料: " + JSON.toJSONString(serviceResponse)); } } //返回結果響應結果 return result; } } 複製程式碼

程式碼中對應的實體bean如下:

入參實體:

/**
 * @program: easywits
 * @description:Dubbo服務請求入參實體
 * @author: zhangshaolin
 * @create: 2019-01-08 20:35
 **/
@Data
public class DubboServiceRequest implements Serializable{
    private static final long serialVersionUID = 7127824956842786618L;

    /**
     * 介面名
     */
    private String interfaceName;

    /**
     * 方法名
     */
    private String methodName;

    /**
     * 引數
     */
    private Object[] args;
}
複製程式碼

響應實體:

/**
 * @program: easywits
 * @description: Dubbo服務響應結果實體
 * @author: zhangshaolin
 * @create: 2019-01-08 20:36
 **/
@Data
public class DubboServiceResponse implements Serializable{
    private static final long serialVersionUID = -2531169660859647737L;

    /**
     * 介面名
     */
    private String interfaceName;

    /**
     * 方法名
     */
    private String methodName;

    /**
     * 引數
     */
    private Object[] args;

    /**
     * 返回結果
     */
    private Object result;

    /**
     * 呼叫耗時(毫秒)
     */
    private long spendTime;
}
複製程式碼

/src/main/resources/META-INF/dubbo目錄下新增純文字檔案org.apache.dubbo.rpc.Filter 內容為:

dubboServiceFilter=com.easywits.common.filter.DubboServiceFilter
複製程式碼
  • 鍵值對形式,鍵隨便起個名字
  • 值為DubboServiceFilter攔截器的完整包名.

最後在服務提供者配置檔案中新增配置使攔截器生效:

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
      ...省略部分程式碼">

    <!--服務提供方應用資訊,用於計算依賴關係-->
    <dubbo:application name="easywits-upms-rpc-service"/>

    <!--用dubbo協議在20881埠暴露服務-->
    <dubbo:protocol name="dubbo" port="20881" payload="52428800"/>

    <!--自定義服務層過濾器,值為上述步驟文字檔案中的鍵-->
    <dubbo:provider filter="dubboServiceFilter"/>
    
    ....省略部分服務配置
</beans>
複製程式碼

驗證結果

抓一下我們業務中的部分日誌資訊看下效果,如下圖:

可以清楚地看到Dubbo服務介面呼叫的請求引數資訊,以及最終的響應結果資訊,便於定位線上問題。

參考文件:http://dubbo.apache.org/zh-cn/docs/dev/impls/filter.html

最後

記錄一個比較簡單的具體實用場景,後續會不定期更新更多的實用場景,歡迎關注公眾號【張少林同學】!