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

相关推荐
m0_686041612 小时前
C++中的装饰器模式变体
开发语言·c++·算法
一杯清茶5202 小时前
Python中ttkbootstrap的介绍与基本使用
开发语言·python
yangminlei2 小时前
SpringSecurity核心源码剖析+jwt+OAuth(一):SpringSecurity的初次邂逅(概念、认证、授权)
java·开发语言·python
星火开发设计2 小时前
动态内存分配:new 与 delete 的基本用法
开发语言·c++·算法·内存·delete·知识·new
小张快跑。2 小时前
【SpringBoot进阶指南(一)】SpringBoot整合MyBatis实战、Bean管理、自动配置原理、自定义starter
java·开发语言·spring boot
A懿轩A2 小时前
【2026 最新】Java 与 IntelliJ IDEA 详细下载安装教程 带图演示(Windows 版)
java·windows·intellij-idea
资深web全栈开发2 小时前
JS防爬虫3板斧
开发语言·javascript·爬虫
好好沉淀2 小时前
maven依赖爆红处理
java·maven
草履虫建模2 小时前
A02 Maven 基础配置:本地仓库、镜像、项目编码与常见问题(IDEA 实战)
xml·java·spring boot·spring·maven·intellij-idea·idea