Dubbo服务分组及多版本、动态调用实现
问题提出
Dubbo常用使用方式大家都比较熟悉,确定好服务的与服务之间调用关系,确定好相关接口参数,基于spring配置好,启动provider,然后启动consumer去调用相关服务。但有这样的一种场景:
所有的Provider的接口都相同,但每个系统有自己的不同实现。例如系统A和B都提供com.HelloService服务,但具体实现不一样,需要Consumer端根据传入参数来区分开来并调用
Dubbo的Consumer端需要在运行时才知道调用具体的Dubbo服务,而这个<dubbo:reference/>并没有在spring的bean中配置
解决方案
同一个服务不同实现版本---group分组方法
先来看第一种场景,可以通过配置group 的方式实现同一个服务的不同实现版本:
提供者dubbo端配置:
<dubbo:service interface="com.HelloService" group="groupA" ref="helloService" />
消费者consumer配置:
<dubbo:reference id="helloService"interface="com.HelloService" group="groupA"/>
说明:只有相同group的才能进行匹配,若要实现消费者任意调用提供者的某个服务,只需要把group设置为“”,即:
<dubbo:reference interface="com.HelloService" group="" id="helloService"/>
同一个服务不同实现版本---多版本号
当一个接口实现,出现不兼容升级时,可以用版本号过渡,版本号不同的服务相互间不引用。
可以按照以下的步骤进行版本迁移:
在低压力时间段,先升级一半提供者为新版本
再将所有消费者升级为新版本
然后将剩下的一半提供者升级为新版本
老版本服务提供者配置:
<dubbo:service interface="com.foo.BarService" version="1.0.0" ref="barServiceV1"/>
新版本服务提供者配置:
<dubbo:service interface="com.foo.BarService" version="2.0.0" ref="barServiceV2"/>
老版本服务消费者配置:
<dubbo:reference id="barServiceV1" interface="com.foo.BarService" version="1.0.0" />
新版本服务消费者配置:
<dubbo:reference id="barServiceV2" interface="com.foo.BarService" version="2.0.0" />
如果不需要区分版本,可以按照以下的方式配置(2.2.0 以上版本支持)
<dubbo:reference id="barService" interface="com.foo.BarService" version="*" />
注:如果服务提供者使用了版本,消费者引入该服务时候,version字段不能为空。
动态调用
需要在实际使用时,构造出Consumer端的服务类,并通过上述的group的方式区分不同的服务实现,如下:
public HelloService getInvokeService(String group) {
ApplicationConfig application = new ApplicationConfig();
application.setName("dubboConsumer");
RegistryConfig registry = new RegistryConfig();
registry.setAddress("127.0.0.1:2181");
registry.setProtocol("zookeeper");
ReferenceConfig<HelloService> referenceConfig = new ReferenceConfig<>();
referenceConfig.setApplication(application);
referenceConfig.setRegistry(registry);
referenceConfig.setGroup(group);
referenceConfig.setInterface(HelloService.class);
return referenceConfig.get();
}
性能优化
上述实现已经可以满足我们提出的两个要求,但是存在性能问题,因为每次调用该方法,都需要重新生成一个新的ReferenceConfig,并且调用get()方法获取一个代理的实现,该实现封装了与注册中心的连接以及与提供者的连接。为了能够重用该连接,可以将其缓存,这里使用dubbo内置的简单缓存工具类进行缓存,实现代码如下:
public HelloService getInvokeService(String group) {
ApplicationConfig application = new ApplicationConfig();
application.setName("dubboConsumer");
RegistryConfig registry = new RegistryConfig();
registry.setAddress("127.0.0.1:2181");
registry.setProtocol("zookeeper");
ReferenceConfig<HelloService> referenceConfig = new ReferenceConfig<>();
referenceConfig.setApplication(application);
referenceConfig.setRegistry(registry);
referenceConfig.setGroup(group);
referenceConfig.setInterface(HelloService.class);
ReferenceConfigCache cache = ReferenceConfigCache.getCache();
return cache.get(referenceConfig);
}
根据url动态调用
@Test
@Disabled
public void doGetServiceByUrlTest() {
String url = "dubbo://127.0.0.1:22880/com.ccc.ddd.domain.XxxService ?anyhost=true&application=xxx&bean.name=xxx&deprecated=false&dubbo=2.0.2&dynamic=true&generic=false&group=xxx&interface=xxx&methods=notify&owner=xxx&pid=66088®ister=true&release=2.7.3&revision=1.0.0-SNAPSHOT&serverApplicationName=xxx&service.filter=catTransaction&side=provider&threads=500&timeout=10000×tamp=1611977670756";
XxxService xxxService= (XxxService ) dubboRealReferenceService.getServiceByUrl(url, XxxService .class);
assertThat(xxxService).isNotNull();
ResponseData responseData = xxxService.mmm("");
assertThat(responseData.getCode()).isEqualTo(0);
}
import com.xxx.ApplicationContextHelper;
import org.apache.dubbo.config.RegistryConfig;
import org.apache.dubbo.config.spring.ReferenceBean;
import org.apache.poi.ss.formula.functions.T;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;
@Service
public class DubboRealReferenceService {
/*
@Value("${dubbo.registry.protocol}")
private String registryProtocol;
@Value("${dubbo.registry.address}")
private String registryAddress;
*/
@Autowired
private RegistryConfig registry;
private final Map<String, ReferenceBean<T>> referenceBeanMap = new ConcurrentHashMap<>();
public Object getServiceByGroup(String group, Class cls) {
/*
RegistryConfig registry = new RegistryConfig();
registry.setAddress(registryAddress);
registry.setProtocol(registryProtocol);
*/
ReferenceBean<T> referenceBean = referenceBeanMap.get(group);
if (referenceBean != null){
return referenceBean.get();
}
ReferenceBean<T> referenceBean = new ReferenceBean<T>();
referenceBean.setApplicationContext(ApplicationContextHelper.getApplicationContext());
referenceBean.setInterface(cls);
referenceBean.setGroup(group);
referenceBean.setRegistry(registry);
try {
referenceBean.afterPropertiesSet();
return referenceBean.get();
} catch (Exception e) {
e.printStackTrace();
}
return null;
}
public Object getServiceByUrl(String url, Class cls) {
ReferenceBean<T> referenceBean = new ReferenceBean<T>();
referenceBean.setApplicationContext(ApplicationContextHelper.getApplicationContext());
referenceBean.setInterface(cls);
referenceBean.setUrl(url);
try {
referenceBean.afterPropertiesSet();
return referenceBean.get();
} catch (Exception e) {
e.printStackTrace();
}
return null;
}
public GenericService getGenericServiceByGroup(String group, Class cls) {
/*
RegistryConfig registry = new RegistryConfig();
registry.setAddress(registryAddress);
registry.setProtocol(registryProtocol);
*/
// 普通编码配置方式
ApplicationConfig application = new ApplicationConfig();
application.setName("xxx");
ReferenceConfig<GenericService> reference = new ReferenceConfig<GenericService>();
reference.setApplication(application);
reference.setRegistry(registry);
reference.setGroup(group);
reference.setInterface(cls);
reference.setGeneric(true);
ReferenceConfigCache cache = ReferenceConfigCache.getCache();
GenericService genericService = cache.get(reference);
return genericService;
}
}
@Test
public void doGetGenericServiceByGroupTest() {
GenericService genericService = dubboRealReferenceService.getGenericServiceByGroup("xxx", IDomainEventService.class);
Object responseObject = genericService.$invoke(SERVICE_METHOD_NAME, PARAM_TYPES, new Object[]{domainEvent});
Map repsoneMap = (HashMap)responseObject;
assertThat(responseObject).isNotNull();
assertThat(repsoneMap.get("code")).isEqualTo(0);
}
refer to:
https://my.oschina.net/u/3729778/blog/2054809
https://www.imooc.com/wenda/detail/504332