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

相关推荐
2的n次方_4 分钟前
CANN Ascend C 编程语言深度解析:异构并行架构、显式存储层级与指令级精细化控制机制
c语言·开发语言·架构
亓才孓4 分钟前
[JDBC]PreparedStatement替代Statement
java·数据库
_F_y27 分钟前
C++重点知识总结
java·jvm·c++
打工的小王28 分钟前
Spring Boot(三)Spring Boot整合SpringMVC
java·spring boot·后端
毕设源码-赖学姐29 分钟前
【开题答辩全过程】以 高校体育场馆管理系统为例,包含答辩的问题和答案
java·spring boot
我真会写代码31 分钟前
SSM(指南一)---Maven项目管理从入门到精通|高质量实操指南
java·spring·tomcat·maven·ssm
vx_Biye_Design31 分钟前
【关注可免费领取源码】房屋出租系统的设计与实现--毕设附源码40805
java·spring boot·spring·spring cloud·servlet·eclipse·课程设计
java干货36 分钟前
为什么 “File 10“ 排在 “File 2“ 前面?解决文件名排序的终极算法:自然排序
开发语言·python·算法
_F_y36 分钟前
C语言重点知识总结(含KMP详细讲解)
c语言·开发语言
DN金猿37 分钟前
接口路径正确,请求接口却提示404
java·tomcat