一、什么是異步調用?
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端配置的接口請求超時時間,那么一旦到達超時時間,直接拋出異常。