请求失败了 Scrapy 会重试几次?哪些状态码和异常会重试?两次重试之间等多久?重试的请求排在什么位置?这篇用能随意返回状态码、断开连接、变慢的服务器把 RetryMiddleware 的每个开关实测一遍。
RETRY_ENABLED / RETRY_TIMES / RETRY_HTTP_CODES / RETRY_PRIORITY_ADJUST / RETRY_GIVE_UP_LOG_LEVEL = True 2 [500, 502, 503, 504, 522, 524, 408, 429] -1 ERROR RETRY_EXCEPTIONS = ['scrapy.exceptions.CannotResolveHostError', 'scrapy.exceptions.DownloadConnectionRefusedError', 'scrapy.exceptions.DownloadFailedError', 'scrapy.exceptions.DownloadTimeoutError', 'scrapy.exceptions.ResponseDataLossError', 'twisted.internet.error.ConnectionDone', 'twisted.internet.error.ConnectError', 'twisted.internet.error.ConnectionLost', <class 'OSError'>, 'scrapy.core.downloader.handlers.http11.TunnelError']
RETRY_HTTP_CODES 里的状态码和 RETRY_EXCEPTIONS 里的异常会触发重试;404、403、400 这类不会。
状态码 服务器收到 retry/count max_reached 谁被调用 500 3 2 1 errback(HttpError) 502 3 2 1 errback(HttpError) 503 3 2 1 errback(HttpError) 504 3 2 1 errback(HttpError) 522 3 2 1 errback(HttpError) 524 3 2 1 errback(HttpError) 408 3 2 1 errback(HttpError) 429 3 2 1 errback(HttpError) 404 1 0 0 errback(HttpError) 403 1 0 0 errback(HttpError) 400 1 0 0 errback(HttpError) 301 1 0 0 errback(HttpError) 重试之间的间隔(500 的三次请求到达时间差):1 ms 1 ms(没有退避) stats:retry/count=2, retry/max_reached=1, retry/reason_count/500 Internal Server Error=2 日志:[scrapy.downloadermiddlewares.retry] ERROR: Gave up retrying <GET http://127.0.0.1:9960/500> (failed 3 times):
RETRY_TIMES = 2 是重试次数,不含原请求;用尽后放弃,响应(或异常)继续交给后面的流程,通常是 HttpError 到 errback。
情形 重试次数 errback 里的异常类型
连接被拒 2 scrapy.exceptions.DownloadConnectionRefusedError
retry/reason_count: {'scrapy.exceptions.DownloadConnectionRefusedError': 2}
读取超时 2 scrapy.exceptions.DownloadTimeoutError (服务器收到 3 次)
retry/reason_count: {'scrapy.exceptions.DownloadTimeoutError': 2}
收到请求后断开连接 2 scrapy.exceptions.DownloadFailedError (服务器收到 3 次)
retry/reason_count: {'scrapy.exceptions.DownloadFailedError': 2}
DNS 失败 2 scrapy.exceptions.CannotResolveHostError
retry/reason_count: {'scrapy.exceptions.CannotResolveHostError': 2}重试紧接着失败发生,间隔只有 1 毫秒。需要退避就得自己写中间件或用 DOWNLOAD_DELAY / AutoThrottle。
各请求被服务器收到的次数:default=3, dont=1, five=6, zero=1(路径里 /500?tag=… 带了 query 所以按 query 统计)
重试请求是原请求的副本:优先级 +RETRY_PRIORITY_ADJUST(默认 -1)、dont_filter = True、meta 里多了 retry_times,并回到调度器重新排队。
默认 RETRY_PRIORITY_ADJUST = -1:服务器收到的顺序 a b c d a a RETRY_PRIORITY_ADJUST = +10:服务器收到的顺序 a a a b c d
dont_retry、max_retry_times、priority_adjust、give_up_log_level 这些 meta 键可以覆盖设置。
{'e': 'retry', 'retry_times': 0, 'new_priority': -1, 'dont_filter': True, 't': 162.4}
{'e': 'retry', 'retry_times': 1, 'new_priority': -2, 'dont_filter': True, 't': 164.6}
{'e': '放弃', 'retry_times': 2, 'new_priority': None, 'dont_filter': None, 't': 166.2}
服务器收到 3 次;stats:retry/count=2, retry/max_reached=1, retry/reason_count/empty=2
日志:[scrapy.downloadermiddlewares.retry] ERROR: Gave up retrying <GET http://127.0.0.1:9960/empty> (failed 3 times): emptyget_retry_request() 和中间件共用同一套规则与 stats,适合“200 但内容不对”的情况。