Node.js中Firebase Admin SDK批量发消息遇ETIMEDOUT问题求助
问题:Node.js集成Firebase Admin SDK批量发送FCM消息时出现网络超时错误
错误信息
/Users/dbw/WebstormProjects/fcm_test/node_modules/firebase-admin/lib/utils/api-request.js:997 throw new error_1.FirebaseAppError(error_1.AppErrorCodes.NETWORK_ERROR, `Error while making requests: ${error}`); ^ FirebaseAppError: Error while making requests: Error: connect ETIMEDOUT 2001:4860:4802:36::39:443 at ClientHttp2Session.<anonymous> (/Users/dbw/WebstormProjects/fcm_test/node_modules/firebase-admin/lib/utils/api-request.js:997:23) at ClientHttp2Session.emit (node:events:513:28) at emitClose (node:internal/http2/core:1085:10) at process.processTicksAndRejections (node:internal/process/task_queues:82:21) { errorInfo: { code: 'app/network-error', message: 'Error while making requests: Error: connect ETIMEDOUT 2001:4860:4802:36::39:443' }, codePrefix: 'app' }
问题描述
依据FCM官方文档在Node.js后端服务中集成Firebase Admin SDK发送消息,最初admin.messaging().send(message)步骤无响应并抛出上述网络超时错误。身处国内需代理访问国际网络,通过添加httpProxy解决了单条消息发送问题,但使用admin.messaging().sendEach([message])和admin.messaging().sendEachForMulticast(multiMessage)时,相同的ETIMEDOUT问题再次出现。而Java项目中无需代理即可正常发送消息。
请问Node.js与Java环境存在哪些差异?如何解决Node.js中批量消息发送的该问题?
相关代码
import * as admin from 'firebase-admin'; import { credential } from 'firebase-admin'; import { HttpsProxyAgent } from 'https-proxy-agent'; const credentialsPath = '/Users/dbw/Downloads/mars-messaging-test-firebase-adminsdk-i2mzh-362b2803d1.json'; const credentials = require(credentialsPath); const httpAgent = new HttpsProxyAgent('http://127.0.0.1:7890/'); const app = admin.initializeApp({ credential: credential.cert(credentials), httpAgent }); const registrationToken = 'my token'; const message = { "notification":{ "title":"I am a title", "body":"I am a body" }, token: registrationToken }; const multiMessage = { data: { score: '850', time: '2:45' }, tokens: [registrationToken] }; const main = async () => { try { const result = await app.messaging().sendEachForMulticast(multiMessage); console.log('result: ' + result); } catch (err) { console.log('err: ' + err); } };
解答
Node.js与Java环境的核心差异
- HTTP协议版本差异:Node.js版Firebase Admin SDK默认使用HTTP/2发送批量消息,单条消息可能使用HTTP/1.1;而Java版SDK默认使用HTTP/1.1,部分代理工具对HTTP/2的兼容性较差,导致批量请求超时。
- 代理配置覆盖范围差异:Java环境可能自动继承系统代理配置,且Firebase Java SDK内部对代理的处理逻辑能覆盖所有请求场景;Node.js版的
httpAgent配置仅作用于单条请求的客户端,批量请求的HTTP/2客户端未继承该代理配置,直接发起无代理请求导致超时。 - 连接池管理逻辑差异:Node.js的HTTP/2连接池管理与Java不同,批量请求时可能创建新的独立连接,未应用指定的代理设置,从而触发网络超时。
解决方法
1. 强制使用HTTP/1.1协议
初始化SDK时通过messagingOptions强制指定HTTP/1.1,规避HTTP/2的代理兼容问题:
const app = admin.initializeApp({ credential: credential.cert(credentials), httpAgent, messagingOptions: { httpVersion: '1.1' // 强制使用HTTP/1.1协议 } });
2. 为批量请求手动配置代理客户端
创建自定义HTTP客户端并替换messaging模块的默认客户端,确保批量请求使用代理:
import * as admin from 'firebase-admin'; import { credential } from 'firebase-admin'; import { HttpsProxyAgent } from 'https-proxy-agent'; import * as http from 'http'; import * as https from 'https'; const credentialsPath = '/Users/dbw/Downloads/mars-messaging-test-firebase-adminsdk-i2mzh-362b2803d1.json'; const credentials = require(credentialsPath); const proxyAgent = new HttpsProxyAgent('http://127.0.0.1:7890/'); // 创建带代理的自定义HTTP客户端 const customHttpClient = new admin.httpClient.HttpClient({ httpAgent: new http.Agent({ agent: proxyAgent }), httpsAgent: new https.Agent({ agent: proxyAgent }) }); const app = admin.initializeApp({ credential: credential.cert(credentials), httpAgent: proxyAgent }); // 替换messaging模块的默认HTTP客户端 app.messaging()._httpClient = customHttpClient;
3. 优化代理的HTTP/2支持
若需保留HTTP/2,检查代理工具(如Clash、V2Ray)是否开启HTTP/2支持,或切换至对HTTP/2兼容性更好的代理服务。
内容的提问来源于stack exchange,提问作者dbwdbw1
相关产品推荐
相关产品推荐

