angular 服务工作者的 HTTP 请求超时
Timeout an HTTTP request properly in angular with a service worker
作为我的团队正在添加的新功能的一部分,我被要求在特定的 HTTP Post 请求失败时(当互联网速度慢或没有可用时),我将每 X 秒重试一次请求在 Y 秒的总跨度期间。例如,在 8 秒的时间段内每 1 秒一次。
这是我得出的代码:
return this.carService.saveCar(car)
.pipe(this.isRetryable() ? retryWhen(errors => {
return errors.pipe(mergeMap(response => {
if (response.status === TIMEOUT_EXCEPTION_CODE || response.status === UNKNOWN_EXCEPTION_CODE) {
return of(response).pipe(delay(this.saveCarRetryTimeout)) // X seconds
}
return throwError(response);
}));
}) : tap(),
this.isRetryable() ? timeout(this.saveCarProccessTime) : tap(), // Y Seconds
tap((carId: number) => {
this.logger.info(saveCarBaseLog.setStatus(LogStatus.COMPLETE));
}),
catchError((err) => {
this.logger.error(saveCarBaseLog).setStatus(LogStatus.FAILED));
return throwError(err);
}));
isRetryable() 函数只是检查我们是否同时设置了 X 和 Y 配置,因此它不会影响进程。
这样做并看到它在本地和开发环境中都运行良好后,我们上传了版本。第二天我们遇到了一个问题 - 在 preprod 和 prod 环境中,一些汽车被保存了两次。
经过我的调查,这个问题似乎来自我们的服务人员——每当发生完全超时时,请求本身就会超时,尽管与之关联的 FETCH 请求永远不会被取消,这会在互联网刚好出现时出现问题慢(FETCH 请求最终成功,但我们没有得到任何相关指示)。
我真的不知道在这里做什么,所以欢迎任何帮助!
我不能上传网络截图,因为它是私有网络,
但在 chrome 的网络部分中,它看起来像这样:
POST 请求 - saveCar - XHR - 504 超时
POST 请求 - (ServiceWorker) saveCar - FETCH - 504 超时
POST 请求 - saveCar - XHR - 504 超时
POST 请求 - (ServiceWorker) saveCar - FETCH - 504 超时
POST 请求 - saveCar - XHR - 504 超时
POST 请求 - (ServiceWorker) saveCar - FETCH - 200 成功(有问题的那个)
问题
稍微简化后,我是这样理解你到目前为止实现的逻辑的:
class arbitraryClass{
/* ... arbitrary code ... */
arbitraryMethod(){
return this.carService.saveCar(car).pipe(
this.retryTimeoutLogic(),
tap({
complete: () => this.logger.info(saveCarBaseLog.setStatus(LogStatus.COMPLETE)),
error: err => this.logger.error(saveCarBaseLog.setStatus(LogStatus.FAILED))
})
);
}
retryTimeoutLogic<T>(): MonoTypeOperatorFunction<T> {
return s => !this.isRetryable() ? s : s.pipe(
retryWhen(errors => errors.pipe(
filter(response => {
if (response.status !== TIMEOUT_EXCEPTION_CODE ||
response.status !== UNKNOWN_EXCEPTION_CODE
) {
throw response;
}
return true;
}),
delay(this.saveCarRetryTimeout) // retry after X seconds
)),
timeout(this.saveCarProccessTime) // error after Y Seconds
);
}
}
问题是当 timeout(this.saveCarProccessTime)
抛出错误时会发生什么。它在源上调用 unsubscribe
然后发出错误 downstream
.
这意味着 this.carService.saveCar(car)
需要一个取消订阅方法,可以取消中途甚至最近完成的请求(由于异步内容可能会自行排序)。
你需要看看那里。
... 或
另一种可能的解决方案
切勿取消飞行中的请求。服务器拥有唯一的真实来源,如果您没有被告知 upstream
(服务器)失败,请始终假设 saveCar
可能仍会成功。
y 秒后停止重试。您将获得另一个 504 超时,您可以将其直接扔给消费者以处理他们的决定(大概与 this.isRetryable()
返回 false 时他们的处理方式相同)。
retryTimeoutLogic<T>(): MonoTypeOperatorFunction<T> {
return s => !this.isRetryable() ? s : defer(() => {
// The time when we stop retrying on TIMEOUT_EXCEPTION_CODE &
// UNKNOWN_EXCEPTION_CODE. After this time we rethrow instead
const timeoutDate = new Date().getMilliseconds() + this.saveCarProccessTime
return s.pipe(
retryWhen(errors => errors.pipe(
filter(response => {
if (response.status !== TIMEOUT_EXCEPTION_CODE ||
response.status !== UNKNOWN_EXCEPTION_CODE ||
new Date().getMilliseconds() > timeoutDate
) {
throw response;
}
return true;
}),
delay(this.saveCarRetryTimeout) // retry after X seconds
))
);
})
}
作为我的团队正在添加的新功能的一部分,我被要求在特定的 HTTP Post 请求失败时(当互联网速度慢或没有可用时),我将每 X 秒重试一次请求在 Y 秒的总跨度期间。例如,在 8 秒的时间段内每 1 秒一次。 这是我得出的代码:
return this.carService.saveCar(car)
.pipe(this.isRetryable() ? retryWhen(errors => {
return errors.pipe(mergeMap(response => {
if (response.status === TIMEOUT_EXCEPTION_CODE || response.status === UNKNOWN_EXCEPTION_CODE) {
return of(response).pipe(delay(this.saveCarRetryTimeout)) // X seconds
}
return throwError(response);
}));
}) : tap(),
this.isRetryable() ? timeout(this.saveCarProccessTime) : tap(), // Y Seconds
tap((carId: number) => {
this.logger.info(saveCarBaseLog.setStatus(LogStatus.COMPLETE));
}),
catchError((err) => {
this.logger.error(saveCarBaseLog).setStatus(LogStatus.FAILED));
return throwError(err);
}));
isRetryable() 函数只是检查我们是否同时设置了 X 和 Y 配置,因此它不会影响进程。
这样做并看到它在本地和开发环境中都运行良好后,我们上传了版本。第二天我们遇到了一个问题 - 在 preprod 和 prod 环境中,一些汽车被保存了两次。 经过我的调查,这个问题似乎来自我们的服务人员——每当发生完全超时时,请求本身就会超时,尽管与之关联的 FETCH 请求永远不会被取消,这会在互联网刚好出现时出现问题慢(FETCH 请求最终成功,但我们没有得到任何相关指示)。 我真的不知道在这里做什么,所以欢迎任何帮助!
我不能上传网络截图,因为它是私有网络, 但在 chrome 的网络部分中,它看起来像这样:
POST 请求 - saveCar - XHR - 504 超时
POST 请求 - (ServiceWorker) saveCar - FETCH - 504 超时
POST 请求 - saveCar - XHR - 504 超时
POST 请求 - (ServiceWorker) saveCar - FETCH - 504 超时
POST 请求 - saveCar - XHR - 504 超时
POST 请求 - (ServiceWorker) saveCar - FETCH - 200 成功(有问题的那个)
问题
稍微简化后,我是这样理解你到目前为止实现的逻辑的:
class arbitraryClass{
/* ... arbitrary code ... */
arbitraryMethod(){
return this.carService.saveCar(car).pipe(
this.retryTimeoutLogic(),
tap({
complete: () => this.logger.info(saveCarBaseLog.setStatus(LogStatus.COMPLETE)),
error: err => this.logger.error(saveCarBaseLog.setStatus(LogStatus.FAILED))
})
);
}
retryTimeoutLogic<T>(): MonoTypeOperatorFunction<T> {
return s => !this.isRetryable() ? s : s.pipe(
retryWhen(errors => errors.pipe(
filter(response => {
if (response.status !== TIMEOUT_EXCEPTION_CODE ||
response.status !== UNKNOWN_EXCEPTION_CODE
) {
throw response;
}
return true;
}),
delay(this.saveCarRetryTimeout) // retry after X seconds
)),
timeout(this.saveCarProccessTime) // error after Y Seconds
);
}
}
问题是当 timeout(this.saveCarProccessTime)
抛出错误时会发生什么。它在源上调用 unsubscribe
然后发出错误 downstream
.
这意味着 this.carService.saveCar(car)
需要一个取消订阅方法,可以取消中途甚至最近完成的请求(由于异步内容可能会自行排序)。
你需要看看那里。
... 或
另一种可能的解决方案
切勿取消飞行中的请求。服务器拥有唯一的真实来源,如果您没有被告知 upstream
(服务器)失败,请始终假设 saveCar
可能仍会成功。
y 秒后停止重试。您将获得另一个 504 超时,您可以将其直接扔给消费者以处理他们的决定(大概与 this.isRetryable()
返回 false 时他们的处理方式相同)。
retryTimeoutLogic<T>(): MonoTypeOperatorFunction<T> {
return s => !this.isRetryable() ? s : defer(() => {
// The time when we stop retrying on TIMEOUT_EXCEPTION_CODE &
// UNKNOWN_EXCEPTION_CODE. After this time we rethrow instead
const timeoutDate = new Date().getMilliseconds() + this.saveCarProccessTime
return s.pipe(
retryWhen(errors => errors.pipe(
filter(response => {
if (response.status !== TIMEOUT_EXCEPTION_CODE ||
response.status !== UNKNOWN_EXCEPTION_CODE ||
new Date().getMilliseconds() > timeoutDate
) {
throw response;
}
return true;
}),
delay(this.saveCarRetryTimeout) // retry after X seconds
))
);
})
}