该文章内容发布已经超过一年,请注意检查文章中内容是否过时。
Dubbo的泛化调用
以下几种场景可以考虑使用泛化调用:
- 服务测试平台
- API 服务网关
泛化调用主要用于消费端没有 API 接口的情况;不需要引入接口 jar 包,而是直接通过 GenericService 接口来发起服务调用,参数及返回值中的所有 POJO 均用 Map
表示。泛化调用对于服务端无需关注,按正常服务进行暴露即可。
下面来看看消费端如何使用泛化调用进行服务调用。
通过 Spring XML 配置进行泛化调用
在 Spring 配置申明 generic="true"
,如:
<dubbo:reference id="userService" interface="com.alibaba.dubbo.samples.generic.api.IUserService" generic="true"/>
需要使用的地方,通过强制类型转化为 GenericService 进行调用:
GenericService userService = (GenericService) context.getBean("userService");
// primary param and return value
String name = (String) userService.$invoke("delete", new String[]{int.class.getName()}, new Object[]{1});
System.out.println(name);
其中:
- GenericService 这个接口只有一个方法,名为
$invoke
,它接受三个参数,分别为方法名、方法参数类型数组和参数值数组; - 对于方法参数类型数组
- 如果是基本类型,如 int 或 long,可以使用
int.class.getName()
获取其类型; - 如果是基本类型数组,如 int[],则可以使用
int[].class.getName()
; - 如果是 POJO,则直接使用全类名,如
com.alibaba.dubbo.samples.generic.api.Params
。
- 如果是基本类型,如 int 或 long,可以使用
通过 API 编程进行泛化调用
ApplicationConfig application = new ApplicationConfig();
application.setName("api-generic-consumer");
RegistryConfig registry = new RegistryConfig();
registry.setAddress("zookeeper://127.0.0.1:2181");
application.setRegistry(registry);
ReferenceConfig<GenericService> reference = new ReferenceConfig<GenericService>();
// 弱类型接口名
reference.setInterface("com.alibaba.dubbo.samples.generic.api.IUserService");
// 声明为泛化接口
reference.setGeneric(true);
reference.setApplication(application);
// 用com.alibaba.dubbo.rpc.service.GenericService可以替代所有接口引用
GenericService genericService = reference.get();
String name = (String) genericService.$invoke("delete", new String[]{int.class.getName()}, new Object[]{1});
System.out.println(name);
通过 API 的方式,不需要像 XML 的方式需要提前将服务配置好,可以动态构建 ReferenceConfig;相对 XML 来说,API 的方式更常见。
参数或返回值是 POJO 的场景
比如方法签名是 User get(Params params);
其中 User 有 id 和 name 两个属性,Params 有 query 一个属性。
以下是消费端的调用代码:
String[] parameterTypes = new String[]{"com.alibaba.dubbo.samples.generic.api.Params"};
Map<String, Object> params = new HashMap<String, Object>();
param.put("class", "com.alibaba.dubbo.samples.generic.api.Params");
param.put("query", "a=b");
Object user = userService.$invoke("get", parameterTypes, new Object[]{param});
System.out.println("sample one result: " + user);
上述代码的输出结果为:
sample one result: {name=charles, id=1, class=com.alibaba.dubbo.samples.generic.api.User}
这里,Dubbo 框架会自动将 POJO 的返回值转换成 Map。可以看到,返回值 user
是一个 HashMap,里面分别存放了 name、id、class 三个 k/v。
泛接口实现
泛接口实现方式主要用于服务端没有 API 接口的情况,参数及返回值中的所有 POJO 均用 Map 表示,通常用于框架集成,如实现一个通用的远程服务 Mock 框架,可通过实现 GenericService 接口处理所有服务请求。
服务端实现 GenericService
public class GenericServiceImpl implements GenericService {
@Override
public Object $invoke(String method, String[] parameterTypes, Object[] args) throws GenericException {
if (method.equals("hi")) {
return "hi, " + args[0];
} else if (method.equals("hello")) {
return "hello, " + args[0];
}
return "welcome";
}
}
服务端暴露服务
ApplicationConfig application = new ApplicationConfig();
application.setName("api-generic-provider");
RegistryConfig registry = new RegistryConfig();
registry.setAddress("zookeeper://127.0.0.1:2181");
application.setRegistry(registry);
GenericService genericService = new GenericServiceImpl();
ServiceConfig<GenericService> service = new ServiceConfig<GenericService>();
service.setApplication(application);
service.setInterface("com.alibaba.dubbo.samples.generic.api.HelloService");
service.setRef(genericService);
service.export();
ServiceConfig<GenericService> service2 = new ServiceConfig<GenericService>();
service2.setApplication(application);
service2.setInterface("com.alibaba.dubbo.samples.generic.api.HiService");
service2.setRef(genericService);
service2.export();
同样,也可以使用 XML 配置的方式暴露服务;此时服务端是没有依赖 HiService 和 HelloService 这两个接口的。
消费端进行服务调用
ApplicationConfig application = new ApplicationConfig();
application.setName("api-generic-consumer");
RegistryConfig registry = new RegistryConfig();
registry.setAddress("zookeeper://127.0.0.1:2181");
application.setRegistry(registry);
ReferenceConfig<GenericService> reference = new ReferenceConfig<GenericService>();
// 弱类型接口名
reference.setInterface(HiService.class);
reference.setApplication(application);
HiService hiService = (HiService) reference.get();
System.out.println(hiService.hi("dubbo"));
ReferenceConfig<GenericService> reference2 = new ReferenceConfig<GenericService>();
// 弱类型接口名
reference2.setInterface(HelloService.class);
reference2.setApplication(application);
HelloService helloService = (HelloService) reference2.get();
System.out.println(helloService.hello("community"));
同样,消费端也可以使用 XML 配置的方式引用服务,然后进行调用。这里可以看到调用方式为普通的服务调用,并非泛化调用。当然使用泛化调用也是可以的。
到这里为止,一个简易的服务 Mock 平台就成功上线了!
其他
- 本文介绍的泛化调用和泛接口实现,都是在原生的
Dubbo
协议之上的。在 2.6.2 版本之前,其他协议如 http/hessian 等是不支持泛化调用的,2.6.3 版本将会对这两个协议的泛化调用做支持。 - 本文中提到的相关示例代码可以在 dubbo-samples中找到:https://github.com/apache/dubbo-samples/tree/master/dubbo-samples-generic