前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >motan之异步调用

motan之异步调用

作者头像
似水的流年
发布2019-12-07 20:28:15
1.2K0
发布2019-12-07 20:28:15
举报
文章被收录于专栏:电光石火

一、什么是异步调用?  1.同步调用 方法间的调用,假设A方法调用B方法,A方法等待B方法执行完毕后才执行本身,这个同步调用,是具有阻塞式的调用,如果B方法非常耗时,那么整个方法的执行效率将会非常低; 2.异步调用 同样是方法间的调用,假设A方法调用B方法,不同的是A方法调用B方法后,B方法很快的返回给A方法个答复(这个答复不是执行完整个B方法的答复),A方法收到答复后就执行本身,这个是异步调用,不管B方法是否耗时,整体的效率都提升。 二、motan的异步调用入门 1.首先,以入门案例为基础案例改造:http://www.cnblogs.com/Json1208/p/8784906.html 2.motan-api工程HelloWorldService添加注解@MotanAsync 复制代码 package com.motan.service; import com.weibo.api.motan.transport.async.MotanAsync; @MotanAsync public interface HelloWorldService {     String hello(String name); } 复制代码 3.motan-api添加maven插件build-helper-maven-plugin,用来把自动生成类的目录设置为source path 复制代码 <build>         <plugins>             <plugin>                 <groupId>org.codehaus.mojo</groupId>                 <artifactId>build-helper-maven-plugin</artifactId>                 <version>1.10</version>                 <executions>                     <execution>                         <phase>generate-sources</phase>                         <goals>                             <goal>add-source</goal>                         </goals>                         <configuration>                             <sources>                                 <source>${project.build.directory}/generated-sources/annotations</source>                             </sources>                         </configuration>                     </execution>                 </executions>             </plugin>         </plugins>     </build> 复制代码 编译时,Motan自动生成异步service类,生成路径为target/generated-sources/annotations/,生成的类名为service名加上Async,例如service类名为HelloWorldService.java,则自动生成的类名为HelloWorldServiceAsync.java。 另外,需要将motan自动生产类文件的路径配置为项目source path,可以使用maven plugin或手动配置,以上使用maven plugin方式。 这样,我们就能在eclipse中的source folder 中生成HelloWorldServiceAsync.java。 4.motan-client.xml配置的motan:referer标签中配置interface为自动生成的以Async为后缀的对应service类 <motan:referer id="helloWorldReferer" interface="com.motan.service.HelloWorldServiceAsync" directUrl="localhost:8002"/> 5.测试,先启动server,再启动client 复制代码 public class Server {     @SuppressWarnings({ "unused", "resource" })     public static void main(String[] args) {         ApplicationContext applicationContext = new ClassPathXmlApplicationContext("classpath:motan-server.xml");         System.out.println("server start...");     } } log4j:WARN No appenders could be found for logger (org.springframework.core.env.StandardEnvironment). log4j:WARN Please initialize the log4j system properly. server start... 复制代码 复制代码 public class Client {     @SuppressWarnings("resource")     public static void main(String[] args) throws InterruptedException {         ApplicationContext ctx = new ClassPathXmlApplicationContext(new String[]{"classpath:motan-client.xml"});         HelloWorldServiceAsync async = (HelloWorldServiceAsync) ctx.getBean("helloWorldReferer");         System.out.println(async.hello("motan"));     } } log4j:WARN No appenders could be found for logger (org.springframework.core.env.StandardEnvironment). log4j:WARN Please initialize the log4j system properly. Hello motan! 复制代码 最后再来看server的控制台,如果成功调用,会输出方法结果: log4j:WARN No appenders could be found for logger (org.springframework.core.env.StandardEnvironment). log4j:WARN Please initialize the log4j system properly. server start... motan 三、motan异步调用详解 1.使用ResponseFuture接口来接收远程调用结果,ResponseFuture具备future和callback能力 ①.将接口实现修改为: 复制代码 package com.motan.service; public class HelloWorldServiceImpl implements HelloWorldService{     @Override     public String hello(String name) {         try {             Thread.sleep(5000);  System.out.println(name);             System.out.println("等待5s后返回");         } catch (InterruptedException e) {             e.printStackTrace();         }         return "Hello " + name + "!";     } } 复制代码 ②.修改客户端调用为: 复制代码 package com.motan.client; import org.springframework.context.ApplicationContext; import org.springframework.context.support.ClassPathXmlApplicationContext; import com.motan.service.HelloWorldServiceAsync; import com.weibo.api.motan.rpc.ResponseFuture; public class Client {     @SuppressWarnings("resource")     public static void main(String[] args) throws InterruptedException {         ApplicationContext ctx = new ClassPathXmlApplicationContext(new String[]{"classpath:motan-client.xml"});         HelloWorldServiceAsync async = (HelloWorldServiceAsync) ctx.getBean("helloWorldReferer");         ResponseFuture future = async.helloAsync("ResponseFuture");         System.out.println(future.getValue());     } } 复制代码 注意:为了防止接口调用超时,消费端需要配置调用超时时间,在motan-client.xml中配置: <motan:referer id="helloWorldReferer" interface="com.motan.service.HelloWorldServiceAsync" directUrl="localhost:8002" connectTimeout="8000" requestTimeout="8000"/> ③.启动服务端 log4j:WARN No appenders could be found for logger (org.springframework.core.env.StandardEnvironment). log4j:WARN Please initialize the log4j system properly. server start... ④.启动客户端 等待5s后服务端控制台打印: log4j:WARN No appenders could be found for logger (org.springframework.core.env.StandardEnvironment). log4j:WARN Please initialize the log4j system properly. server start... ResponseFuture 等待5s后返回 客户端控制台打印: log4j:WARN No appenders could be found for logger (org.springframework.core.env.StandardEnvironment). log4j:WARN Please initialize the log4j system properly. Hello ResponseFuture! 2.使用FutureListener监听,该监听器可以监听到接口是否成功调用,可以很灵活的判断如果成功调用在输出相关调用返回信息 虽然ResponseFuture带有isDone和isSuccess,但是经过测试,isDone和isSuccess并没办法在异步调用后用于判断,而是得配合FutureListener一起使用: ①.service实现不变,仍然是带有休眠的效果: 复制代码 package com.motan.service; public class HelloWorldServiceImpl implements HelloWorldService{     @Override     public String hello(String name) {         try {             Thread.sleep(5000);             System.out.println(name);             System.out.println("等待5s后返回");         } catch (InterruptedException e) {             e.printStackTrace();         }         return "Hello " + name + "!";     } } 复制代码 ②.使用FutureListener监听server端是否执行成功 复制代码 package com.motan.client; import org.springframework.context.ApplicationContext; import org.springframework.context.support.ClassPathXmlApplicationContext; import com.motan.service.HelloWorldServiceAsync; import com.weibo.api.motan.rpc.Future; import com.weibo.api.motan.rpc.FutureListener; import com.weibo.api.motan.rpc.ResponseFuture; public class Client {     @SuppressWarnings("resource")     public static void main(String[] args) throws InterruptedException {         ApplicationContext ctx = new ClassPathXmlApplicationContext(new String[]{"classpath:motan-client.xml"});         HelloWorldServiceAsync async = (HelloWorldServiceAsync) ctx.getBean("helloWorldReferer");         FutureListener listener = new FutureListener() {             @Override             public void operationComplete(Future future) throws Exception {                 System.out.println("async call "                         + (future.isSuccess() ? "sucess! value:" + future.getValue() : "fail! exception:"                                 + future.getException().getMessage()));             }         };         ResponseFuture future1 = async.helloAsync("motan FutureListener...");         future1.addListener(listener);     } } 复制代码 ③.测试 首先,执行server端启动程序: 复制代码 package com.motan.server; import org.springframework.context.ApplicationContext; import org.springframework.context.support.ClassPathXmlApplicationContext; public class Server {     @SuppressWarnings({ "unused", "resource" })     public static void main(String[] args) {         ApplicationContext applicationContext = new ClassPathXmlApplicationContext("classpath:motan-server.xml");         System.out.println("server start...");     } } log4j:WARN No appenders could be found for logger (org.springframework.core.env.StandardEnvironment). log4j:WARN Please initialize the log4j system properly. server start... 复制代码 接着,启动client端启动程序: 等待5s之后,server控制台输出: log4j:WARN No appenders could be found for logger (org.springframework.core.env.StandardEnvironment). log4j:WARN Please initialize the log4j system properly. server start... motan FutureListener... 等待5s后返回 再来看client控制台输出: log4j:WARN No appenders could be found for logger (org.springframework.core.env.StandardEnvironment). log4j:WARN Please initialize the log4j system properly. async call sucess! value:Hello motan FutureListener...! 注意:在server端休眠的时候,client端是阻塞着的,由于我们超时时间跟上方一致配置的是8s,所以并不会超时,导致client一致阻塞,我们试着把超时实际调为3s(比server休眠时间短): 复制代码 <?xml version="1.0" encoding="UTF-8"?> <beans xmlns="http://www.springframework.org/schema/beans"        xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:motan="http://api.weibo.com/schema/motan"        xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-2.5.xsd        http://api.weibo.com/schema/motan http://api.weibo.com/schema/motan.xsd">     <!-- 具体referer配置。使用方通过beanid使用服务接口类 -->     <motan:referer id="helloWorldReferer" interface="com.motan.service.HelloWorldServiceAsync" directUrl="localhost:8002" connectTimeout="3000" requestTimeout="3000"/> </beans> 复制代码 重新启动server应用程序,server控制台输出: log4j:WARN No appenders could be found for logger (org.springframework.core.env.StandardEnvironment). log4j:WARN Please initialize the log4j system properly. server start... 还未到休眠5s执行结束,client端就抛出一个异常: log4j:WARN No appenders could be found for logger (org.springframework.core.env.StandardEnvironment). log4j:WARN Please initialize the log4j system properly. async call fail! exception:error_message: com.weibo.api.motan.rpc.DefaultResponseFuture task cancel: serverPort=localhost:8002 requestId=1597643022347010049  interface=com.motan.service.HelloWorldService method=hello(java.lang.String) cost=3042, status: 503, error_code: 10001,r=null 最后,server端才把休眠之后的消息打印: log4j:WARN No appenders could be found for logger (org.springframework.core.env.StandardEnvironment). log4j:WARN Please initialize the log4j system properly. server start... motan FutureListener... 等待5s后返回 说明:client使用监听器监听server是否执行完毕,若server实际执行业务的时间在client端配置的接口请求超时时间之内,那么client请求后会一致阻塞着,直到server实际业务执行完成返回;

若server实际执行业务的时间大于client端配置的接口请求超时时间,那么一旦到达超时时间,直接抛出异常。

总结 在异步调用中,如果发起一次异步调用后,立刻使用 future.get() ,则大致和同步调用等同。其真正的优势是在submit 和 future.get() 之间可以混杂一些非依赖性的耗时操作,而不是同步等待,从而充分利用时间片。 另外需要注意,如果异步调用涉及到数据的修改,则多个异步操作直接不能保证 happens-before 原则,这属于并发控制的范畴了,谨慎使用。查询操作则大多没有这样的限制。 在能使用并发的地方使用并发,不能使用的地方才选择同步,这需要我们思考更多细节,但可以最大限度的提升系统的性能。

本文参与 腾讯云自媒体同步曝光计划,分享自作者个人站点/博客。
原始发表:2018-06-05,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
相关产品与服务
日志服务
日志服务(Cloud Log Service,CLS)是腾讯云提供的一站式日志服务平台,提供了从日志采集、日志存储到日志检索,图表分析、监控告警、日志投递等多项服务,协助用户通过日志来解决业务运维、服务监控、日志审计等场景问题。
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档