def _sync_actions( class_: str, mode_sync: str = None, only_sync=False, beat_sync=True, ): """ @param class_: @param mode_sync: 是否同步消息队列。False:同步本机任务队列,True:同步Redis订阅任务 @param only_sync: @param beat_sync: @return: """ logger.info(f"<TaskManager> Sync{mode_sync.title()} || 正在同步<{class_}>任务队列...") # TODO 原子化同步行为 rc = RedisClient() # 拷贝生成队列,需使用copy()完成拷贝,否则pop()会影响actions-list本体 # [A-Cloud,B-Cloud, ...] task_list: list = actions.__all__.copy() random.shuffle(task_list) # 在本机环境中生成任务并加入消息队列 if mode_sync == 'upload': # 临时方案,解决链接溢出问题 if round(rc.__len__(REDIS_SECRET_KEY.format(class_)) * 1.25) > SINGLE_TASK_CAP: logger.warning("<TaskManager> UploadHijack -- 连接池任务已溢出,上传任务被劫持") return None # 持续实例化采集任务 while True: if task_list.__len__() == 0: logger.success("<TaskManager> EmptyList -- 本机任务为空或已完全生成") break else: slave_ = task_list.pop() # 将相应的任务执行语句转换成exec语法 expr = f'from src.BusinessLogicLayer.cluster.slavers.actions import {slave_}\n' \ f'{slave_}(beat_sync={beat_sync}).run()' # 将执行语句同步至消息队列 rc.sync_message_queue(mode='upload', message=expr) # 节拍同步线程锁 if only_sync: logger.warning("<TaskManager> OnlySync -- 触发节拍同步线程锁,仅上传一枚原子任务") break logger.info(f"<TaskManager> 本节点任务({actions.__all__.__len__()})已同步至消息队列," f"待集群接收订阅后既可完成后续任务") # 同步分布式消息队列的任务 elif mode_sync == 'download': while True: # 判断同步状态 # 防止过载。当本地缓冲任务即将突破容载极限时停止同步 # _state 状态有三,continue/offload/stop _state = _is_overflow(task_name=class_, rc=rc) if _state != 'continue': return _state # 获取原子任务,该任务应已封装为exec语法 # todo 将入队操作封装到redis里,以获得合理的循环退出条件 atomic = rc.sync_message_queue(mode='download') # 若原子有效则同步数据 if atomic: # 将执行语句推送至Poseidon本机消息队列 Middleware.poseidon.put_nowait(atomic) logger.info(f'<TaskManager> offload atomic<{class_}>') # 节拍同步线程锁 if only_sync: logger.warning(f"<TaskManager> OnlySync -- <{class_}>触发节拍同步线程锁,仅下载一枚原子任务") return 'offload' # 否则打印警告日志并提前退出同步 else: logger.warning(f"<TaskManager> SyncFinish -- <{class_}>无可同步任务") break elif mode_sync == 'force_run': for slave_ in task_list: # force_run :适用于单机部署或单步调试下 _state = _is_overflow(task_name=class_, rc=rc) # 需要确保无溢出风险,故即使是force_run的启动模式,任务执行数也不应逾越任务容载数 if _state == 'stop': return 'stop' # 将相应的任务执行语句转换成exec语法 expr = f'from src.BusinessLogicLayer.cluster.slavers.actions import {slave_}\n' \ f'{slave_}(beat_sync={beat_sync}).run()' # 将执行语句推送至Poseidon本机消息队列 Middleware.poseidon.put_nowait(expr) # 在force_run模式下仍制约于节拍同步线程锁 # 此举服务于主机的订阅补充操作 # 优先级更高,不受队列可用容载影响强制中断同步操作 if only_sync: logger.warning(f"<TaskManager> OnlySync -- <{class_}>触发节拍同步线程锁,仅下载一枚原子任务") return 'stop' else: logger.success(f"<TaskManager> ForceCollect" f" -- 已将本地预设任务({actions.__all__.__len__()})录入待执行队列") return 'offload'
def _sync_actions( class_: str, mode_sync: str = None, only_sync=False, beat_sync=True, ): """ @param class_: @param mode_sync: 是否同步消息队列。False:同步本机任务队列,True:同步Redis订阅任务 @param only_sync: @param beat_sync: @return: """ logger.info( f"<TaskManager> Sync{mode_sync.title()} || 正在同步<{class_}>任务队列...") # TODO 原子化同步行为 rc = RedisClient() # 节拍停顿 _state = _is_overflow(task_name=class_, rc=rc) if _state == 'stop': return _state sync_queue: list = ActionShunt(class_, silence=True, beat_sync=beat_sync).shunt() random.shuffle(sync_queue) # 在本机环境中生成任务并加入消息队列 if mode_sync == 'upload': # fixme:临时方案:解决链接溢出问题 if round(rc.__len__(REDIS_SECRET_KEY.format(class_)) * 1.25) > SINGLE_TASK_CAP: logger.warning("<TaskManager> UploadHijack -- 连接池任务即将溢出,上传任务被劫持") return None # 持续实例化采集任务 for _ in range(sync_queue.__len__()): rc.sync_message_queue(mode='upload', message=class_) # 节拍同步线程锁 if only_sync: logger.warning("<TaskManager> OnlySync -- 触发节拍同步线程锁,仅上传一枚原子任务") break logger.success("<TaskManager> UploadTasks -- 任务上传完毕") # 同步分布式消息队列的任务 elif mode_sync == 'download': async_queue: list = [] while True: # 获取原子任务 atomic = rc.sync_message_queue(mode='download') # 若原子有效则同步数据 if atomic and atomic in CRAWLER_SEQUENCE: # 判断同步状态 # 防止过载。当本地缓冲任务即将突破容载极限时停止同步 # _state 状态有三,continue/offload/stop _state = _is_overflow(task_name=atomic, rc=rc) if _state != 'continue': return _state if async_queue.__len__() == 0: async_queue = ActionShunt(atomic, silence=True, beat_sync=beat_sync).shunt() random.shuffle(async_queue) # 将执行语句推送至Poseidon本机消息队列 Middleware.poseidon.put_nowait(async_queue.pop()) logger.info( f'<TaskManager> offload atomic<{atomic}>({Middleware.poseidon.qsize()})' ) # 节拍同步线程锁 if only_sync: logger.warning( f"<TaskManager> OnlySync -- <{atomic}>触发节拍同步线程锁,仅下载一枚原子任务" ) return 'offload' # 否则打印警告日志并提前退出同步 else: # logger.warning(f"<TaskManager> SyncFinish -- <{atomic}>无可同步任务") return 'offload' elif mode_sync == 'force_run': for slave_ in sync_queue: # force_run :适用于单机部署或单步调试下 # 需要确保无溢出风险,故即使是force_run的启动模式,任务执行数也不应逾越任务容载数 _state = _is_overflow(task_name=class_, rc=rc) if _state != 'continue': return _state # 将执行语句推送至Poseidon本机消息队列 Middleware.poseidon.put_nowait(slave_) # 在force_run模式下仍制约于节拍同步线程锁 # 此举服务于主机的订阅补充操作 # 优先级更高,不受队列可用容载影响强制中断同步操作 if only_sync: logger.warning( f"<TaskManager> OnlySync -- <{class_}>触发节拍同步线程锁,仅下载一枚原子任务") return 'stop' return 'offload'