@@ -1221,23 +1221,88 @@ def new_decide_dir(photo, ensure_exists=True) -> str:
12211221class AdvancedRetryPlugin (JmOptionPlugin ):
12221222 plugin_key = 'advanced-retry'
12231223
1224+ def __init__ (self , option : JmOption ):
1225+ super ().__init__ (option )
1226+ self .retry_config = None
1227+
12241228 def invoke (self ,
12251229 retry_config ,
12261230 ** kwargs ):
1231+ self .require_param (isinstance (retry_config , dict ), '必须配置retry_config为dict' )
1232+ self .retry_config = retry_config
1233+
12271234 new_jm_client : Callable = self .option .new_jm_client
12281235
12291236 def hook_new_jm_client (* args , ** kwargs ):
1230- client : AbstractJmClient = new_jm_client (* args , ** kwargs )
1237+ client : JmcomicClient = new_jm_client (* args , ** kwargs )
12311238 client .domain_retry_strategy = self .request_with_retry
1239+ client .domain_req_failed_counter = {}
1240+ from threading import Lock
1241+ client .domain_counter_lock = Lock ()
12321242 return client
12331243
12341244 self .option .new_jm_client = hook_new_jm_client
12351245
12361246 def request_with_retry (self ,
1237- client ,
1238- request ,
1239- url ,
1240- is_image ,
1247+ client : AbstractJmClient ,
1248+ request : Callable ,
1249+ url : str ,
1250+ is_image : bool ,
12411251 ** kwargs ,
12421252 ):
1243- pass
1253+ """
1254+ 实现如下域名重试机制:
1255+ - 对域名列表轮询请求,配置:retry_rounds
1256+ - 限制单个域名最大失败次数,配置:retry_domain_max_times
1257+ - 轮询域名列表前,根据历史失败次数对域名列表排序,失败多的后置
1258+ """
1259+
1260+ def do_request (domain ):
1261+ url_to_use = url
1262+ if url_to_use .startswith ('/' ):
1263+ # path → url
1264+ url_to_use = client .of_api_url (url , domain )
1265+ client .update_request_with_specify_domain (kwargs , domain , is_image )
1266+ jm_log (client .log_topic (), client .decode (url_to_use ))
1267+ elif is_image :
1268+ # 图片url
1269+ client .update_request_with_specify_domain (kwargs , None , is_image )
1270+
1271+ resp = request (url_to_use , ** kwargs )
1272+ resp = client .raise_if_resp_should_retry (resp , is_image )
1273+ return resp
1274+
1275+ retry_domain_max_times : int = self .retry_config ['retry_domain_max_times' ]
1276+ retry_rounds : int = self .retry_config ['retry_rounds' ]
1277+ for rindex in range (retry_rounds ):
1278+ domain_list = self .get_sorted_domain (client , retry_domain_max_times )
1279+ for i , domain in enumerate (domain_list ):
1280+ if self .failed_count (client , domain ) >= retry_domain_max_times :
1281+ continue
1282+
1283+ try :
1284+ return do_request (domain )
1285+ except Exception as e :
1286+ from common import traceback_print_exec
1287+ traceback_print_exec ()
1288+ jm_log ('req.error' , str (e ))
1289+ self .update_failed_count (client , domain )
1290+
1291+ return client .fallback (request , url , 0 , 0 , is_image , ** kwargs )
1292+
1293+ def get_sorted_domain (self , client : JmcomicClient , times ):
1294+ domain_list = client .get_domain_list ()
1295+ return sorted (
1296+ filter (lambda d : self .failed_count (client , d ) < times , domain_list ),
1297+ key = lambda d : self .failed_count (client , d )
1298+ )
1299+
1300+ # noinspection PyUnresolvedReferences
1301+ def update_failed_count (self , client : AbstractJmClient , domain : str ):
1302+ with client .domain_counter_lock :
1303+ client .domain_req_failed_counter [domain ] = self .failed_count (client , domain ) + 1
1304+
1305+ @staticmethod
1306+ def failed_count (client : JmcomicClient , domain : str ) -> int :
1307+ # noinspection PyUnresolvedReferences
1308+ return client .domain_req_failed_counter .get (domain , 0 )
0 commit comments