需求缘由
最近部分只读服务已经切换到了Dubbo提供的服务化接口;经过一段时间的生产验证, 各种监控指标显示非常稳定,所以打算开始切换写服务接口; 但大家对写操作跑服务化没有信心,于是提了一个需求能随时将远程rpc调用切换打本地jar方法调用;
dubbo的stub
听到这个需求,马上想到dubbo的stub属性;但需要在原来代码中做部分改造,具体步骤是:
- 原来的jar包实现方法中增加带自身接口类型参数的构造函数;
- 在所有的接口实现方法中判断开关状态决定调用RPC/本地方法;
但是,这样对原代码有极强入侵性;我们认为这不是最好的方案,于是有了下面的方案;
扩展dubbo:reference标签属性
首先需要说明,我们在实际使用dubbo的过程中并没有直接使用dubbo这个命名空间,而是自定义了自己的命名空间,自定义命名空间兼容了dubbo命名空间的所有属性,而且扩展了自己的属性;于是针对该需求我们有了新的方案,具体流程如下 :
- 增加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"/> - 继承ReferenceBean实现自定义属性locbean;
- 继承DubboNamespaceHandler重写init方法,如下:
1
2
3
4public void init() {
super.init();
this.registerBeanDefinitionParser("reference", new MyBeanDefinitionParser(LocalReferenceBean.class, false));
} - 自定义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
39public 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;
}
} - 配置filter
在resources/META-INF/dubbo/com.alibaba.dubbo.rpc.Filter文件中增加如下内容:配置consumer的filter属性1
reflocal=com.example.filter. RPCLocalSwitchFilter
至此可以通过配置中的开关状态来控制远程RPC/本地jar方法调用 ;1
<mydubbo:consumer filter="reflocal" />