官方文档:泛化调用(客户端泛化) | Apache Dubbo
官方dubbo项目案例:dubbo-samples/dubbo-samples-generic at master · apache/dubbo-samples · GitHub
下载下案例源码,本地启动下zk即可debug调试
泛化调用主要用于实现一个通用的远程服务 Mock 框架,可通过实现 GenericService 接口处理所有服务请求。比如如下场景:
网关服务:如果要搭建一个网关服务,那么服务网关要作为所有RPC服务的调用端。但是网关本身不应该依赖于服务提供方的接口API(这样会导致每有一个新的服务发布,就需要修改网关的代码以及重新部署),所以需要泛化调用的支持。
测试平台:如果要搭建一个可以测试RPC调用的平台,用户输入分组名、接口、方法名等信息,就可以测试对应的RPC服务。那么由于同样的原因(即会导致每有一个新的服务发布,就需要修改网关的代码以及重新部署),所以平台本身不应该依赖于服务提供方的接口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
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接口的实现
- // 实体类
- public class User implements Serializable {
- private static final long serialVersionUID = 4741417427705290705L;
- private String name;
- private int gender;
- //getter & setter & constructor
- }
-
- // 实体类
- public class PageReq
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
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);
- }