官方文档:泛化调用(客户端泛化) | Apache Dubbo
官方dubbo项目案例:dubbo-samples/dubbo-samples-generic at master · apache/dubbo-samples · GitHub
下载下案例源码,本地启动下zk即可debug调试
使用场景
泛化调用主要用于实现一个通用的远程服务 Mock 框架,可通过实现 GenericService 接口处理所有服务请求。比如如下场景:
-
网关服务:如果要搭建一个网关服务,那么服务网关要作为所有RPC服务的调用端。但是网关本身不应该依赖于服务提供方的接口API(这样会导致每有一个新的服务发布,就需要修改网关的代码以及重新部署),所以需要泛化调用的支持。
-
测试平台:如果要搭建一个可以测试RPC调用的平台,用户输入分组名、接口、方法名等信息,就可以测试对应的RPC服务。那么由于同样的原因(即会导致每有一个新的服务发布,就需要修改网关的代码以及重新部署),所以平台本身不应该依赖于服务提供方的接口API。所以需要泛化调用的支持
dubbo泛化调用主要API
com.alibaba.dubbo.config.RegistryConfig; //注册中心配置 com.alibaba.dubbo.config.ApplicationConfig; //应用配置 com.alibaba.dubbo.config.ReferenceConfig; //引用的服务配置(类似reference注解或xml配置) com.alibaba.dubbo.rpc.service.GenericService;//实际调用的服务(泛化服务)
其中com.alibaba.dubbo.rpc.service.GenericService#$invoke是泛化调用方法
其中com.alibaba.dubbo.rpc.service.GenericService#$invoke
是泛化调用方法
方法入参分别为方法名称、参数类型数组、入参对象数组
GenericService只有一个方法Object$invoke(String method,String[] parameterTypes,Object[] args)throwsGenericException;invoke接受三个参数,分别为方法名,参数类型组以及参数值组,其中参数类型和参数值一一对应。
GenericService的实例化是通过ReferenceConfig的单例调用方法得到的。ReferenceConfig配置了诸如服务接口名等参数。需要注意的是,在使用泛化调用时,generic属性要置为true
package com.alibaba.dubbo.rpc.service;
public interface GenericService {
/**
* Generic invocation
*
* @param method Method name, e.g. findPerson. If there are overridden methods, parameter info is
* required, e.g. findPerson(java.lang.String)
* @param parameterTypes Parameter types
* @param args Arguments
* @return invocation return value
* @throws Throwable potential exception thrown from the invocation
*/
Object $invoke(String method, String[] parameterTypes, Object[] args) throws GenericException;
}
GenericService的实例化实例化案例:
public static void main(String[] args) throws Exception {
ApplicationConfig applicationConfig = new ApplicationConfig();
applicationConfig.setName("generic-call-consumer");
RegistryConfig registryConfig = new RegistryConfig();
registryConfig.setAddress("zookeeper://127.0.0.1:2181");
ReferenceConfig<GenericService> referenceConfig = new ReferenceConfig<>();
referenceConfig.setInterface("org.apache.dubbo.samples.generic.call.api.HelloService");
applicationConfig.setRegistry(registryConfig);
referenceConfig.setApplication(applicationConfig);
referenceConfig.setGeneric(true);
referenceConfig.setAsync(true);
referenceConfig.setTimeout(7000);
genericService = referenceConfig.get();
}
调用流程:
调用 <-> GenericImplFilter <-> 网络 <-> GenericFilter <-> 服务实现
其中在GenericImplFilter中会在Consumer端,对泛化调用相关参数重新构建和处理,使用invoke.invoke发起调用
然后会调用服务端的对应的接口和方法,会被GenericFilter拦截
注意:GenericImplFilter并不是GenericFilter的实现,他们两个是Filter接口的实现
参数封装案例:如果是多重对象参数,可以传入json,例如
// 实体类
public class User implements Serializable {
private static final long serialVersionUID = 4741417427705290705L;
private String name;
private int gender;
//getter & setter & constructor
}
// 实体类
public class PageReq<T> implements Serializable {
private static final long serialVersionUID = -6720015744986291779L;
private int pageSize;
private int pageNum;
private T queryParam;
//getter & setter & constructor
}
// 接口
public interface TestGenericService {
String genericTypeParam(PageReq<User> userReq);
}
// 参数封装,并调用
public static void main(String[] args) {
//方法名
String methodName = "genericTypeParam";
//参数类型
String[] parameterTypes = new String[1]; //1个参数类型
parameterTypes[0] = "service.PageReq"; //参数类的全限定名
//参数对象
Object[] objectParams = new Object[1]; //1个参数值
JSONObject paramPage = new JSONObject(); //看作是对象转换为JSONObject的过程
paramPage.put("pageNum", 10);
paramPage.put("pageSize", 1);
JSONObject paramUser = new JSONObject();
paramUser.put("name", "nicolimine");
paramUser.put("gender", 1);
paramUser.put("class", "service.User");//注意这里,PojoUtils解析Map类型,如果存在key=class时,会直接指定该Object类型
paramPage.put("queryParam", paramUser);
objectParams[0] = paramPage;
//执行泛化调用,genericService如何实例化可以参考官方案例
genericService.$invoke(methodName, parameterTypes, objectParams);
}