3.motan之異步調用


一、什么是異步調用?


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

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM