zoukankan      html  css  js  c++  java
  • 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端配置的接口请求超时时间,那么一旦到达超时时间,直接抛出异常。

  • 相关阅读:
    BZOJ_2661_[BeiJing wc2012]连连看_费用流
    BZOJ_4867_[Ynoi2017]舌尖上的由乃_分块+dfs序
    BZOJ_3238_[Ahoi2013]差异_后缀自动机
    BZOJ_3207_花神的嘲讽计划Ⅰ_哈希+主席树
    [转载]快速幂与矩阵快速幂
    ACM的一点基础知识
    [转载]C++STL—vector的插入与删除
    C++STL—map的使用
    [转载]汇编语言assume伪指令的作用
    [转载]c++中的位运算符
  • 原文地址:https://www.cnblogs.com/Json1208/p/8799370.html
Copyright © 2011-2022 走看看