如何快速向百万级设备发送FCM V1推送请求?
5分钟完成100万FCM V1推送的优化策略
一、HTTP/2客户端深度调优
- 拉满HTTP/2并发流上限:FCM V1基于HTTP/2协议,单连接默认并发流数为100,可上调至FCM允许的最大值(建议设为1000,需结合实际测试验证)。同时优化IO线程数,匹配CPU核心数:
this.httpClient = HttpAsyncClients.customHttp2() .setIOReactorConfig(IOReactorConfig.custom() .setSoTimeout(Timeout.ofMinutes(1)) .setSelectInterval(Timeout.ofMilliseconds(100)) .setIoThreadCount(Runtime.getRuntime().availableProcessors() * 2) .build()) .setMaxConnPerRoute(1000) .setMaxConnTotal(1000) .build(); - 预序列化固定Payload:将FCM请求中除
token外的固定部分提前序列化为JSON字符串,每次发送仅替换token字段,避免重复序列化带来的CPU开销。
二、线程池与任务调度优化
- 替换为弹性线程池:固定100线程的配置无法支撑每秒约3333次的请求量,改用自定义弹性线程池,避免任务堆积:
threadPoolExecutor = new ThreadPoolExecutor( 200, // 核心线程数 1000, // 最大线程数 60L, TimeUnit.SECONDS, new SynchronousQueue<>(), // 同步队列,直接提交任务到线程 new ThreadFactoryBuilder().setNameFormat("fcm-push-%d").build() ); - 移除冗余Future包装:现有代码中用
SettableFuture二次包装请求Future,增加不必要的开销,直接收集sender.send()返回的ListenableFuture即可:final List<ListenableFuture<Result>> futureList = new ArrayList<>(); for (Recipient recipient : recipients) { futureList.add(sender.send(msg, recipient.getPushToken())); } // 用Futures.allAsList统一处理结果 ListenableFuture<List<Result>> allResults = Futures.allAsList(futureList);
三、请求传输优化
- 启用请求压缩:在HTTP请求头中添加
Content-Encoding: gzip,将Payload压缩后发送,FCM支持gzip解码,可大幅减少网络传输时间。 - 预创建HTTP请求实例:提前初始化
HttpPost对象,仅在每次请求时更新请求体中的token字段,避免重复创建请求对象的开销。
四、错误处理与重试优化
- 异步指数退避重试:针对FCM返回的可重试错误(503服务不可用、429请求超限),实现异步重试逻辑,用
Futures.retryWhenFailed封装,避免同步阻塞浪费资源:ListenableFuture<Result> sendWithRetry(Message message, String token) { return Futures.retryWhenFailed( () -> sender.sendNoRetry(message, token), RetryPolicy.exponentialBackoff(3, 1000), // 最多重试3次,初始间隔1秒 throwable -> { // 判断是否为可重试错误 if (throwable instanceof FcmRetryableException) { return true; } return false; }, executorService ); } - 批量处理错误结果:待所有请求完成后,统一统计失败的token列表,集中进行重试,减少单请求错误处理的分散开销。
五、系统与JVM调优
- JVM参数优化:调整堆内存与垃圾收集策略,减少GC停顿:
-Xmx8G -Xms4G -XX:+UseG1GC -XX:MaxGCPauseMillis=200 - 操作系统网络调优:增大TCP缓冲区、启用TCP快速打开,减少网络握手与传输延迟:
sysctl -w net.core.rmem_max=16777216 sysctl -w net.core.wmem_max=16777216 sysctl -w net.ipv4.tcp_fastopen=3 - 异步日志输出:将同步日志改为异步(如Logback AsyncAppender),避免磁盘IO阻塞推送线程。
六、请求分发优化
- 分片处理设备列表:将100万设备拆分为10-20个批次,用独立的HTTP客户端实例或线程池处理每个批次,避免单客户端的并发瓶颈。
- 全链路非阻塞:确保Payload序列化、错误处理等所有环节均为非阻塞操作,禁用任何
Future.get()同步调用,全部依赖ListenableFuture回调处理结果。
内容的提问来源于stack exchange,提问作者A Greenwood
相关产品推荐
相关产品推荐

