使HTTTP请求超时,使其与服务工作者处于正确的角度



作为我的团队正在添加的一项新功能的一部分,有人问我,当特定的HTTPPost请求失败时(当互联网速度很慢或没有可用的互联网时(,我将在总共Y秒的时间内每X秒重试一次请求。例如,在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配置,所以它不会影响进程。

在这样做之后,看到它在本地和开发环境中都很好,我们上传了这个版本。第二天,我们遇到了一个问题——在预编程和生产环境中,一些汽车被保存了两次。经过我的调查,这个问题似乎来自我们的服务人员——每当出现完全超时时,请求本身就会超时,尽管与之相关的FETCH请求从未被取消,这会在互联网速度缓慢时造成问题(FETCH请求最终成功,我们没有得到任何指示(。我真的不知道在这里该做什么,所以欢迎任何帮助!

我无法上传网络截图,因为这是一个私人网络,但在chrome的网络部分,它看起来是这样的:

POST请求-saveCar-XHR-504超时
POST请求-(ServiceWorker(saveCar-FETCH-504超时
POST要求-saveCar-XHR 504超时
POST要求-(ServiceWork(saveCar-FETCH 504超时
POST要求-saveCar-XHTML-504超时POST要求–(ServiceWorkers(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)需要一个取消订阅方法,该方法可以取消飞行途中甚至最近完成的请求(由于异步内容可能会自行订购(。

你需要看看那里。

另一种可能的解决方案

永远不要取消飞行中的请求。服务器拥有唯一的真相来源,始终假设saveCar可能仍然成功,如果你没有被告知upstream(服务器(它已经失败。

只需在y秒后停止重试。您将获得另一个504 Timeout,您可以将其直接提交给消费者,以按照他们的决定进行处理(可能与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
))
);
})
}

最新更新