zoukankan      html  css  js  c++  java
  • SpringBoot多线程请求数据之后拼接返回

    SpringBoot多线程请求数据之后拼接返回

    假设现在有这样一个场景,我需要实现一个接口满足以下功能:

    1、需要从A接口取一些数据

    2、需要从B接口取一些数据

    3、将两个接口取到的数据进行拼接返回给前端。

    4、假设第一个接口需要10秒查询时间,第二个接口需要7秒,但是需求是只能在15秒内返回给前端。

    如果不用多线程明显会超时,不满足需求。那只能用多线程取分别请求A和B接口的数据,然后把得到的数据进行拼接,返回给前端。

    现在有这么几个类

    RestRequestDataA,RestRequestDataB,ShowData分别表示从A接口取得的数据,从B接口取得的数据,拼接之后返回给前端的数据。

    这三个类的定义如下

    class ShowData{
        private RestRequestDataA name;
        private RestRequestDataB value;
    
        public ShowData(RestRequestDataA name, RestRequestDataB value) {
            this.name = name;
            this.value = value;
        }
    
        @Override
        public String toString() {
            return "ShowData{" +
                    "name=" + name.getName() +
                    ", value=" + value.getValue() +
                    '}';
        }
    }
    
    class RestRequestDataA{
        private String name;
    
        public RestRequestDataA(String name) {
            this.name = name;
        }
    
        public String getName() {
            return name;
        }
    
    
    }
    
    class RestRequestDataB{
        private String value;
    
        public RestRequestDataB(String value) {
            this.value = value;
        }
    
        public String getValue() {
            return value;
        }
    
    
    }
    
    

    省了set方法


    解决代码

    /**
     * @author wx
     * @date 2021/7/9
     */
    public class CompletableFutureTest {
        public static void main(String[] args) throws ExecutionException, InterruptedException {
            Date startDate = new Date();
            CountDownLatch countDownLatch = new CountDownLatch(2);
            FutureTask<RestRequestDataA> futureTask1 = new FutureTask<>(new Callable<RestRequestDataA>() {
                @Override
                public RestRequestDataA call() throws Exception {
                    // 假设这里是请求接口A的调用,花费十秒
                    TimeUnit.SECONDS.sleep(10);
                    countDownLatch.countDown();
                    return new RestRequestDataA("myname");
                }
            });
            new Thread(futureTask1).start();
    
            FutureTask<RestRequestDataB> futureTask2 = new FutureTask<>(new Callable<RestRequestDataB>() {
                @Override
                public RestRequestDataB call() throws Exception {
                    // 假设这里是请求接口B的调用,花费七秒
                    TimeUnit.SECONDS.sleep(7);
                    countDownLatch.countDown();
                    return new RestRequestDataB("myvalue");
                }
            });
            new Thread(futureTask2).start();
    
            countDownLatch.await();
            // A,B接口数据到了,可以开始拼接了
            ShowData showData = new ShowData(futureTask1.get(),futureTask2.get());
            System.out.println(showData); //拼接之后的数据
            Date endDate = new Date();
            System.out.println(String.format("一共花费%d毫秒",endDate.getTime()-startDate.getTime()));
    
    
        }
    
    }
    

    接下来我们来捋一下这个代码

    主要是这个四个类:CountDownLatch, FutureTask, Runnable, Future

    首先CountDownLatch,latch英文翻译为门栓,这个类的用处在于,确保了A接口和B接口请求的数据都拿到了,再进行拼接。具体我就不展开讲了,可以看这里(传送门)。

    Callable是针对于多线程有返回值的时候涉及的类。

    FutureTask是实现了RunnableFuture接口,可以看源码

    public class FutureTask<V> implements RunnableFuture<V> {
        /*
         * Revision notes: This differs from previous versions of this
         * class that relied on AbstractQueuedSynchronizer, mainly to
         * avoid surprising users about retaining interrupt status during
         * cancellation races. Sync control in the current design relies
         * on a "state" field updated via CAS to track completion, along
         * with a simple Treiber stack to hold waiting threads.
         */
    

    RunnableFuture 实现了Runnable接口和Future接口

    public interface RunnableFuture<V> extends Runnable, Future<V> {
        /**
         * Sets this Future to the result of its computation
         * unless it has been cancelled.
         */
        void run();
    }
    
    

    Runnable这个接口我就不介绍了。

    来说一下Future接口,这个类的设计主要是存储多线程还未返回的数据,里面有很多方法,比如:

    boolean isDone(); //判断数据是否返回了

    boolean isCancelled(); //判断多线程是否取消

    V get() throws InterruptedException, ExecutionException; //取数据

    ....还有很多方法可以自己去看看

    最后的效果:

    消耗的时间为10秒多一点点,因为拼接也需要时间。

    也就是说,通过这种做法可以把请求时间从17秒压缩到10秒(两个接口中返回时间最大的值)。

    说在最后,这个问题主要是我在SpringCloud最近用Feign调用其他微服务接口超时了,然后找解决方案,想到可以用这样的方法,springboot中把多线程部分代码改成@Asyn就行,主要是解决思路。

    完整代码

    package mycode;
    
    import java.util.Date;
    import java.util.concurrent.*;
    
    /**
     * @author wx
     * @date 2021/7/9
     */
    public class CompletableFutureTest {
        public static void main(String[] args) throws ExecutionException, InterruptedException {
            Date startDate = new Date();
            CountDownLatch countDownLatch = new CountDownLatch(2);
            FutureTask<RestRequestDataA> futureTask1 = new FutureTask<>(new Callable<RestRequestDataA>() {
                @Override
                public RestRequestDataA call() throws Exception {
                    // 假设这里是请求接口A的调用,花费十秒
                    TimeUnit.SECONDS.sleep(10);
                    countDownLatch.countDown();
                    return new RestRequestDataA("myname");
                }
            });
            new Thread(futureTask1).start();
    
            FutureTask<RestRequestDataB> futureTask2 = new FutureTask<>(new Callable<RestRequestDataB>() {
                @Override
                public RestRequestDataB call() throws Exception {
                    // 假设这里是请求接口B的调用,花费七秒
                    TimeUnit.SECONDS.sleep(7);
                    countDownLatch.countDown();
                    return new RestRequestDataB("myvalue");
                }
            });
            new Thread(futureTask2).start();
    
            countDownLatch.await();
    
            ShowData showData = new ShowData(futureTask1.get(),futureTask2.get());
            System.out.println(showData);
            Date endDate = new Date();
            System.out.println(String.format("一共花费%d毫秒",endDate.getTime()-startDate.getTime()));
    
    
        }
    
    }
    
    class ShowData{
        private RestRequestDataA name;
        private RestRequestDataB value;
    
        public ShowData(RestRequestDataA name, RestRequestDataB value) {
            this.name = name;
            this.value = value;
        }
    
        @Override
        public String toString() {
            return "ShowData{" +
                    "name=" + name.getName() +
                    ", value=" + value.getValue() +
                    '}';
        }
    }
    
    class RestRequestDataA{
        private String name;
    
        public RestRequestDataA(String name) {
            this.name = name;
        }
    
        public String getName() {
            return name;
        }
    
    
    }
    
    class RestRequestDataB{
        private String value;
    
        public RestRequestDataB(String value) {
            this.value = value;
        }
    
        public String getValue() {
            return value;
        }
    
    
    }
    
    
    本博客所有文章除特别声明外,均采用 CC BY 4.0 CN协议 许可协议。转载请注明出处!
  • 相关阅读:
    投票通过,PHP 8 确认引入 Union Types 2.0
    Laravel 菜鸟的晋级之路
    给公司写的composer包开发的规范
    Swoft 源码剖析
    听说PHP的生成器yield处理大量数据杠杠的
    读懂JWT的使用,你就会用PHP如何实现了
    python标准库及其它应用
    python常用算法题
    python迭代器实例
    python生成器实例
  • 原文地址:https://www.cnblogs.com/realwuxiong/p/14990981.html
Copyright © 2011-2022 走看看