Dubbo学习笔记4:服务消费端泛化调用与异步调用
本文借用dubbo.learn的Dubbo API方式来解释原理。
服务消费端泛化调用
前面我们讲解到,基于Spring和基于Dubbo API方式搭建简单的分布式系统时,服务消费端引入了一个SDK二方包,里面存放着服务提供端提供的所有接口类,之所以需要引入接口类是因为服务消费端一般是基于接口使用JDK代理实现远程调用的。
泛化接口调用方式主要在服务消费端没有API接口类及模型类元(比如入参和出参的POJO类)的情况下使用。其参数及返回值中没有对应的POJO类,所以所有POJO均转换为Map表示。使用泛化调用时候服务消费模块不再需要引入SDK二方包。
下面基于Dubbo API实现异步调用,在Consumer模块里面TestConsumerApiGeneric是泛化调用的方式,代码如下:
public class TestConsumerApiGeneric(){
public static void main(String[] args) throws IOException{
// 当前应用配置
ApplicationConfig application = new ApplicationConfig();
application.setName("dubboConsumer");
// 连接注册中心配置
RegistryConfig registry = new RegistryConfig();
registry.setAddress("127.0.0.1:2181");
registry.setProtocol("zookeeper");
// 泛型参数设置为GenericService
ReferenceConfig<GenericService> reference = new ReferenceConfig<GenericService>();
reference.setApplication(application);
reference.setRegistry(registry);
reference.setVersion("1.0.0");
reference.setGroup("dubbo");
reference.setTimeout(3000);
// 设置为泛化
reference.setInterface("com.test.UserServiceBo");
reference.setGeneric(true);
// 用com.alibaba.dubbo.rpc.service.GenericService替代所有接口引用
GenericService userService = reference.get();
// 基于类型以及Date/List/Map等不需要转换,直接调用,如果返回值为POJO也将自动转为Map
Object result = userService.$invoke("sayHello",new String[]{"java.lang.String"},new Object[]{"哈哈哈"});
System.out.println(JSON.json(result));
// POJO参数转换为map
Map<String,Object> map = new HashMap<String,Object>();
map.put("class","com.test.PersonImpl");
map.put("name","jiaduo");
map.put("password","password");
result = userService.$invoke("testPojo",new String[]{"com.test.Person"},new Object[]{map});
System.out.println((result));
}
}
上面代码中,由于sayHello的参数是String,没有很好的体现参数转换为Map,下面我们具体来说下POJO参数转换Map的含义。
比如服务提供者提供的一个接口的 testPojo(Person person) 方法的参数为如下所示:
package com.test;
public class PersonImpl implements Person{
private String name;
private String password;
public String getName(){
return name;
}
public void setName(String name){
this.name = name;
}
public String getPassword(){
return password;
}
public void setPassword(String password){
this.password = password;
}
}
则POJO数据:
Person person = new PersonImpl();
person.setName("jiaduo");
person.setPassword("password");
正常情况下调用接口是使用:
servicePerson.testPojo(person);
泛化调用下需要首先转换person为Map,如下表示:
Map<String,Object> map = new HashMap<String,Object>();
// 注意:如果参数类型是接口,或者List等丢失泛型,可通过class属性指定类型。
map.put("class","com.test.PersonImpl");
map.put("name","jiaduo");
map.put("password","password");
然后使用下面方法进行泛化调用:
servicePerson.$invoke("testPojo",new String[]{"com.test.Person"},new Object[]{map});
泛化调用通常用于框架集成,比如:实现一个通用的服务测试框架,可通过GenericService调用所有服务实现,而不需要依赖服务实现方提供的接口类以及接口的入参和出参的POJO类。
服务消费端异步调用
无论前面我们讲解的正常调用还是泛化调用,都是同步调用,也就是服务消费方发起一个远程调用后,调用线程要被阻塞挂起,直到服务提供方返回。
本节讲解下服务消费端异步调用,异步调用是指服务消费方发起一个远程调用后,不等服务提供方返回结果,调用方法就返回了,也就是当前线程不会被阻塞,这就允许调用方同时调用多个远程方法。
在Consumer模块里面TestConsumerAsync是泛化调用,代码如下:
public class TestConsumerAsync{
public static void main(String[] args) throws InterruptedException,ExecutionException{
// 当前应用配置
ApplicationConfig application = new ApplicationConfig();
application.setName("dubboConsumer");
// 连接注册中心配置
RegistryConfig registry = new RegistryConfig();
registry.setAddress("127.0.0.1:2181");
registry.setProtocol("zookeeper");
// 引用远程服务
ReferenceConfig<UserServiceBo> reference = new ReferenceConfig<UserServiceBo>();
reference.setApplication(application);
reference.setRegistry(registry);
reference.setInterface(UserServiceBo.class);
reference.setVersion("1.0.0");
reference.setGroup("dubbo");
reference.setTimeout(3000);
// (1)设置为异步调用
reference.setAsync(true);
// 和本地bean一样使用xxxService
UserServiceBo userService = reference.get();
long startTime = System.currentTimeMillis() / 1000;
// (2)因为异步调用,此处返回null
System.out.println(userService.sayHello("哈哈哈"));
// 拿到调用的Future引用,当结果返回后,会被通知和设置到此Futrue
Future<String> userServiceFutureOne = RpcContext.getContext().getFuture();
// (3)因为异步调用,此处返回null
System.out.println(userService.sayHello2("哈哈哈2"));
// 拿到调用的Future引用,当结果返回后,会被通知和设置到此Future
Future<String> userServiceFutureTwo = RpcContext.getContext().getFuture();
// (4)阻塞到get方法,等待结果返回
System.out.println(userServiceFutureOne.get());
System.out.println(userServiceFutureTwo.get());
long endTime = System.currentTimeMillis() / 1000;
System.out.println("costs:" + (endTime - startTime));
}
}
运行上面代码,输出如下图所示:

其中代码(2)(3)处输出null,说明开启异步调用后调用方直接返回null。
输出costs:2说明异步调用生效了,因为sayHello和sayHello2方法内都sleep了2s,如果是顺序调用则会耗时至少4s,这里耗时2s说明两次调用是并发进行的。
异步调用是基于nio的非阻塞实现并行调用,客户端不需要启动多线程即可完成并行调用多个远程服务,相对调用不同的服务使用不同线程来说开销较小。
Dubbo学习笔记4:服务消费端泛化调用与异步调用的更多相关文章
- Angular JS 学习笔记(自定义服务:factory,Promise 模式异步请求查询:$http,过滤器用法filter,指令:directive)
刚学没多久,作了一个小项目APP,微信企业号开发与微信服务号的开发,使用的是AngularJS开发,目前项目1.0版本已经完结,但是项目纯粹为了赶工,并没有发挥AngularJS的最大作用,这几天项目 ...
- Dubbo学习笔记0:RPC框架Dubbo介绍
整体来说,一个公司业务系统的演进流程基本都是从单体应用到多应用.在单体应用时,不同业务模块相互调用直接在本地JVM进程内就可以完成,而变为多个应用时,相互之间进行通信就不能简单的进行本地调用了,因为不 ...
- Dubbo学习笔记2:Dubbo服务提供端与消费端应用的搭建
Demo结构介绍 Demo使用Maven聚合功能,里面有三个模块,目录如下: 其中Consumer模块为服务消费者,里面TestConsumer和consumer.xml组成了基于Spring配置方式 ...
- Dubbo学习笔记10:Dubbo服务消费方启动流程源码分析
同理我们看下服务消费端启动流程时序图: 在<Dubbo整体架构分析>一文中,我们提到服务消费方需要使用ReferenceConfig API来消费服务,具体是调用代码(1)get()方法来 ...
- 源码分析Dubbo服务消费端启动流程
通过前面文章详解,我们知道Dubbo服务消费者标签dubbo:reference最终会在Spring容器中创建一个对应的ReferenceBean实例,而ReferenceBean实现了Spring生 ...
- Dubbo学习笔记1:使用Zookeeper搭建服务治理中心
Zookeeper是Apache Hadoop的子项目,是一个树形的目录服务,支持变更推送,适合作为Dubbo服务的注册中心,工业强度较高,推荐生成环境使用. , 下面结合上图介绍Zookeeper在 ...
- dubbo源码之服务消费
消费端启动初始化过程: 消费端的代码解析也是从配置文件解析开始的,服务发布对应的<dubbo:service,解析xml的时候解析了一个ServiceBean,并且调用ServiceConfig ...
- Dubbo学习笔记11:使用Dubbo中需要注意的一些事情
指定方法异步调用 前面我们讲解了通过设置ReferenceConfig的setAsync()方法来让整个接口里的所有方法变为异步调用,那么如何指定某些方法为异步调用呢?下面讲解下如何正确地设置默写方法 ...
- Dubbo学习笔记7:Dubbo的集群容错与负载均衡策略
Dubbo的集群容错策略 正常情况下,当我们进行系统设计时候,不仅要考虑正常逻辑下代码该如何走,还要考虑异常情况下代码逻辑应该怎么走.当服务消费方调用服务提供方的服务出现错误时候,Dubbo提供了多种 ...
随机推荐
- 新员工入门 - for测试
23456人员介绍 XXX 测试工作 [软件] Chrome 浏览器.jsonviewer.Firefox.FireBug HTTP协议与抓包 - fildder.wireshirk等 DB查询工具 ...
- 架构师修练 I - 超级代码控
可实现的是架构,空谈是概念 So don't tell me the concepts show me the code! “不懂编码的架构师不是好架构师” 好架构师都是超级代码控. 代码是最好 ...
- 【Android UI设计与开发】第04期:引导界面(四)仿人人网V5.9.2最新版引导界面
这一篇我将会以人人网的引导界面为实例来展开详细的讲解,人人网的引导界面比较的新颖,不同于其他应用程序千篇一律的靠滑动来引导用户,而是以一个一个比较生动形象的动画效果展示在用户们的面前,有一种给人眼前一 ...
- 利用JS实现一个简单的二级联动菜单
前几天在看js的相关内容,所以就简单写了一个二级联动菜单.分享一下. <!DOCTYPE html> <html lang="en"> <head&g ...
- Centos 7 安装mysql5.7.24二进制 版本
Mysql 二进制安装方法 下载mysql https://dev.mysql.com/downloads/mysql/ 1.解压包 tar xf mysql-5.7.24-linux-glibc2. ...
- ECS centos7 使用外部邮件服务商的465加密端口
ECS centos7 使用外部邮件服务商的465加密端口发送邮件. 1.修改/etc/mail.rc 文件中添加以下的 set smtp="smtps://smtp.163.com:465 ...
- 代理神器allproxy
背景 allproxy意为all as proxy,即是说所有设备均可以成为一个网络代理,唯一的要求就是有网络访问权限. 一般的代理软件要求宿主机必须有公网地址,然后才能把网络代理出去,但在实际情况下 ...
- Java设置PPT的扇形图,与内嵌Excel联动
/** * 设置饼图的主方法 * @param slide 图表 * @param index 图标位置 * @param data 需要设置的数据 * @param titles 关联Excel的标 ...
- Java 笔记——在 IDEA 中使用 Maven 配置和使用 MyBatis
1.前言 MyBatis 是什么? MyBatis 是一款优秀的持久层框架,它支持定制化 SQL.存储过程以及高级映射. MyBatis 避免了几乎所有的 JDBC 代码和手动设置参数以及获取结果集. ...
- Installing OpenSSH from the Settings UI on Windows Server 2019 or Windows 10 1809
Installing OpenSSH from the Settings UI on Windows Server 2019 or Windows 10 1809 OpenSSH client and ...