收藏 分销(赏)

Dubbo服务调用动态选择版本.docx

上传人:仙人****88 文档编号:9454766 上传时间:2025-03-26 格式:DOCX 页数:30 大小:40.58KB
下载 相关 举报
Dubbo服务调用动态选择版本.docx_第1页
第1页 / 共30页
Dubbo服务调用动态选择版本.docx_第2页
第2页 / 共30页
点击查看更多>>
资源描述
Dubbo服务调用动态选择版本 问题说明 Dubbo是阿里巴巴SOA服务化治理方案的核心框架,致力于高性能和透明化的远程服务调用方案和服务治理方案。官方文档见:http://alibaba.github.io/dubbo-doc-static/Home-zh.htm。Dubbo可以和Spring无缝集成,示例如下: 1. 服务提供端 Provider.xml <context:component-scan base-package="com.telecom.**" /> <dubbo:application name="wuchangzheng-demo-app" /> <dubbo:registry address="zookeeper://132.228.12.40:2181" /> <dubbo:protocol name="dubbo" port="20880" /> <dubbo:service interface="com.telecom.dubbo.intf.IHelloService" ref="helloService" version="1.0"/> HelloServiceImpl.java package com.telecom.dubbo.provider; import org.springframework.stereotype.Service; import com.telecom.dubbo.intf.IHelloService; @Service("helloService") public class HelloServiceImpl implements IHelloService{ @Override public String sayHello(String name) { return "Hello "+name; } } 2. 客户端调用 Consumer.xml <context:annotation-config /> <context:component-scan base-package="com.telecom.**" /> <dubbo:application name="wuchangzheng-demo-app-consumer" /> <dubbo:registry address="zookeeper://132.228.12.40:2181" /> <!-- 生成远程服务代理,可以和本地bean一样使用demoService --> <dubbo:reference id="remoteHelloService" interface="com.telecom.dubbo.intf.IHelloService" check="false" version="1.0"/> 服务端 dubbo:service 和 客户端 dubbo:reference 分别有1个version属性。说明:服务端发布provider时可以为服务分配1个版本号,客户端调用服务端的provider时,需要指定调用哪1个版本的服务。项目实施过程碰到一些问题: 1. version属性在xml文件中配置,意味着服务端升级版本号,客户端也要相应修改配置文件并重启应用。 2. 项目中存在灰度发布的要求,服务端的provider可能同时存在多个版本,客户端根据用户工号来选择调用哪个版本,比如指定一批测试工号,来测试新版本的provider。整个发布过程需要不重启web应用实现无缝切换,客户端在xml文件中指定版本号就显得不够,dubbo需要支持客户端自行实现版本选择规则,根据规则决定调用相应版本的provider。 本文描述了如何重载dubbo的部分代码实现客户端版本规则。 实现思路 通过分析dubbo 2.5.3 的源码,<dubbo:reference/> 标签对应的类是 com.alibaba.dubbo.config.spring.ReferenceBean <dubbo:reference/>的version属性被写到ReferenceBean的version属性中,可以通过ReferenceBean.getVersion() 来获取配置文件中指定的远程服务的版本号。 该类是工厂类,实现 org.springframework.beans.factory.FactoryBean,第64行代码 public Object getObject() throws Exception {         return get(); } 返回IHelloService的代理对象,调用该对象的sayHello方法即可调用远程dubbo服务,远程服务的版本号由ReferenceBean的version属性决定。 本文的实现方法是重载 ReferenceBean 的get() 方法,使用javassist技术构造一个新的代理,根据用户自定义的规则来调用相应版本的dubbo服务。 实现方法 重写ReferenceBean package com.telecom.dubbo.extend; import java.lang.reflect.Method; import java.util.concurrent.ConcurrentHashMap; import org.springframework.context.ApplicationContext; import com.alibaba.dubbo.config.ReferenceConfig; import com.alibaba.dubbo.config.annotation.Reference; import javassist.util.proxy.MethodHandler; import javassist.util.proxy.ProxyFactory; import javassist.util.proxy.ProxyObject; /** * 自定义的referencebean,该类会创建动态代理,运行时根据上下文的版本号选择远程服务 * @author wuchangzheng * * @param <T> */ public class ReferenceBean<T> extends com.alibaba.dubbo.config.spring.ReferenceBean<T> implements Cloneable{ private static final long serialVersionUID = 8383612147446385736L; private transient T proxy;//代理 private transient IVersionDesider versionDesider;//版本决定器 private transient ApplicationContext applicationContext; private transient ReferenceBean self_=this; private transient ConcurrentHashMap<String,ReferenceConfig> versionMap = new ConcurrentHashMap<String,ReferenceConfig>(); private transient final String DEFAULT_VERSION="DEFAULT_VERSION"; @SuppressWarnings({ "all"}) public void setApplicationContext(ApplicationContext applicationContext){ super.setApplicationContext(applicationContext); this.applicationContext=applicationContext; } @SuppressWarnings({ "all"}) public void afterPropertiesSet() throws Exception{ super.afterPropertiesSet(); try{ versionDesider=applicationContext.getBean(IVersionDesider.class);//从上下文中查找IVersionDesider实现,该实现代表用户自定义版本规则 }catch(Exception ex){ versionDesider = null; } proxy=getVersionDecideProxy();//创建代理对象 } /** * * @return * @throws ClassNotFoundException * @throws InstantiationException * @throws IllegalAccessException */ @SuppressWarnings({ "all"}) private T getVersionDecideProxy() throws ClassNotFoundException, InstantiationException, IllegalAccessException{ // 创建代理工厂 ProxyFactory proxyFactory = new ProxyFactory(); Class[] interfaces={Class.forName(this.getInterface())}; proxyFactory.setInterfaces(interfaces); // 创建代理类型的Class Class<ProxyObject> proxyClass = proxyFactory.createClass(); T proxy = (T) proxyClass.newInstance(); ((ProxyObject) proxy).setHandler(new MethodHandler() { @Override public T invoke(Object self, Method thisMethod, Method proceed, Object[] args) throws Throwable { T target = null; String version=DEFAULT_VERSION; if (versionDesider!=null){ version=versionDesider.desideVersion(self_); } if (version==null) version=DEFAULT_VERSION; ReferenceConfig config =null; synchronized(self_){ config=versionMap.get(version); if (config==null){ config=(ReferenceConfig)self_.clone(); config.setVersion(version); config.setCheck(false);// versionMap.put(version, config); } } target = (T)config.get(); T retObj = (T)thisMethod.invoke(target, args); return retObj; } }); return proxy; } public Object getObject() throws Exception { return proxy; } } 重载dubbo命名空间解析器 /* * Copyright 1999-2011 Alibaba Group. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.telecom.dubbo.extend; import org.springframework.beans.factory.xml.NamespaceHandlerSupport; import mon.Version; import com.alibaba.dubbo.config.ApplicationConfig; import com.alibaba.dubbo.config.ConsumerConfig; import com.alibaba.dubbo.config.ModuleConfig; import com.alibaba.dubbo.config.MonitorConfig; import com.alibaba.dubbo.config.ProtocolConfig; import com.alibaba.dubbo.config.ProviderConfig; import com.alibaba.dubbo.config.RegistryConfig; import com.alibaba.dubbo.config.spring.AnnotationBean; import com.telecom.dubbo.extend.ReferenceBean; import com.alibaba.dubbo.config.spring.ServiceBean; /** * DubboNamespaceHandler * * @author william.liangf * @export */ public class DubboNamespaceHandler extends NamespaceHandlerSupport { static { Version.checkDuplicate(DubboNamespaceHandler.class); } public void init() { registerBeanDefinitionParser("application", new DubboBeanDefinitionParser(ApplicationConfig.class, true)); registerBeanDefinitionParser("module", new DubboBeanDefinitionParser(ModuleConfig.class, true)); registerBeanDefinitionParser("registry", new DubboBeanDefinitionParser(RegistryConfig.class, true)); registerBeanDefinitionParser("monitor", new DubboBeanDefinitionParser(MonitorConfig.class, true)); registerBeanDefinitionParser("provider", new DubboBeanDefinitionParser(ProviderConfig.class, true)); registerBeanDefinitionParser("consumer", new DubboBeanDefinitionParser(ConsumerConfig.class, true)); registerBeanDefinitionParser("protocol", new DubboBeanDefinitionParser(ProtocolConfig.class, true)); registerBeanDefinitionParser("service", new DubboBeanDefinitionParser(ServiceBean.class, true)); registerBeanDefinitionParser("reference", new DubboBeanDefinitionParser(ReferenceBean.class, false)); registerBeanDefinitionParser("annotation", new DubboBeanDefinitionParser(AnnotationBean.class, true)); } } /* * Copyright 1999-2011 Alibaba Group. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.telecom.dubbo.extend; import java.lang.reflect.Method; import java.lang.reflect.Modifier; import java.util.Date; import java.util.HashSet; import java.util.Set; import java.util.regex.Pattern; import org.springframework.beans.PropertyValue; import org.springframework.beans.factory.config.BeanDefinition; import org.springframework.beans.factory.config.BeanDefinitionHolder; import org.springframework.beans.factory.config.RuntimeBeanReference; import org.springframework.beans.factory.config.TypedStringValue; import org.springframework.beans.factory.support.ManagedList; import org.springframework.beans.factory.support.ManagedMap; import org.springframework.beans.factory.support.RootBeanDefinition; import org.springframework.beans.factory.xml.BeanDefinitionParser; import org.springframework.beans.factory.xml.ParserContext; import org.w3c.dom.Element; import org.w3c.dom.NamedNodeMap; import org.w3c.dom.Node; import org.w3c.dom.NodeList; import mon.Constants; import mon.extension.ExtensionLoader; import mon.logger.Logger; import mon.logger.LoggerFactory; import mon.utils.ReflectUtils; import mon.utils.StringUtils; import com.alibaba.dubbo.config.ArgumentConfig; import com.alibaba.dubbo.config.ConsumerConfig; import com.alibaba.dubbo.config.MethodConfig; import com.alibaba.dubbo.config.MonitorConfig; import com.alibaba.dubbo.config.ProtocolConfig; import com.alibaba.dubbo.config.ProviderConfig; import com.alibaba.dubbo.config.RegistryConfig; import com.telecom.dubbo.extend.ReferenceBean; import com.alibaba.dubbo.config.spring.ServiceBean; import com.alibaba.dubbo.rpc.Protocol; /** * AbstractBeanDefinitionParser * * @author william.liangf * @export */ public class DubboBeanDefinitionParser implements BeanDefinitionParser { private static final Logger logger = LoggerFactory.getLogger(DubboBeanDefinitionParser.class); private final Class<?> beanClass; private final boolean required; public DubboBeanDefinitionParser(Class<?> beanClass, boolean required) { this.beanClass = beanClass; this.required = required; } public BeanDefinition parse(Element element, ParserContext parserContext) { return parse(element, parserContext, beanClass, required); } @SuppressWarnings("unchecked") private static BeanDefinition parse(Element element, ParserContext parserContext, Class<?> beanClass, boolean required) { RootBeanDefinition beanDefinition = new RootBeanDefinition(); beanDefinition.setBeanClass(beanClass); beanDefinition.setLazyInit(false); String id = element.getAttribute("id"); if ((id == null || id.length() == 0) && required) { String generatedBeanName = element.getAttribute("name"); if (generatedBeanName == null || generatedBeanName.length() == 0) { if (ProtocolConfig.class.equals(beanClass)) { generatedBeanName = "dubbo"; } else { generatedBeanName = element.getAttribute("interface"); } } if (generatedBeanName == null || generatedBeanName.length() == 0) { generatedBeanName = beanClass.getName(); } id = generatedBeanName; int counter = 2; while(parserContext.getRegistry().containsBeanDefinition(id)) { id = generatedBeanName + (counter ++); } } if (id != null && id.length() > 0) { if (parserContext.getRegistry().containsBeanDefinition(id)) { throw new IllegalStateException("Duplicate spring bean id " + id); } parserContext.getRegistry().registerBeanDefinition(id, beanDefinition); beanDefinition.getPropertyValues().addPropertyValue("id", id); } if (ProtocolConfig.class.equals(beanClass)) { for (String name : parserContext.getRegistry().getBeanDefinitionNames()) { BeanDefinition definition = parserContext.getRegistry().getBeanDefinition(name); PropertyValue property = definition.getPropertyValues().getPropertyValue("protocol"); if (property != null) { Object value = property.getValue(); if (value instanceof ProtocolConfig && id.equals(((ProtocolConfig) value).getName())) { definition.getPropertyValues().addPropertyValue("protocol", new RuntimeBeanReference(id)); } } } } else if (ServiceBean.class.equals(beanClass)) { String className = element.getAttribute("class"); if(className != null && className.length() > 0) { RootBeanDefinition classDefinition = new RootBeanDefinition(); classDefinition.setBeanClass(ReflectUtils.forName(className)); classDefinition.setLazyInit(false); parseProperties(element.getChildNodes(), classDefinition); beanDefinition.getPropertyValues().addPropertyValue("ref", new BeanDefinitionHolder(classDefinition, id + "Impl")); } } else if (ProviderConfig.class.equals(beanClass)) { parseNested(element, parserContext, ServiceBean.class, true, "service", "provider", id, beanDefinition); } else if (ConsumerConfig.class.equals(beanClass)) { parseNested(element, parserContext, ReferenceBean.class, false, "reference", "consumer", id, beanDefinition); } Set<String> props = new HashSet<String>(); ManagedMap parameters = null; for (Method setter : beanClass.getMethods()) { String name = setter.getName(); if (name.length() > 3 && name.startsWith("set") && Modifier.isPublic(setter.getModifiers()) && setter.getParameterTypes().length == 1) { Class<?> type = setter.getParameterT
展开阅读全文

开通  VIP会员、SVIP会员  优惠大
下载10份以上建议开通VIP会员
下载20份以上建议开通SVIP会员


开通VIP      成为共赢上传
相似文档                                   自信AI助手自信AI助手

当前位置:首页 > 教育专区 > 小学其他

移动网页_全站_页脚广告1

关于我们      便捷服务       自信AI       AI导航        抽奖活动

©2010-2025 宁波自信网络信息技术有限公司  版权所有

客服电话:4009-655-100  投诉/维权电话:18658249818

gongan.png浙公网安备33021202000488号   

icp.png浙ICP备2021020529号-1  |  浙B2-20240490  

关注我们 :微信公众号    抖音    微博    LOFTER 

客服