MST

星途 面试题库

面试题:Java 中 CompletableFuture 多个任务 OR 组合关系 - 专家难度

在一个复杂的分布式系统场景下,有多个微服务调用,每个微服务调用返回一个 CompletableFuture。这些微服务调用存在一定的依赖关系,部分微服务之间是 OR 组合关系(即只要其中一个成功就可以继续后续流程),同时要考虑网络超时、服务熔断等情况。请设计一个通用的框架来处理这种复杂的 CompletableFuture 组合任务,并详细描述设计思路和关键代码实现。
40.3万 热度难度
编程语言Java

知识考点

AI 面试

面试题答案

一键面试

设计思路

  1. 依赖关系管理:使用有向无环图(DAG)来表示微服务之间的依赖关系。节点代表微服务调用,边代表依赖关系。对于 OR 组合关系的节点,只要其中一个子节点完成(成功),父节点就可以继续。
  2. 超时处理:为每个 CompletableFuture 设置超时时间。可以使用 CompletableFutureorTimeout 方法来实现。
  3. 服务熔断:引入熔断器机制,例如使用 Hystrix 或 Sentinel。当某个微服务调用失败次数达到一定阈值,触发熔断,直接返回预设的 fallback 结果。
  4. 通用框架:设计一个通用的类或方法,接受微服务调用的 CompletableFuture 以及它们之间的依赖关系作为输入,返回最终的 CompletableFuture

关键代码实现

  1. 定义微服务调用的包装类
import java.util.concurrent.CompletableFuture;

public class MicroserviceCall<T> {
    private CompletableFuture<T> future;
    private boolean isFallback;

    public MicroserviceCall(CompletableFuture<T> future) {
        this.future = future;
        this.isFallback = false;
    }

    public CompletableFuture<T> getFuture() {
        return future;
    }

    public void setFallback(boolean isFallback) {
        this.isFallback = isFallback;
    }

    public boolean isFallback() {
        return isFallback;
    }
}
  1. 构建 DAG 结构
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

public class DependencyGraph<T> {
    private Map<MicroserviceCall<T>, List<MicroserviceCall<T>>> graph;

    public DependencyGraph() {
        this.graph = new HashMap<>();
    }

    public void addEdge(MicroserviceCall<T> from, MicroserviceCall<T> to) {
        graph.putIfAbsent(from, new ArrayList<>());
        graph.get(from).add(to);
    }

    public Map<MicroserviceCall<T>, List<MicroserviceCall<T>>> getGraph() {
        return graph;
    }
}
  1. 通用框架实现
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.TimeoutException;

public class CompletableFutureCombiner<T> {
    private static final int DEFAULT_TIMEOUT = 10; // 秒

    public CompletableFuture<T> combineTasks(DependencyGraph<T> graph, MicroserviceCall<T> root) {
        return processNode(graph, root, DEFAULT_TIMEOUT);
    }

    private CompletableFuture<T> processNode(DependencyGraph<T> graph, MicroserviceCall<T> node, int timeout) {
        CompletableFuture<T> future = node.getFuture();
        future = future.orTimeout(timeout, TimeUnit.SECONDS);

        List<MicroserviceCall<T>> children = graph.getGraph().getOrDefault(node, new ArrayList<>());
        if (children.isEmpty()) {
            return future;
        }

        List<CompletableFuture<T>> childFutures = new ArrayList<>();
        for (MicroserviceCall<T> child : children) {
            CompletableFuture<T> childFuture = processNode(graph, child, timeout);
            childFutures.add(childFuture);
        }

        return CompletableFuture.anyOf(childFutures.toArray(new CompletableFuture[0]))
              .thenApply(result -> {
                    try {
                        return (T) ((CompletableFuture<T>) result).get();
                    } catch (InterruptedException | ExecutionException e) {
                        throw new RuntimeException(e);
                    }
                });
    }
}
  1. 示例使用
public class Main {
    public static void main(String[] args) {
        CompletableFuture<String> future1 = CompletableFuture.supplyAsync(() -> "Result from MS1");
        CompletableFuture<String> future2 = CompletableFuture.supplyAsync(() -> "Result from MS2");

        MicroserviceCall<String> ms1 = new MicroserviceCall<>(future1);
        MicroserviceCall<String> ms2 = new MicroserviceCall<>(future2);

        DependencyGraph<String> graph = new DependencyGraph<>();
        graph.addEdge(ms1, ms2);

        CompletableFutureCombiner<String> combiner = new CompletableFutureCombiner<>();
        CompletableFuture<String> result = combiner.combineTasks(graph, ms1);

        result.join();
    }
}

以上代码实现了一个简单的通用框架来处理复杂的 CompletableFuture 组合任务,包含依赖关系处理、超时处理。在实际应用中,需要结合具体的熔断器库(如 Hystrix 或 Sentinel)来实现服务熔断功能。