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协议 许可协议。转载请注明出处!
原文地址:https://www.cnblogs.com/realwuxiong/p/14990981.html