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

相关推荐
逝水无殇12 小时前
C# 异常处理详解
开发语言·后端·c#
懒鸟一枚13 小时前
深入理解 Linux 内存、Swap 交换分区与分页机制的关系
java·linux·数据库
玖玥拾13 小时前
C# 语言进阶(十五)C# 游戏服务端 MySQL 数据库
服务器·开发语言·网络·数据库·mysql·c#
铅笔侠_小龙虾14 小时前
Rust 学习目录
开发语言·学习·rust
我命由我1234514 小时前
执行 Gradle 指令报错,无法将“grep”项识别为 cmdlet、函数、脚本文件或可运行程序的名称
android·java·java-ee·android studio·android jetpack·android-studio·android runtime
考虑考虑14 小时前
Sentinel安装
java·后端·微服务
云泽80814 小时前
从零吃透 C++ 异常:抛出捕获、栈展开、异常重抛与编码规范详解
开发语言·c++·代码规范
灯澜忆梦14 小时前
GO---可见性规则
开发语言·golang
逝水无殇15 小时前
C# 文件的输入与输出详解
开发语言·数据库·后端·c#
这是个栗子15 小时前
前端开发中的常用工具函数(八)
开发语言·前端·javascript