0%

基于配置中心属性,Dubbo Consumer动态切换调用RPC/本地方法

需求缘由

最近部分只读服务已经切换到了Dubbo提供的服务化接口;经过一段时间的生产验证, 各种监控指标显示非常稳定,所以打算开始切换写服务接口; 但大家对写操作跑服务化没有信心,于是提了一个需求能随时将远程rpc调用切换打本地jar方法调用;

dubbo的stub

听到这个需求,马上想到dubbo的stub属性;但需要在原来代码中做部分改造,具体步骤是:

  1. 原来的jar包实现方法中增加带自身接口类型参数的构造函数;
  2. 在所有的接口实现方法中判断开关状态决定调用RPC/本地方法;
    但是,这样对原代码有极强入侵性;我们认为这不是最好的方案,于是有了下面的方案;

扩展dubbo:reference标签属性

首先需要说明,我们在实际使用dubbo的过程中并没有直接使用dubbo这个命名空间,而是自定义了自己的命名空间,自定义命名空间兼容了dubbo命名空间的所有属性,而且扩展了自己的属性;于是针对该需求我们有了新的方案,具体流程如下 :

  1. 增加reference的属性localbean,这个属性值为一个本地bean的id;类似的xml配置如下:
    1
    2
    3
    <bean id="localDemoService" class="com.example.Service.impl.DemoServiceImpl" />
    <mydubbo:reference interface="com.example.Service.DemoService" id="remoteDemoService"
    localbean ="localDemoService" version="1.0"/>
  2. 继承ReferenceBean实现自定义属性locbean;
  3. 继承DubboNamespaceHandler重写init方法,如下:
    1
    2
    3
    4
    public void init() {
    super.init();
    this.registerBeanDefinitionParser("reference", new MyBeanDefinitionParser(LocalReferenceBean.class, false));
    }
  4. 自定义filter
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    public class RPCLocalSwitchFilter<T> implements Filter {
    private static final Logger logger = LoggerFactory.getLogger(RPCLocalSwitchFilter.class);
    private static final ProxyFactory PROXY_FACTORY = (ProxyFactory)ExtensionLoader.getExtensionLoader(ProxyFactory.class).getAdaptiveExtension();

    public RPCLocalSwitchFilter() {
    }

    public Result invoke(Invoker<?> invoker, Invocation invocation) throws RpcException {
    if(cfg.getBooleanProperty("")) { //判断配置中心开工状态
    Object bean = this.getLocalBean(invoker);
    return bean != null?this.executeLocalMethod(bean, invoker, invocation):invoker.invoke(invocation);
    } else {
    return invoker.invoke(invocation);
    }
    }

    private Result executeLocalMethod(T bean, Invoker<?> invoker, Invocation invocation) {
    if(logger.isDebugEnabled()) {
    logger.debug("execute local method instead of rpc " + bean.getClass().getCanonicalName() + "." + invocation.getMethodName());
    }

    Invoker localInvoker = PROXY_FACTORY.getInvoker(bean, invoker.getInterface(), invoker.getUrl());
    return localInvoker.invoke(invocation);
    }

    private <T> T getLocalBean(Invoker<?> invoker) {
    String refLocal = invoker.getUrl().getParameter("localBean");
    if(refLocal != null) {
    try {
    ApplicationContext e = LocalReferenceBean.getSpringContext();
    return e.getBean(refLocal, invoker.getInterface());
    } catch (BeansException e) {
    logger.error(var4.getMessage() + " couldn\'t find the bean[" + localBean + "], please check", e);
    }
    }

    return null;
    }
    }
  5. 配置filter
    在resources/META-INF/dubbo/com.alibaba.dubbo.rpc.Filter文件中增加如下内容:
    1
    reflocal=com.example.filter. RPCLocalSwitchFilter
    配置consumer的filter属性
    1
    <mydubbo:consumer filter="reflocal" />
    至此可以通过配置中的开关状态来控制远程RPC/本地jar方法调用 ;