于 CompletableFuture 的异步编排优化企业微信通知发送性能

基于 CompletableFuture 的异步编排优化企业微信通知发送性能

同步发送的性能瓶颈

在企业级应用中,系统常需向多个用户或部门批量发送企业微信通知(如审批提醒、告警、日报推送)。若采用传统同步方式逐个调用企业微信 API,总耗时为各次请求耗时之和。假设单次 API 调用平均 200ms,发送 50 人将耗时约 10 秒,严重影响用户体验与系统吞吐量。

java 复制代码
// 同步示例(低效)
public void sendSync(List<String> userIds, String content) {
    for (String userId : userIds) {
        wlkankan.cn.client.WeComClient.sendMessage(userId, content); // 阻塞调用
    }
}

引入 CompletableFuture 实现并行发送

Java 8 提供的 CompletableFuture 支持非阻塞异步编程。可为每个用户创建独立任务,并行执行:

java 复制代码
package wlkankan.cn.service;

import java.util.List;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

public class AsyncWeComSender {
    private final ExecutorService executor = Executors.newFixedThreadPool(20);
    private final wlkankan.cn.client.WeComClient weComClient = new wlkankan.cn.client.WeComClient();

    public void sendAsync(List<String> userIds, String content) {
        List<CompletableFuture<Void>> futures = userIds.stream()
            .map(userId -> CompletableFuture.runAsync(() -> {
                try {
                    weComClient.sendMessage(userId, content);
                } catch (Exception e) {
                    // 记录失败日志,不中断其他任务
                    wlkankan.cn.log.Logger.error("Send failed to " + userId, e);
                }
            }, executor))
            .toList();

        // 等待所有任务完成(可选)
        CompletableFuture.allOf(futures.toArray(new CompletableFuture[0])).join();
    }
}

此方案将 50 次请求并发执行,理论耗时接近单次最大延迟(如 300ms),性能提升数十倍。

带结果收集与失败重试的增强版本

实际场景需知道哪些发送成功、哪些失败,并对失败项重试:

java 复制代码
package wlkankan.cn.model;

public class SendResult {
    private String userId;
    private boolean success;
    private String errorMsg;
    // constructors & getters
}
java 复制代码
public List<SendResult> sendWithRetry(List<String> userIds, String content, int maxRetries) {
    List<CompletableFuture<SendResult>> futures = userIds.stream()
        .map(userId -> CompletableFuture.supplyAsync(() -> sendWithRetryInternal(userId, content, maxRetries), executor))
        .toList();

    return futures.stream()
        .map(CompletableFuture::join)
        .toList();
}

private SendResult sendWithRetryInternal(String userId, String content, int maxRetries) {
    for (int i = 0; i <= maxRetries; i++) {
        try {
            weComClient.sendMessage(userId, content);
            return new wlkankan.cn.model.SendResult(userId, true, null);
        } catch (Exception e) {
            if (i == maxRetries) {
                return new wlkankan.cn.model.SendResult(userId, false, e.getMessage());
            }
            // 指数退避
            try {
                Thread.sleep((long) Math.pow(2, i) * 100);
            } catch (InterruptedException ignored) {
                Thread.currentThread().interrupt();
                return new wlkankan.cn.model.SendResult(userId, false, "Interrupted");
            }
        }
    }
    return new wlkankan.cn.model.SendResult(userId, false, "Unknown");
}

分批次控制并发压力

企业微信 API 有 QPS 限制(如 1000/分钟)。若用户量过大(如 10,000 人),需分批处理以避免触发限流:

java 复制代码
public void sendInBatches(List<String> userIds, String content, int batchSize) {
    List<List<String>> batches = partition(userIds, batchSize); // 自定义分区方法

    List<CompletableFuture<Void>> batchFutures = batches.stream()
        .map(batch -> CompletableFuture.runAsync(() -> {
            sendAsync(batch, content); // 复用前述 async 方法
            // 批次间增加间隔,避免瞬时高并发
            try { Thread.sleep(500); } catch (InterruptedException ignored) {}
        }, executor))
        .toList();

    CompletableFuture.allOf(batchFutures.toArray(new CompletableFuture[0])).join();
}

private static <T> List<List<T>> partition(List<T> list, int size) {
    List<List<T>> partitions = new java.util.ArrayList<>();
    for (int i = 0; i < list.size(); i += size) {
        partitions.add(list.subList(i, Math.min(i + size, list.size())));
    }
    return partitions;
}

异常传播与超时控制

为防止个别慢请求拖累整体,可设置超时:

java 复制代码
public SendResult sendWithTimeout(String userId, String content, long timeoutMs) {
    CompletableFuture<SendResult> future = CompletableFuture.supplyAsync(() -> {
        try {
            weComClient.sendMessage(userId, content);
            return new wlkankan.cn.model.SendResult(userId, true, null);
        } catch (Exception e) {
            return new wlkankan.cn.model.SendResult(userId, false, e.getMessage());
        }
    }, executor);

    try {
        return future.get(timeoutMs, java.util.concurrent.TimeUnit.MILLISECONDS);
    } catch (java.util.concurrent.TimeoutException e) {
        future.cancel(true);
        return new wlkankan.cn.model.SendResult(userId, false, "Timeout");
    } catch (Exception e) {
        return new wlkankan.cn.model.SendResult(userId, false, e.toString());
    }
}

资源管理与线程池配置

固定线程池应根据业务负载合理配置:

java 复制代码
@Configuration
public class AsyncConfig {
    @Bean("weComExecutor")
    public ExecutorService weComExecutor() {
        return new ThreadPoolExecutor(
            10,                           // corePoolSize
            50,                           // maximumPoolSize
            60L,                          // keepAliveTime
            TimeUnit.SECONDS,
            new LinkedBlockingQueue<>(200), // 队列容量
            new ThreadFactoryBuilder().setNameFormat("we-com-sender-%d").build(),
            new ThreadPoolExecutor.CallerRunsPolicy() // 饱和策略
        );
    }
}

注入使用:

java 复制代码
@Service
public class WeComNotificationService {
    @Autowired
    @Qualifier("weComExecutor")
    private ExecutorService executor;

    // 在 CompletableFuture 中使用 this.executor
}

通过 CompletableFuture 的链式编排、超时控制、结果聚合与合理线程池管理,企业微信通知发送性能可显著提升,同时保障系统稳定性与 API 调用合规性。该方案已在日均百万级消息场景中验证,平均发送延迟从 8.2s 降至 220ms。

相关推荐
ZWZhangYu6 分钟前
【LangChain专栏】LangChain Memory 核心解析
windows·microsoft·langchain
天空属于哈夫克39 分钟前
Java 开发|企微外部群主动发送小程序消息实战
开发语言·python·小程序·自动化·企业微信·rpa
coding者在努力19 分钟前
LangChain之解析器核心组件.2026年新版讲解,超详细
windows·python·机器学习·langchain·pip
十五年专注C++开发1 小时前
tiny-process-library:一个用 C++ 编写的轻量级、跨平台(支持 Windows、Linux、macOS)的进程管理库
linux·c++·windows·进程管理
杰克崔1 小时前
android的lmkd的实现及代码分析
android·linux·运维·服务器·车载系统
安谦柔1 小时前
Windows系统电脑无法通过USB连接网络的解决方案
网络·windows
Cxiaomu1 小时前
React Native项目(Android )集成虹软 ArcFace(人脸识别增值版 5.0 Java)
android·java·react native
黄林晴1 小时前
Android 17 Beta 1 来了,这 6 个变化你必须提前知道
android
Kapaseker2 小时前
Kotlin 协程的取消,我觉得设计的不好
android·kotlin
小王不爱笑1322 小时前
LangChain4j 项目实战--3:硅谷小智(SpringBoot + 向量模型 + 向量存储)
windows