学习目标:
通过纯java代码实现dubbo的调用,只涉及消费者部分调用。
tips:不涉及spring的版本
学习内容:
本人通过dubbo的泛化调用,结合网上的一些参考文档,总结了本文章。
dubbo中文文档:https://cn.dubbo.apache.org/zh/docs3-v2/java-sdk/advanced-features-and-usage/service/generic-reference/
参考其他大佬分享的文档:https://zhuanlan.zhihu.com/p/68903627
学习时间:
在开发中,有的时候苦于API没有实时同步或者其他因素,导致mock的时候会有各种奇葩问题,经过学习,发现可以用泛化调用完成dubbo的测试。
具体做法如下:
1、先设计开发通用的调用接口和请求类,与生产者那边保持一致(包结构)
2、设计一个dubbo的utils类,用于实际的调用
在设置 ReferenceConfig 时,使用 setGeneric("true") 来开启泛化调用
在设置 ReferenceConfig 时,使用 setGeneric("true") 来开启泛化调用
配置完 ReferenceConfig 后,使用 referenceConfig.get() 获取到 GenericService 类的实例
使用其 $invoke 方法获取结果
其他设置与正常 Api 服务启动一致即可
public class DubboUtils {
//todo 正常,zk、host、groupId、version都是从springcontext中获取的
private static final String zk = "zookeeper://127.0.0.1:2181";
/**
* 获取Dubbo服务
* @param dubboService
* @param version
* @param host
* @param groupId
* @param <T>
* @return
*/
public static <T> T getService(Class dubboService, String version, String host,String groupId) {
Enhancer enhancer = new Enhancer();
enhancer.setSuperclass(dubboService);
//手动实现泛化调用
enhancer.setCallback((MethodInterceptor) (obj, method, args, proxy) -> {
ReferenceConfig<GenericService> referenceConfig = getReferenceConfig(dubboService, version, host,groupId);
GenericService genericService = referenceConfig.get();
//$invoke方法 参数说明:调用的远程方法名、参数类型数组、参数列表数组
return genericService.$invoke(method.getName(), getMethodParamType(method), args);
});
return (T) enhancer.create();
}
private static <T> ReferenceConfig<T> getReferenceConfig(Class<?> clazz, String version, String host, String groupId) {
ReferenceConfig<T> referenceConfig = new ReferenceConfig<>();
ApplicationConfig applicationConfig = new ApplicationConfig();
applicationConfig.setName(clazz.getName());
referenceConfig.setApplication(applicationConfig);
//远程调用的dubbo地址 dubbo://1270.0.1:8105
referenceConfig.setUrl(host);
//dubbo的api调用接口
referenceConfig.setInterface(DubboService.class);
//开启泛化调用
referenceConfig.setGeneric(Constants.GENERIC_SERIALIZATION_DEFAULT);
//版本号
referenceConfig.setVersion(version);
//超时时间
referenceConfig.setTimeout(20000);
//dubbo分组
referenceConfig.setGroup(groupId);
RegistryConfig registryConfig = new RegistryConfig();
//zk地址
registryConfig.setAddress(zk);
referenceConfig.setRegistry(registryConfig);
return referenceConfig;
}
/**
* 获取method中的所有 参数类型
* @param method
* @return
*/
private static String[] getMethodParamType(Method method) {
Class<?>[] parameterTypes = method.getParameterTypes();
String[] paramTypeArray = new String[parameterTypes.length];
for (int i = 0; i < parameterTypes.length; i++) {
paramTypeArray[i] = parameterTypes[i].getTypeName();
}
return paramTypeArray;
}
}
对部分内容做补充说明:
我只是将核心代码贴出,至于参数中的zk地址、version管理、groupId分组、具体你请求的生产者模块的dubbo地址,都根据实际业务或者具体学习情况进行调整,参数列表灵活进行修改即可。
其次,大家也应该发现了,$invoke方法使用cglig这种方式进行调用的,官网给的是用 CompletableFuture 这种方式进行的,也就是异步回调响应结果,因人而异,条条大路通罗马嘛。
还有就是我省略了生产者模块以及注册zk的那部分代码,大家可以去官网一览。
3、编写测试类
这里注意,host地址,要将对应的api接口也就是DubboService的包路径也带上,DubboRequest也要保证和生产者的DubboRequest包路径一致,因为哦都是通过反射最后去获取嘛,你想想mybatis的xml映射,mapper放的路径不也得和dao层的mapper一致嘛。一个道理。
因为DubboService、DubboRequest里面东西比较少,我就不贴了,大家根据实际情况自己从项目的api直接拿过来就行了。
public static void main(String[] args) {
//BankDubboService是已经注册到zookeeper注册中心的Dubbo服务,这里不指定调用特定服务实例
BankDubboService service = DubboUtils.getService(DubboService.class, "1.0.0","dubbo://127.0.0.1:38105/cn.sk.dubbo.DubboService","groupId");
DubboRequest dubboRequest = new DubboRequest();
//todo 根据实际情况赋予req内容即可
dubboRequest.setXmlRequest(reqXml.toString());
Map<String,Object> DubboResponse = service.doSomething(dubboRequest);
System.out.println(DubboResponse.get("result"));
}
注意事项:
这里我是用的Map<String,Object>去接收的,因为使用泛化调用,对于自定义bean是有一些特殊处理的,我暂时没有做过多的研究,官网给的说明如下:
1、如果参数为基本类型或者 Date,List,Map 等,则不需要转换,直接调用。
2、如果参数为其他 POJO,则使用 Map 代替。
3、对于其他序列化格式,需要特殊配置
可以看到,我的DubboResponse就是其他POJO的,所以dubbo调用完成后会直接用map去解析,获取里面的值也是通过map.get(key)来完成的。
如果强行用自己的response接受的话,会报错map无法转化为对应的实体类,已经替大家跳过坑了,不用再试了。。。
简单了解了一下,可以借助重写Filter实现自定义序列化,这个后面看时间安排补充吧,大家可以先自行百度学习~
到此完成consumer端的泛化版dubbo调用,可以快速的做mock等测试了,当然一切以实际为主哈。
文中难免有些理解不到位的,说的不太清楚的地方,欢迎大家一起讨论研究~