利用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合规的同时极大提升了发送效率与稳定性。

相关推荐
Amumu121387 小时前
Js:内置对象
开发语言·前端·javascript
2301_807367197 小时前
C++代码风格检查工具
开发语言·c++·算法
飞Link7 小时前
具身智能音频处理核心框架 PyAudio 深度拆解与实战
开发语言·python·音视频
皙然7 小时前
深度解析 JVM 方法区:从永久代到元空间的核心逻辑
开发语言·jvm
博语小屋7 小时前
多路转接select、poll
开发语言·网络·c++·php
沐知全栈开发7 小时前
C# 预处理器指令
开发语言
m0_730115117 小时前
C++中的命令模式实战
开发语言·c++·算法
Nyarlathotep01137 小时前
线程创建和Thread类
java
阿波罗尼亚7 小时前
JDK17 新特性
java
独自破碎E7 小时前
【面试真题拆解】Spring事务机制
java·spring·面试