Dubbo的Filter實戰--整合Oval校驗框架

 

前言:
  其實很早以前就想寫一篇關於oval和具體服務相整合的常見作法, 並以此做爲一篇筆記. 趁如今項目中間空閒期, 恰好對dubbo的filter有一些瞭解. 所以想結合二者, 寫一下既結合校驗框架, 又能少侵入性的編程模式.
  Dubbo的filter機制, 其實對標springmvc的interceptor, netty的iohandler, 甚至servlet體系的filter, 都是責任鏈模式的實現方式. 本文着重dubbo的filter和oval校驗框架的組合, 其實徹底能夠借鑑/延伸過去, 所以本文有它的意義.html

 

常見的Dubbo接口約定:
  如今流行的作法, 兩個服務之間的調用, 是不會把異常信息拋給對方, 而是內部把異常轉化爲特定的錯誤碼, 並告知調用方.
  常見的響應類模式被設計爲泛型Result, 而真正返回的實體就是泛型對應的對象.java

@Getter
@Setter
@ToString
public class TResult<T> {

    private boolean success = true;

    private int errCode = 0;

    private String errMsg = "OK";

    private T value = null;

}

  當success爲true時, 泛型對象value爲真正的實體, 而success爲false時, 則errCode/errMsg會具體描述各種錯誤狀況, 好比參數不正確/狀態不正確.spring

  

常見的實現方法:
  服務端的接口具體的實現, 每每是這樣的模樣:編程

@Getter
@Setter
class EchoReq {

    @NotNull(message = "message字段不能爲空")
    private String message;

}

public interface IEchoService {

    TResult<String> echo1(String name);

    TResult<String> echo2(EchoReq req);

}

// *) 須要設定@Guarded註解, 才能使函數參數的preconditions校驗機制生效
@Guarded
@Service("echoService")
public class EchoServiceImpl implements IEchoService {


    @Override
    public TResult<String> echo1(@NotNull(message="name字段不能爲空") String name) {
        TResult<String> result = new TResult<String>();
        return result;
    }

    @Override
    public TResult<String> echo2(EchoReq req) {
        TResult<String> result = new TResult<String>();
        try {
            // *) 參數校驗
            Validator validator = new Validator();
            List<ConstraintViolation> cvs = validator.validate(req);
            if ( cvs != null && cvs.size() > 0 ) {
                result.setSuccess(false);
                result.setErrCode(10001);
                result.setErrMsg("參數不正確:" + cvs.get(0).getMessage());
                return result;
            }

            // *) 具體的業務代碼

        } catch(Throwable e) {
            result.setSuccess(false);
            result.setErrCode(10002);
            result.setErrMsg("Internal Server Error:");
            return result;
        }
        return result;
    }

}

  正如你所見的, 核心代碼外圍須要一個try/catch以支持異常到錯誤碼的轉換, 可否有個辦法去掉這個try/catch.
  同時Oval註解不光做用於Bean類的屬性成員上, 還能夠在做用於函數的參數上, 這樣的話, try/catch就覆蓋不及了.mvc

  來個題外話, 讓oval支持preconditions, 須要一些額外的工做, 默認不開啓. 可以使用spring-aop來開啓, 可具體參閱該文章.框架

  相似這樣的配置:ide

<beans>
  <bean id="myService" class="MyServiceImpl" />

  <bean id="ovalGuardInterceptor" class="net.sf.oval.guard.GuardInterceptor" />

  <bean class="org.springframework.aop.framework.autoproxy.BeanNameAutoProxyCreator">
    <property name="proxyTargetClass" value="true" />
    <property name="beanNames" value="*Service" />
    <property name="interceptorNames"><list><value>ovalGuardInterceptor</value></list></property>
  </bean>
</beans>

   

引入filter:
  咱們引入OvalFilter, 具體代碼以下:函數

public class OvalFilter implements Filter {

    @Override
    public Result invoke(Invoker<?> invoker, Invocation invocation) throws RpcException {

        Result result = invoker.invoke(invocation);

        if ( result.hasException() ) {
            TResult<Void> res = new TResult<Void>();

            Throwable e = result.getException();
            if ( e instanceof ConstraintsViolatedException) {
                // *) 參數不正確
                res.setSuccess(false);
                res.setErrCode(10001);
                res.setErrMsg(e.getMessage());
            } else {
                // *) 服務端內部錯誤
                res.setSuccess(false);
                res.setErrCode(10002);
                res.setErrMsg("Internal Server Error");
            }
            return new RpcResult(res);
        }

        return result;

    }

}

  Dubbo的filter的引入, 以及配置, 能夠具體參閱文章:
  1. Dubbo透傳traceId/logid的一種思路 
  2. Dubbo的Filter鏈梳理---分組可見和順序調整 
  測試的結果, 符合預期, OvalFilter成功地把Dubbo的service拋出的ConstraintsViolatedException捕獲, 併成功轉化爲參數校驗失敗的錯誤信息返回. 這樣的好處, 可讓dubbo具體的service實現類, 減小異常的處理, 使得代碼簡潔, 可讀性更強. 測試

  以上面的樣例代碼爲例, 咱們能夠簡化以下:ui

@Guarded
@Service("echoService")
public class EchoServiceImpl implements IEchoService {

    @Override
    public TResult<String> echo1(@NotNull(message="name字段不能爲空") String name) {
        TResult<String> result = new TResult<String>();
        return result;
    }

    @Override
    public TResult<String> echo2(EchoReq req) {
        TResult<String> result = new TResult<String>();
        // *) 參數校驗
        Validator validator = new Validator();
        List<ConstraintViolation> cvs = validator.validate(req);
        if ( cvs != null && cvs.size() > 0 ) {
            throw new ConstraintsViolatedException(cvs);
        }

        // *) 具體的業務代碼

        return result;
    }

}

 

總結:   Dubbo服務如何處理參數校驗這塊, 不一樣的人/公司, 都有本身的偏好. 本文講述了利用Oval框架來校驗參數, 同時利用dubbo強大的自定義filter機制, 把校驗參數異常隱藏, 並返回更友好的提示信息. 同時使服務的代碼更加簡潔, 可讀性更強.   在整理這塊時, 感受Oval的preconditions支持水更深, 但願本身有機會對這塊可以深刻研究下.

相關文章
相關標籤/搜索