我將相同的查詢發送到大量(好吧,n=5)相同的端點(一些 kubernetes 集群)并將結果整理在一起。我希望請求并行發出并在一段時間后超時;我希望向用戶報告失敗而不妨礙進一步的進展。
作為所需輸出的示例:
$ node find_broken_pods.js
tomato: error: timed out
Cluster Pod Reason
potato bar-foos Invalid PVC
potato foo-eggnog Invalid image
yoghurt spam-baz Invalid name
$ node find_broken_pods.js
Cluster Pod Reason
potato bar-foos Invalid PVC
potato foo-eggnog Invalid image
yoghurt spam-baz Invalid name
tomato what-bat Insufficient jQuery
第一次,無論出于何種原因,我們都未能從番茄集群中得到答案,但我們能夠列舉其他集群的資源。第二次,沒有超時,我們能夠列出所有內容。
我以前想出了這個:
export async function queryAll(): Promise<{cluster: string; deployments: V1Pod[]}[]> {
const out: {cluster: string; result: V1Pod[]}[] = [];
const promises: Promise<number>[] = [];
for (const cluster of Object.values(CLUSTERS)) {
promises.push(
Promise.race([
new Promise<number>((_, reject) => setTimeout(() => reject(new Error(`${cluster}: timed out`)), 5000)),
new Promise<number>((resolve, _) =>
getAllPods(cluster)
.then(pods => resolve(out.push({cluster: cluster, result: pods})))
),
])
);
}
await Promise.all(promises);
return out;
}
這確實會并行運行所有內容,但單個故障會導致整個函式崩潰和燒毀。我想我可以把它改成這樣:
export async function queryAll(): Promise<{cluster: string; deployments?: V1Deployment[]; error?: string}[]> {
const out: {cluster: string; result?: V1Pod[]; error?: string}[] = [];
const promises: Promise<number>[] = [];
for (const cluster of Object.values(CLUSTERS)) {
promises.push(
Promise.race([
new Promise<number>((resolve, _) =>
setTimeout(() => {
resolve(out.push({cluster: cluster, error: 'timed out'}));
}, 5000)
),
new Promise<number>((resolve, _) =>
getAllPods(cluster)
.then(pods => resolve(out.push({cluster: cluster, result: pods})))
),
])
);
}
await Promise.all(promises);
return out;
}
However, I'm now witnessing both arms of the promise run to completion, that is the out array contains (some) clusters reporting data and either all or none of my clusters reporting a timeout:
- if no timeout is reached,
Promise.allwon't wait for thesetTimeouts, but the node process will (i.e. if I set the timeout to 60 seconds then the node process will only exit after 60 seconds) - if any timeout is reached,
Promise.allwill wait for the timeout to happen... meaning allsetTimeouts end up triggering.
I instead expected that the losing arm of the Promise.race would be killed somehow or prevented from running.
Something tells me my approach is fundamentally broken... how can I improve my fault tolerance?
uj5u.com熱心網友回復:
是的,您有兩個任務正在運行并將push物件添加到out陣列中。沒有什么能阻止他們——Promise.race不會神奇地撤銷為創建傳遞給它的承諾而采取的步驟。
您可以使用每個集群的標志來解決這個問題,無論是否已經報告了超時或結果,這樣比賽的失敗者就不會報告他們的狀態。
但是,僅使用 promise 實作的結果值要簡單得多,這Promise.race已經向前推進了:
interface QueryResult {cluster: string; deployments?: V1Deployment[]; error?: string}
export async function queryAll(): Promise<QueryResult[]> {
const promises: Promise<QueryResult>[] = Object.values(CLUSTERS).map(cluster =>
Promise.race([
new Promise(resolve => {
setTimeout(() => {
resolve({cluster: cluster, error: 'timed out'});
}, 5000)
}),
getAllPods(cluster).then(pods => ({cluster: cluster, result: pods}))
])
);
const out = await Promise.all(promises);
return out;
}
使用這種方法,您甚至可以按照與 相同的順序獲得結果CLUSTERS,而不是按照它們回傳的順序。如果您不想這樣,您仍然可以采用原來的方法push- 只需根據每場比賽的結果來做!
export async function queryAll(): Promise<QueryResult[]> {
const out: QueryResult[] = [];
await Promise.all(Object.values(CLUSTERS).map(cluster =>
Promise.race([
new Promise(resolve => {
setTimeout(() => {
resolve({cluster: cluster, error: 'timed out'});
}, 5000)
}),
getAllPods(cluster).then(pods => ({cluster: cluster, result: pods}))
]).then(result => {
out.push(result);
})
));
return out;
}
順便說一句,也考慮一下- 你也可以從你的超時承諾中得到它Promise.allSettled。Promise.allreject
uj5u.com熱心網友回復:
看起來這足以滿足我的需求,但仍然不是:
export async function queryAll(): Promise<{cluster: string; deployments: V1Pod[]}[]> {
const out: {cluster: string; result: V1Pod[]}[] = [];
const promises: Promise<number>[] = [];
for (const cluster of Object.values(CLUSTERS)) {
promises.push(
Promise.race([
new Promise<number>((_, reject) => setTimeout(() => reject(new Error(`${cluster}: timed out`)), 5000)),
new Promise<number>((resolve, _) =>
getAllPods(cluster)
.then(pods => resolve(out.push({cluster: cluster, result: pods})),
error => {
process.stderr.write(`${cluster}: ${error}\n`);
resolve(0);
})
),
]).catch(error => {
process.stderr.write(`${cluster}: ${error}\n`);
return 0;
})
);
}
await Promise.all(promises);
return out;
}
由于重復的錯誤處理,這可能會導致此輸出,我不太介意;例如,如果我強制 /etc/hosts 失敗,我會得到:
$ node find_broken_pods.js
tomato: Error: timed out
Cluster Pod Reason
potato bar-foos Invalid PVC
potato foo-eggnog Invalid image
yoghurt spam-baz Invalid name
tomato: Error: connect ECONNREFUSED 0.0.0.0:443
我不太高興的是,該節點專門等待了足夠長的時間讓任何庫拋出 ECONNREFUSED,這發生在我的程式生成其輸出幾秒鐘后;我首先這樣做的部分原因是存在故障模式,其中行程只是掛起等待服務器回應被防火墻吃掉的請求。
轉載請註明出處,本文鏈接:https://www.uj5u.com/yidong/414489.html
標籤:
上一篇:登錄后無限請求
