反复收到此错误。错误#1
{ Error: fcm.googleapis.com network timeout. Please try again.
at FirebaseAppError.Error (native)
at FirebaseAppError.FirebaseError [as constructor] (/user_code/node_modules/firebase-admin/lib/utils/error.js:25:28)
at new FirebaseAppError (/user_code/node_modules/firebase-admin/lib/utils/error.js:70:23)
at TLSSocket.<anonymous> (/user_code/node_modules/firebase-admin/lib/utils/api-request.js:106:51)
at emitNone (events.js:86:13)
at TLSSocket.emit (events.js:185:7)
at TLSSocket.Socket._onTimeout (net.js:339:8)
at ontimeout (timers.js:365:14)
at tryOnTimeout (timers.js:237:5)
at Timer.listOnTimeout (timers.js:207:5)
errorInfo:
{ code: 'app/network-timeout',
message: 'fcm.googleapis.com network timeout. Please try again.' } }
我得到的另一个错误几次.错误 #2
{ Error: Credential implementation provided to initializeApp() via the "credential" property failed to fetch a valid Google OAuth2 access token with the following error: "read ECONNRESET".
at FirebaseAppError.Error (native)
at FirebaseAppError.FirebaseError [as constructor] (/user_code/node_modules/firebase-admin/lib/utils/error.js:25:28)
at new FirebaseAppError (/user_code/node_modules/firebase-admin/lib/utils/error.js:70:23)
at /user_code/node_modules/firebase-admin/lib/firebase-app.js:106:23
at process._tickDomainCallback (internal/process/next_tick.js:129:7)
errorInfo:
{ code: 'app/invalid-credential',
message: 'Credential implementation provided to initializeApp() via the "credential" property failed to fetch a valid Google OAuth2 access token with the following error: "read ECONNRESET".' } }
另一种类型.错误 #3
Error sending message: { Error: A network request error has occurred: read ECONNRESET
at FirebaseAppError.Error (native)
at FirebaseAppError.FirebaseError [as constructor] (/user_code/node_modules/firebase-admin/lib/utils/error.js:25:28)
at new FirebaseAppError (/user_code/node_modules/firebase-admin/lib/utils/error.js:70:23)
at ClientRequest.<anonymous> (/user_code/node_modules/firebase-admin/lib/utils/api-request.js:115:43)
at emitOne (events.js:96:13)
at ClientRequest.emit (events.js:188:7)
at TLSSocket.socketErrorListener (_http_client.js:310:9)
at emitOne (events.js:96:13)
at TLSSocket.emit (events.js:188:7)
at emitErrorNT (net.js:1276:8)
errorInfo:
{ code: 'app/network-error',
message: 'A network request error has occurred: read ECONNRESET' } }
我尝试使用我的云功能做的是根据价格匹配发送 FCM 消息检查用户详细信息。云函数是一个数据库触发器。这是我的云函数的代码。我在代码中没有看到任何问题,因为我正在使用承诺。
// Checks price alerts for users
exports.priceAlertCheck = functions.database.ref('/crons/alerts/price').onWrite(event => {
const promises = [];
admin.database().ref(`/alerts/price`).once('value', function(alertSnapshot) {
alertSnapshot.forEach(function(dataSnapshot) {
promises.push(createPriceAlertPromise(dataSnapshot));
});
});
return Promise.all(promises);
});
function createPriceUrl(fromCurrency, toCurrency, exchange) {
return 'https://zzzz.com/data/price?fsym='
+fromCurrency+'&tsyms='+toCurrency+(exchange ? '&e='+exchange : '');
}
function createPriceAlertPromise(snapshot) {
const comboKeyArray = snapshot.key.split('-');
const fromCurrency = comboKeyArray[0];
const toCurrency = comboKeyArray[1];
const exchange = comboKeyArray[2];
return request(createPriceUrl(fromCurrency, toCurrency, exchange), function (error, response, body) {
if (!error && response.statusCode == 200) {
const jsonobj = JSON.parse(response.body);
const currentPrice = jsonobj[toCurrency];
const promises = [];
snapshot.forEach(function(data) {
promises.push(sendAlertNotifications(snapshot.key, data.key, currentPrice));
});
return Promise.all(promises);
} else {
console.log('Error fetching price', snapshot.key);
}
});
}
function sendAlertNotifications(comboKey, userId, currentPrice) {
const getUserPromise = admin.database()
.ref(`/users/${userId}`)
.once('value');
const getUserPriceAlertsPromise = admin.database()
.ref(`/user_alerts/price/${userId}`)
.once('value');
return Promise.all([getUserPromise, getUserPriceAlertsPromise]).then(results => {
const userSnapshot = results[0];
if(!userSnapshot.val()){
return console.log('Not user details', userId)
}
const instanceId = userSnapshot.val().instanceId;
const subscriptionStatus = userSnapshot.val().subscriptionStatus;
const priceAlertSnapshot = results[1];
if(subscriptionStatus != 1){
return console.log('Not Sending alerts. Subscription Expired', userId);
}
// Check if there are any device tokens.
if (!priceAlertSnapshot.hasChildren()) {
return console.log('There are no alerts to send for', comboKey, ", userId:", userId);
}
console.log("Alerts of users fetched for ", comboKey, " : ", priceAlertSnapshot.numChildren(), ", userId:", userId);
const promises = [];
priceAlertSnapshot.forEach(function(dataSnapshot) {
promises.push(sendAlertNotification(userId, instanceId, currentPrice, dataSnapshot));
});
return Promise.all(promises);
})
.catch(error => {
console.log("Error getting user alert details:", error, ", userId:", userId);
});
}
function sendAlertNotification(userId, instanceId, currentPrice, dataSnapshot) {
const comboKey = dataSnapshot.val().name;
const comboKeyArray = comboKey.split('-');
const fromCurrency = comboKeyArray[0];
const toCurrency = comboKeyArray[1];
const exchange = comboKeyArray[2];
const alertPrice = dataSnapshot.val().value;
if(priceAlertConditionCheck(currentPrice, dataSnapshot)) {
// Notification details.
const payload = {
notification: {
title: `${fromCurrency} Price Alert`,
body: "You have been notified",
sound: 'default',
tag: comboKey
},
data: {
title: `${fromCurrency} Price Alert`,
body: "You have been notified",
name: comboKey,
sound: 'default',
type: "alert"
}
};
// Set the message as high priority and have it expire after 24 hours.
var options = {
priority: "high",
timeToLive: 60 * 10
};
return admin.messaging().sendToDevice(instanceId, payload, options).then(response => {
response.results.forEach((result, index) => {
const error = result.error;
if (error) {
console.error("Failure sending message:", error, " userId:", userId, " token:", instanceId);
}
console.log("Successfully sent message:", response, ", userId:", userId);
});
})
.catch(error => {
console.log("Error sending message:", error, " userId:", userId, " token:", instanceId);
});
}
return;
}
目前数据很小,但我在 Firebase 数据库中仍然遇到 30% 的失败(10 到 15 条记录(。当有 10k 条记录时,这将如何工作?如何防止这些错误?此外,没有这些错误代码"app/"的文档,而只有"消息/"错误的文档。
更新#1:更新的功能:
function createPriceAlertPromise(snapshot) {
const comboKeyArray = snapshot.key.split('-');
const fromCurrency = comboKeyArray[0];
const toCurrency = comboKeyArray[1];
const exchange = comboKeyArray[2];
return rp(createPriceUrl(fromCurrency, toCurrency, exchange),
{resolveWithFullResponse: true}).then(response => {
if (response.statusCode === 200) {
const jsonobj = JSON.parse(response.body);
const currentPrice = jsonobj[toCurrency];
const promises = [];
snapshot.forEach(function(data) {
promises.push(sendAlertNotifications(snapshot.key, data.key, currentPrice));
});
return Promise.all(promises);
}
throw response.body;
}).catch(error => {
console.log('Error fetching price', error);
});
}
更新#2:将函数的超时增加到540秒,但仍收到错误#1
更新#3:更新的功能:错误#1现在消失了,但错误#3仍然存在并且发生的频率更高
// Checks price alerts for users
exports.priceAlertCheck = functions.database.ref('/crons/alerts/price').onWrite(event => {
return admin.database().ref(`/alerts/price`).once('value').then(alertSnapshot => {
const promises = [];
alertSnapshot.forEach(function(dataSnapshot) {
promises.push(createPriceAlertPromise(dataSnapshot));
});
return Promise.all(promises).then(response => {
return deleteFirebaseApp();
})
.catch(function(error) {
return logError(error);
});
});
});
function createPriceAlertPromise(snapshot) {
const comboKeyArray = snapshot.key.split('-');
const fromCurrency = comboKeyArray[0];
const toCurrency = comboKeyArray[1];
const exchange = comboKeyArray[2];
return rp(createPriceUrl(fromCurrency, toCurrency, exchange),
{resolveWithFullResponse: true}).then(response => {
if (response.statusCode === 200) {
const jsonobj = JSON.parse(response.body);
const currentPrice = jsonobj[toCurrency];
const forEachPromise = new Promise(function(resolve) {
const promises = [];
snapshot.forEach(function(data) {
promises.push(sendAlertNotifications(snapshot.key, data.key, currentPrice));
});
resolve(promises);
});
forEachPromise.then(promises => {
return Promise.all(promises);
})
.catch(error => {
return reportError(error, { type: 'database_query', context: 'forEach'});
});
} else {
throw response.body;
}
}).catch(error => {
return reportError(error, { type: 'http_request', context: 'price fetching'});
});
}
您的代码正在使用不处理承诺的节点模块request
。 在处理 Cloud Functions 时,通常更容易在名为 request-promise 的模块周围使用包装器,该模块返回一个 promise,以便您可以以更有用的方式对 Cloud Functions 中运行的代码做出 HTTP 请求的结果做出反应。
开始从函数返回有效承诺后,云函数将等待这些请求完全完成,然后再进行清理。 ECONNRESET 错误是它在请求完成之前清理得太快的症状。 我最近在Firebase的博客文章中写了一点关于这个问题的文章。
第 494 行: response.results.forEach((result, index( => {
应修改为:
返回 RSVP.all(response.results.map((result, index( => {
"forEach" 返回 null,这意味着承诺实际上立即使用 null 解析。"map" 返回一个数组,RSVP.all 确保解析数组中的所有承诺。
目前,该函数在所有异步任务完成之前被终止,这就是您收到错误的原因。
我通过更新"解决"了这个问题:
npm install -g firebase-tools@latest
npm install --save firebase-functions@latest firebase-admin@latest firebase@latest