使用 CompletableFuture 提升调用多个Service的接口查询效率

102 阅读2分钟

业务背景

  • 返回给前端的VO类是需要调用三种不同的Service方法或Feign来拼接而成的。
  • 由于系统需要同时访问多个数据源,传统的同步调用会导致多个 API 调用的串行执行,这显著影响了响应时间和系统吞吐量。

解决

  • 通过 CompletableFuture 来实现异步调用。

大致代码如下:

import java.util.*;
import java.util.concurrent.*;
import java.util.function.Function;
import java.util.stream.Collectors;

public class AsyncExample {

    private static Executor executor = Executors.newFixedThreadPool(10);  // 自定义线程池

    public static void main(String[] args) {
        // 示例输入数据
        List<String> employeeIds = Arrays.asList("emp1", "emp2", "emp3");
        List<String> customerIds = Arrays.asList("cus1", "cus2");
        List<String> chatIds = Arrays.asList("chat1", "chat2");

        // 执行异步任务
        fetchUserData(employeeIds, customerIds, chatIds);
    }

    public static void fetchUserData(List<String> employeeIds, List<String> customerIds, List<String> chatIds) {
        // 异步获取员工、客户和群聊信息
        CompletableFuture<Map<String, Employee>> employeeFuture = fetchEmployeeInfo(employeeIds);
        CompletableFuture<Map<String, Customer>> customerFuture = fetchCustomerInfo(customerIds);
        CompletableFuture<Map<String, ChatGroup>> chatFuture = fetchChatGroupInfo(chatIds);

        // 等待所有异步任务完成
        CompletableFuture<Void> allOf = CompletableFuture.allOf(employeeFuture, customerFuture, chatFuture);

        // 在所有任务完成后进行结果处理
        allOf.thenRun(() -> {
            Map<String, Employee> employees = employeeFuture.join();
            Map<String, Customer> customers = customerFuture.join();
            Map<String, ChatGroup> chats = chatFuture.join();

            // 处理获取的结果
            processResults(employees, customers, chats);
        }).join();  // 阻塞当前线程直到异步操作完成
    }

    // 获取员工信息的异步任务
    private static CompletableFuture<Map<String, Employee>> fetchEmployeeInfo(List<String> employeeIds) {
        return CompletableFuture.supplyAsync(() -> {
            // 模拟从外部系统或数据库获取员工信息
            return employeeIds.stream()
                    .collect(Collectors.toMap(Function.identity(), id -> new Employee(id, "Employee " + id)));
        }, executor);
    }

    // 获取客户信息的异步任务
    private static CompletableFuture<Map<String, Customer>> fetchCustomerInfo(List<String> customerIds) {
        return CompletableFuture.supplyAsync(() -> {
            // 模拟从外部系统或数据库获取客户信息
            return customerIds.stream()
                    .collect(Collectors.toMap(Function.identity(), id -> new Customer(id, "Customer " + id)));
        }, executor);
    }

    // 获取群聊信息的异步任务
    private static CompletableFuture<Map<String, ChatGroup>> fetchChatGroupInfo(List<String> chatIds) {
        return CompletableFuture.supplyAsync(() -> {
            // 模拟从外部系统或数据库获取群聊信息
            return chatIds.stream()
                    .collect(Collectors.toMap(Function.identity(), id -> new ChatGroup(id, "Chat Group " + id)));
        }, executor);
    }

    // 处理获取到的所有结果
    private static void processResults(Map<String, Employee> employees, Map<String, Customer> customers, Map<String, ChatGroup> chats) {
        // 示例:输出所有获取到的信息
        System.out.println("Employees: " + employees);
        System.out.println("Customers: " + customers);
        System.out.println("Chats: " + chats);
    }

    // 示例数据对象
    static class Employee {
        String id;
        String name;

        public Employee(String id, String name) {
            this.id = id;
            this.name = name;
        }

        @Override
        public String toString() {
            return "Employee{id='" + id + "', name='" + name + "'}";
        }
    }

    static class Customer {
        String id;
        String name;

        public Customer(String id, String name) {
            this.id = id;
            this.name = name;
        }

        @Override
        public String toString() {
            return "Customer{id='" + id + "', name='" + name + "'}";
        }
    }

    static class ChatGroup {
        String id;
        String name;

        public ChatGroup(String id, String name) {
            this.id = id;
            this.name = name;
        }

        @Override
        public String toString() {
            return "ChatGroup{id='" + id + "', name='" + name + "'}";
        }
    }
}

异步回调要传线程池?

建议强制传线程池,且根据实际情况做线程池隔离

当不传递线程池时,会使用ForkJoinPool中的公共线程池CommonPool,这里所有调用将共用该线程池,核心线程数=处理器数量-1(单核核心线程数为1),所有异步回调都会共用该CommonPool,核心与非核心业务都竞争同一个池中的线程,很容易成为系统瓶颈。手动传递线程池参数可以更方便的调节参数,并且可以给不同的业务分配不同的线程池,以求资源隔离,减少不同业务之间的相互干扰。

来自:美团技术团队