现在分布式架构盛行,RPC框架也被大家熟知,目前就国内而言,dubbo可以说是中小型企业的首选。我们先看一下官网上的一张图:
这张图从架构的角度展现了,dubbo框架的关键组件和实现方式。那么我们在日常开发中的使用方式大概如下图:
也就是说服务提供者,有一个interface(也叫client),还有一个服务实现,interface给消费者依赖。这样做没问题,我们分析一下,这种方式有优点也会优缺点:
优点:1)消费端像调用本地服务一样调用远程服务,使用简单
缺点:1)服务端interface会带过来一些依赖可能和消费端某些依赖冲突 2)服务端interface也许会把自己的配置传递到消费端,导致 消费端平白无故要多配置完全和自己无关或者不关心的项
假如我有一下这些场景:
1)消费端只依赖了服务端一个服务(或者说很少)
2)消费端不想把interface的依赖带过来,导致需要花费人力成本去排包,让依 赖变得特别重
3)消费端只依赖服务,只关心它所以来的服务调用方式和返回结果,完全不关心你服务怎么实现的,你依赖什么配置,你把这些配置传递给我,我是无法接受的
基于上述问题和描述的应用场景,dubbo提供了泛化引用。
何谓泛化引用?
官网给了如下解释:
泛化接口调用方式主要用于客户端没有 API 接口及模型类元的情况,参数及返回值中的所有 POJO 均用 Map 表示,通常用于框架集成,比如:实现一个通用的服务测试框架,可通过 GenericService 调用所有服务实现。
我自己的理解是,不直接依赖服务提供者,利用dubbo框架层的特性来实现远程服务调用:
接下来就一步一步实现dubbo服务的泛型引用。
I 服务端编写与启动
服务端结构如下图:
具体实现参考徒手搭建dubbo服务
II 消费端编写与测试
消费端工程如下图:
1)dubbo-consumer-interface层定义service和响应service:
public interface ImitateConsumerService {
/**
* 客户端查询用户信息
*
* @param id
* @return
*/
ResponseBase doQueryUserById(Long id);
}
response:
/**
* 响应
*
* @author Typhoon
*
*/
public class ResponseBase implements Serializable {
private static final long serialVersionUID = 7825036327382479394L;
public static final String SUCCESS = "4001";
public static final String ERROR_CODE = "4000";
private String code = "4001";
private String reason;
private String result;
private Object content;
······
}
2)dubbo-consumer-provider编写服务引用与测试
consumer只依赖基本的dubbo和spring。dubbo泛化引用有两种实现方式:
①通过 Spring 使用泛化调用
<dubbo:reference interface="com.typhoon.service.UserService" url="dubbo://localhost:20289" id="userService" protocol="dubbo" timeout="30000" generic="true" />
服务引用具体实现
@Service("imitateConsumerService")
public class ImitateConsumerServiceImpl implements ImitateConsumerService,ApplicationContextAware {
private GenericService genericService ;
@Override
public ResponseBase doQueryUserById(Long id) {
ResponseBase resp = new ResponseBase();
String methodName = "queryByPK";
String[] paramTypes = new String[1];
paramTypes[0] = "java.lang.Long";
Object[] params = new Object[1];
params[0] = id;
Object result = this.genericService.$invoke(methodName,paramTypes,params);
resp.setAttach(result);
return resp;
}
@Override
public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
if(null == applicationContext) {
throw new IllegalArgumentException("applicationContext未注入");
}
this.genericService = (GenericService)applicationContext.getBean("userService");
}
}
将dubbo-server端启动,在消费端编写单元测试
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration({"classpath:spring-root.xml"})
public class Test1 {
@Resource
ImitateConsumerService imitateConsumerService;
@Test
public void testA() {
try {
ResponseBase resp = this.imitateConsumerService.doQueryUserById(1L);
System.out.println(JSON.toJSONString(resp));
} catch (Exception e) {
e.printStackTrace();
}
}
}
运行单元测试
②通过 API 方式使用泛化调用
改造①中ImitateConsumerServiceImpl的setApplicationContext方法:
再次运行①中的单元测试,结果如下:
可以看到两种方式都实现了泛型引用调用rpc服务的效果。
泛化引用应用场景
1)服务端暴露client层比较重,富客户端或者带过来很多配置和依赖
2)消费端对服务端依赖比较轻,依赖单个或很少个服务,不想承担引入服务端接口带来的成本开销
3)只关心服务,完全不关心服务端实现
泛化实现
本篇幅重点讲述了dubbo泛化引用,但是还有泛化实现的概念,泛化实现在真是项目场景中应用不是太多,反正我是尚未见过。此处简单过一下。
官网解释:
泛接口实现方式主要用于服务器端没有API接口及模型类元的情况,参数及返回值中的所有POJO均用Map表示,通常用于框架集成,比如:实现一个通用的远程服务Mock框架,可通过实现GenericService接口处理所有服务请求。
在 Java 代码中实现 GenericService 接口:
package com.foo;
public class MyGenericService implements GenericService {
public Object $invoke(String methodName, String[] parameterTypes, Object[] args) throws GenericException {
if ("sayHello".equals(methodName)) {
return "Welcome " args[0];
}
}
}
同样,泛化实现也有两种实现方式:
1)通过 Spring 暴露泛化实现
在 Spring 配置申明服务的实现:
<bean id="genericService" class="com.foo.MyGenericService" />
<dubbo:service interface="com.foo.BarService" ref="genericService" />
2)通过 API 方式暴露泛化实现
...
// 用com.alibaba.dubbo.rpc.service.GenericService可以替代所有接口实现
GenericService xxxService = new XxxGenericService();
// 该实例很重量,里面封装了所有与注册中心及服务提供方连接,请缓存
ServiceConfig<GenericService> service = new ServiceConfig<GenericService>();
// 弱类型接口名
service.setInterface("com.xxx.XxxService");
service.setVersion("1.0.0");
// 指向一个通用服务实现
service.setRef(xxxService);
// 暴露及注册服务
service.export();
总结
此篇我们根据真实业务场景讲解了dubbo泛化引用和泛化实现,希望给大家在日常开发中带来帮助!