利用Java CompletableFuture优化企业微信批量消息发送的异步编排

利用Java CompletableFuture优化企业微信批量消息发送的异步编排

在企业级应用中,通过企业微信向大量成员或客户群发通知是常见需求。若采用同步串行发送,不仅耗时长,还会因单个请求失败阻塞整体流程。Java 8 引入的 CompletableFuture 提供了强大的异步编排能力,可显著提升吞吐量与容错性。本文以 wlkankan.cn 包结构为例,展示如何基于 CompletableFuture 实现高并发、带限流与失败重试的企业微信消息批量发送系统。

基础异步发送框架

首先定义消息实体与发送客户端:

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

public class WeComMessage {
    private String userId;
    private String content;
    private String accessToken;

    // constructors, getters, setters
}
java 复制代码
package wlkankan.cn.message.client;

import java.util.concurrent.CompletableFuture;

public class WeComApiClient {
    public static CompletableFuture<String> sendMessageAsync(WeComMessage msg) {
        return CompletableFuture.supplyAsync(() -> {
            try {
                // 模拟HTTP调用企业微信API
                Thread.sleep(200); // 网络延迟
                if (Math.random() < 0.1) { // 10% 模拟失败
                    throw new RuntimeException("Send failed for " + msg.getUserId());
                }
                return "OK:" + msg.getUserId();
            } catch (Exception e) {
                throw new RuntimeException(e);
            }
        });
    }
}

批量发送与结果聚合

使用 CompletableFuture.allOf() 并行发送并等待全部完成:

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

import wlkankan.cn.message.client.WeComApiClient;
import wlkankan.cn.message.model.WeComMessage;
import java.util.List;
import java.util.concurrent.CompletableFuture;
import java.util.stream.Collectors;

public class BatchMessageService {

    public List<String> sendBatch(List<WeComMessage> messages) {
        List<CompletableFuture<String>> futures = messages.stream()
            .map(WeComApiClient::sendMessageAsync)
            .collect(Collectors.toList());

        CompletableFuture<Void> allDone = CompletableFuture.allOf(
            futures.toArray(new CompletableFuture[0])
        );

        allDone.join(); // 阻塞等待全部完成

        return futures.stream()
            .map(f -> {
                try {
                    return f.get();
                } catch (Exception e) {
                    return "ERROR:" + e.getCause().getMessage();
                }
            })
            .collect(Collectors.toList());
    }
}

引入信号量限流防止API过载

企业微信API有严格的QPS限制(如2000/分钟)。为避免触发限流,需控制并发数:

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

import java.util.concurrent.Semaphore;
import java.util.concurrent.CompletableFuture;

public class RateLimitedWeComClient {
    private final Semaphore semaphore;

    public RateLimitedWeComClient(int maxConcurrent) {
        this.semaphore = new Semaphore(maxConcurrent);
    }

    public CompletableFuture<String> sendMessageWithLimit(WeComMessage msg) {
        return CompletableFuture.supplyAsync(() -> {
            try {
                semaphore.acquire();
                try {
                    // 调用真实API
                    return WeComApiClient.sendMessage(msg).get();
                } finally {
                    semaphore.release();
                }
            } catch (InterruptedException e) {
                Thread.currentThread().interrupt();
                throw new RuntimeException(e);
            }
        });
    }
}

失败重试机制

对失败请求自动重试最多3次:

java 复制代码
package wlkankan.cn.message.retry;

import java.util.concurrent.CompletableFuture;
import java.util.concurrent.TimeUnit;
import java.util.function.Supplier;

public class RetryableFuture {
    public static <T> CompletableFuture<T> retry(Supplier<CompletableFuture<T>> action, int maxRetries, long delayMs) {
        CompletableFuture<T> result = new CompletableFuture<>();
        attempt(action, maxRetries, delayMs, result);
        return result;
    }

    private static <T> void attempt(Supplier<CompletableFuture<T>> action, int retriesLeft, long delayMs, CompletableFuture<T> result) {
        action.get().whenComplete((value, throwable) -> {
            if (throwable == null) {
                result.complete(value);
            } else if (retriesLeft > 0) {
                try {
                    TimeUnit.MILLISECONDS.sleep(delayMs);
                } catch (InterruptedException e) {
                    Thread.currentThread().interrupt();
                    result.completeExceptionally(e);
                    return;
                }
                attempt(action, retriesLeft - 1, delayMs, result);
            } else {
                result.completeExceptionally(throwable);
            }
        });
    }
}

集成到发送逻辑中:

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

import wlkankan.cn.message.retry.RetryableFuture;
import wlkankan.cn.message.client.WeComApiClient;
import wlkankan.cn.message.model.WeComMessage;
import java.util.concurrent.CompletableFuture;

public class ResilientMessageService {
    public CompletableFuture<String> sendWithRetry(WeComMessage msg) {
        return RetryableFuture.retry(
            () -> WeComApiClient.sendMessageAsync(msg),
            3,
            500
        ).exceptionally(ex -> "FAILED_AFTER_RETRY:" + msg.getUserId());
    }
}

完整批量发送流程

组合限流、重试与结果收集:

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

import wlkankan.cn.message.model.WeComMessage;
import java.util.List;
import java.util.concurrent.CompletableFuture;
import java.util.stream.Collectors;

public class EnterpriseBatchSender {
    private final RateLimitedWeComClient client = new RateLimitedWeComClient(50); // 限制50并发

    public List<String> sendAll(List<WeComMessage> messages) {
        List<CompletableFuture<String>> futures = messages.stream()
            .map(msg -> client.sendMessageWithLimit(msg)
                .handle((res, ex) -> ex == null ? res : "INITIAL_FAIL:" + msg.getUserId())
                .thenCompose(result -> {
                    if (result.startsWith("INITIAL_FAIL")) {
                        return new ResilientMessageService().sendWithRetry(
                            new WeComMessage(/* reconstruct from result */)
                        );
                    }
                    return CompletableFuture.completedFuture(result);
                }))
            .collect(Collectors.toList());

        CompletableFuture<Void> all = CompletableFuture.allOf(futures.toArray(new CompletableFuture[0]));
        all.join();

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

通过 CompletableFuture 的链式编排,wlkankan.cn.message 模块实现了高并发、带限流、自动重试的企业微信批量消息发送系统,在保障API合规的同时极大提升了发送效率与稳定性。

相关推荐
杰克尼6 分钟前
天机学堂面试题
java
Web极客码7 分钟前
用 Python 搭建工具调用 Agent 的调试过程
java·服务器·前端
我命由我1234522 分钟前
Android 控件 - CardView(快速实现圆角)
android·java·java-ee·kotlin·android studio·android-studio·android runtime
2601_9638699525 分钟前
【计算机毕业设计】基于Java的潮牌购物网站系统的设计与实现
java·开发语言·课程设计
梦想的旅途230 分钟前
企业微信API二次开发:接入 AI 大模型实现外部群智能问答
人工智能·自动化·二次开发·企业微信
203号居民33 分钟前
LeetCode hot 100 —560. 和为 K 的子数组
java·算法·leetcode
名字还没想好☜1 小时前
Go 的 io.Reader/Writer 组合实战:io.Copy、TeeReader、MultiWriter 优雅处理数据流
开发语言·后端·golang·go·iphone
浪客川1 小时前
使用 IDEA 生成API文档
java·ide·intellij-idea
郝学胜-神的一滴1 小时前
Qt 高级编程 042:进度条从入门到自定义美化
开发语言·c++·qt·软件开发·用户界面
鸿芯微控科技1 小时前
压电纳米定位分辨率怎么测?噪声、带宽、最小可检测位移与Python分析
开发语言·python·噪声分析·压电执行器·纳米定位·位移测量