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支持水更深, 但愿本身有机会对这块可以深刻研究下.

相关文章
相关标签/搜索