@@ -79,9 +79,9 @@ def request_with_retry(self,
7979 """
8080 if domain_index >= len (self .domain_list ):
8181 return self .fallback (request , url , domain_index , retry_count , ** kwargs )
82-
82+
8383 url_backup = url
84-
84+
8585 if url .startswith ('/' ):
8686 # path → url
8787 domain = self .domain_list [domain_index ]
@@ -976,21 +976,25 @@ def get_cookies(self):
976976 return cookies
977977
978978
979- class FutureClientProxy (JmcomicClient ):
979+ class PhotoConcurrentFetcherProxy (JmcomicClient ):
980980 """
981- 在Client上做了一层线程池封装来实现异步,对外仍然暴露JmcomicClient的接口,可以看作Client的代理。
982- 除了使用线程池做异步,还通过加锁和缓存结果,实现同一个请求不会被多个线程发出,减少开销
981+ 为了解决 JmApiClient.get_photo_detail 方法的排队调用问题,
982+ 即在访问完photo的接口后,需要另外排队访问获取album和scramble_id的接口。
983+
984+ 这三个接口可以并发请求,这样可以提高效率。
985+
986+ 此Proxy代理了get_photo_detail,实现了并发请求这三个接口,然后组装返回值返回photo。
983987
984988 可通过插件 ClientProxyPlugin 启用本类,配置如下:
985989 ```yml
986990 plugins:
987991 after_init:
988992 - plugin: client_proxy
989993 kwargs:
990- proxy_client_key: cl_proxy_future
994+ proxy_client_key: photo_concurrent_fetcher_proxy
991995 ```
992996 """
993- client_key = 'cl_proxy_future '
997+ client_key = 'photo_concurrent_fetcher_proxy '
994998
995999 class FutureWrapper :
9961000 def __init__ (self , future , after_done_callback ):
@@ -1024,16 +1028,15 @@ def __init__(self,
10241028 executors = ThreadPoolExecutor (max_workers )
10251029
10261030 self .executors = executors
1027- self .future_dict : Dict [str , FutureClientProxy .FutureWrapper ] = {}
1031+ self .future_dict : Dict [str , PhotoConcurrentFetcherProxy .FutureWrapper ] = {}
10281032 from threading import Lock
10291033 self .lock = Lock ()
10301034
10311035 def route_notimpl_method_to_internal_client (self , client ):
10321036
1033- impl_methods = str_to_set ('''
1037+ proxy_methods = str_to_set ('''
10341038 get_album_detail
10351039 get_photo_detail
1036- search
10371040 ''' )
10381041
10391042 # 获取对象的所有属性和方法的名称列表
@@ -1043,7 +1046,7 @@ def route_notimpl_method_to_internal_client(self, client):
10431046 # 判断是否为方法(可调用对象)
10441047 if (not method .startswith ('_' )
10451048 and callable (getattr (client , method ))
1046- and method not in impl_methods
1049+ and method not in proxy_methods
10471050 ):
10481051 setattr (self , method , getattr (client , method ))
10491052
@@ -1055,15 +1058,19 @@ def get_album_detail(self, album_id) -> JmAlbumDetail:
10551058
10561059 def get_future (self , cache_key , task ):
10571060 if cache_key in self .future_dict :
1061+ # cache hit, means that a same task is running
10581062 return self .future_dict [cache_key ]
10591063
10601064 with self .lock :
10611065 if cache_key in self .future_dict :
10621066 return self .future_dict [cache_key ]
10631067
1068+ # after future done, remove it from future_dict.
1069+ # cache depends on self.client instead of self.future_dict
10641070 future = self .FutureWrapper (self .executors .submit (task ),
10651071 after_done_callback = lambda : self .future_dict .pop (cache_key , None )
10661072 )
1073+
10671074 self .future_dict [cache_key ] = future
10681075 return future
10691076
@@ -1115,8 +1122,3 @@ def get_photo_detail(self, photo_id, fetch_album=True, fetch_scramble_id=True) -
11151122 photo .scramble_id = scramble_id
11161123
11171124 return photo
1118-
1119- def search (self , search_query : str , page : int , main_tag : int , order_by : str , time : str ) -> JmSearchPage :
1120- cache_key = f'search_query_{ search_query } _page_{ page } _main_tag_{ main_tag } _order_by_{ order_by } _time_{ time } '
1121- future = self .get_future (cache_key , task = lambda : self .client .search (search_query , page , main_tag , order_by , time ))
1122- return future .result ()
0 commit comments