def patch_frame_config(MONGO_CONNECT_URL: str = None, RABBITMQ_USER: str = None, RABBITMQ_PASS: str = None, RABBITMQ_HOST: str = None, RABBITMQ_PORT: int = None, RABBITMQ_VIRTUAL_HOST: str = None, REDIS_HOST: str = None, REDIS_PASSWORD: str = None, REDIS_PORT: int = None, REDIS_DB: int = None, NSQD_TCP_ADDRESSES: list = None, NSQD_HTTP_CLIENT_HOST: str = None, NSQD_HTTP_CLIENT_PORT: int = None, KAFKA_BOOTSTRAP_SERVERS: list = None, SQLACHEMY_ENGINE_URL='sqlite:////sqlachemy_queues/queues.db' ): """ 对框架的配置使用猴子补丁的方式进行更改。利用了模块天然是单利的特性。格式参考frame_config.py :return: """ kw = copy.copy(locals()) for var_name, var_value in kw.items(): if var_value is not None: setattr(frame_config, var_name, var_value) nb_print('使用patch_frame_config 函数设置框架配置了。') show_frame_config()
def auto_creat_config_file_to_project_root_path(): """ 在没有使用pycahrm运行代码时候,如果实在cmd 或者 linux 运行, python xx.py, 请在临时会话窗口设置linux export PYTHONPATH=你的项目根目录 ,winwdos set PYTHONPATH=你的项目根目录 :return: """ # print(Path(sys.path[1]).as_posix()) # print((Path(__file__).parent.parent).absolute().as_posix()) # if Path(sys.path[1]).as_posix() in Path(__file__).parent.parent.absolute().as_posix(): # nb_print('不希望在本项目里面创建') # return if '/lib/python' in sys.path[1] or r'\lib\python' in sys.path[ 1] or '.zip' in sys.path[1]: raise EnvironmentError( f'''如果是cmd 或者shell启动而不是pycharm 这种ide启动脚本,请先在会话窗口设置临时PYTHONPATH为你的项目路径, windwos 使用 set PYTHONNPATH=你的当前python项目根目录, linux 使用 export PYTHONPATH=你的当前你python项目根目录, PYTHONPATH 作用是python的基本常识,请百度一下。 需要在会话窗口命令行设置临时的环境变量,而不是修改linux配置文件的方式设置永久环境变量,每个python项目的PYTHONPATH都要不一样,不要在配置文件写死''' ) return # 当没设置pythonpath时候,也不要在 /lib/python36.zip这样的地方创建配置文件。 file_name = Path(sys.path[1]) / Path('distributed_frame_config.py') copyfile( Path(__file__).absolute().parent / Path('frame_config.py'), file_name) nb_print( f'在 {Path(sys.path[1])} 目录下自动生成了一个文件, 请查看或修改 \n "{file_name}:1" 文件')
def use_config_form_distributed_frame_config_module(): """ 自动读取配置。会优先读取启动脚本的目录的distributed_frame_config.py文件。没有则读取项目根目录下的distributed_frame_config.py :return: """ current_script_path = sys.path[0].replace('\\', '/') project_root_path = sys.path[1].replace('\\', '/') inspect_msg = f""" 分布式函数调度框架会自动导入distributed_frame_config模块 当第一次运行脚本时候,函数调度框架会在你的python当前项目的根目录下 {project_root_path} 下,创建一个名为 distributed_frame_config.py 的文件。 自动读取配置,会优先读取启动脚本的所在目录 {current_script_path} 的distributed_frame_config.py文件, 如果没有 {current_script_path}/distributed_frame_config.py 文件,则读取项目根目录 {project_root_path} 下的distributed_frame_config.py做配置。 在 "{project_root_path}/distributed_frame_config.py:1" 文件中,需要按需重新设置要使用到的中间件的键和值,例如没有使用rabbitmq而是使用redis做中间件,则不需要配置rabbitmq。 """ # sys.stdout.write(f'\033[0;33m{time.strftime("%H:%M:%S")}\033[0m "{__file__}:{sys._getframe().f_lineno}" \033[0;30;43m{inspect_msg}\033[0m\n') # noinspection PyProtectedMember sys.stdout.write( f'\033[0;93m{time.strftime("%H:%M:%S")}\033[0m "{__file__}:{sys._getframe().f_lineno}" \033[0;93;100m{inspect_msg}\033[0m\n') try: # noinspection PyUnresolvedReferences # import distributed_frame_config m = importlib.import_module('distributed_frame_config') # nb_print(m.__dict__) nb_print(f'分布式函数调度框架 读取到\n "{m.__file__}:1" 文件里面的变量作为配置了\n') for var_namex, var_valuex in m.__dict__.items(): if var_namex.isupper(): setattr(frame_config, var_namex, var_valuex) except ModuleNotFoundError: nb_print( f'''分布式函数调度框架检测到 你的项目根目录 {project_root_path} 和当前文件夹 {current_script_path} 下没有 distributed_frame_config.py 文件,\n\n''') auto_creat_config_file_to_project_root_path() show_frame_config()
def join_all_consumer_shedual_task_thread(cls): nb_print((cls.schedulal_thread_to_be_join, len(cls.schedulal_thread_to_be_join), '模式:', cls.global_concurrent_mode)) if cls.schedual_task_always_use_thread: for t in cls.schedulal_thread_to_be_join: nb_print(t) t.join() else: if cls.global_concurrent_mode == 1: for t in cls.schedulal_thread_to_be_join: nb_print(t) t.join() elif cls.global_concurrent_mode == 2: # cls.logger.info() nb_print(cls.schedulal_thread_to_be_join) gevent.joinall( cls.schedulal_thread_to_be_join, raise_error=True, ) elif cls.global_concurrent_mode == 3: for g in cls.schedulal_thread_to_be_join: # eventlet.greenthread.GreenThread. nb_print(g) g.wait()
def wait_for_possible_has_finish_all_tasks( queue_name: str, minutes: int, send_stop_to_broker=0, broker_kind: int = 0, ): """ 由于是异步消费,和存在队列一边被消费,一边在推送,或者还有结尾少量任务还在确认消费者实际还没彻底运行完成。 但有时候需要判断 所有任务,务是否完成,提供一个不精确的判断,要搞清楚原因和场景后再慎用。 :param queue_name: 队列名字 :param minutes: 连续多少分钟没任务就判断为消费已完成 :param send_stop_to_broker :发送停止标志到中间件,这回导致消费退出循环调度。 :param broker_kind: 中间件种类 :return: """ if minutes <= 1: raise ValueError('疑似完成任务,判断时间最少需要设置为2分钟内,最好是是10分钟') pb = get_publisher(queue_name, broker_kind=broker_kind) no_task_time = 0 while 1: # noinspection PyBroadException try: message_count = pb.get_message_count() except Exception as e: nb_print(e) message_count = -1 if message_count == 0: no_task_time += 30 else: no_task_time = 0 time.sleep(30) if no_task_time > minutes * 60: break if send_stop_to_broker: pb.publish({'stop': 1}) pb.close()
def show_all_consumer_info(cls): nb_print( f'当前解释器内,所有消费者的信息是:\n {json.dumps(cls.consumers_queue__info_map, indent=4, ensure_ascii=False)}' ) for _, consumer_info in cls.consumers_queue__info_map.items(): sys.stdout.write( f'{time.strftime("%H:%M:%S")} "{consumer_info["where_to_instantiate"]}" \033[0;30;44m{consumer_info["queue_name"]} 的消费者\033[0m\n' )
def auto_creat_config_file_to_project_root_path(): # print(Path(sys.path[1]).as_posix()) # print((Path(__file__).parent.parent).absolute().as_posix()) if Path(sys.path[1]).as_posix() in Path( __file__).parent.parent.absolute().as_posix(): nb_print('不希望在本项目里面创建') return with (Path(sys.path[1]) / Path('distributed_frame_config.py')).open( mode='w', encoding='utf8') as f: f.write(config_file_content)
def auto_creat_config_file_to_project_root_path(): # print(Path(sys.path[1]).as_posix()) # print((Path(__file__).parent.parent).absolute().as_posix()) if Path(sys.path[1]).as_posix() in Path( __file__).parent.parent.absolute().as_posix(): nb_print('不希望在本项目里面创建') return if '/lib/python' in sys.path[1] or r'\lib\python' in sys.path[ 1] or '.zip' in sys.path[1]: return # 当没设置pythonpath时候,也不要在 /lib/python36.zip这样的地方创建配置文件。 with (Path(sys.path[1]) / Path('distributed_frame_config.py')).open( mode='w', encoding='utf8') as f: f.write(config_file_content)
def auto_creat_config_file_to_project_root_path(): # print(Path(sys.path[1]).as_posix()) # print((Path(__file__).parent.parent).absolute().as_posix()) # if Path(sys.path[1]).as_posix() in Path(__file__).parent.parent.absolute().as_posix(): # nb_print('不希望在本项目里面创建') # return if '/lib/python' in sys.path[1] or r'\lib\python' in sys.path[1] or '.zip' in sys.path[1]: return # 当没设置pythonpath时候,也不要在 /lib/python36.zip这样的地方创建配置文件。 file_name = Path(sys.path[1]) / Path('distributed_frame_config.py') with (file_name).open(mode='w', encoding='utf8') as f: nb_print(f'在 {file_name} 目录下自动生成了一个文件, 请查看或修改 \n "{file_name}:1" 文件') f.write(config_file_content)
def __gevent_timeout_deceo(*args, **kwargs): timeout = gevent.Timeout(timeout_t, ) timeout.start() result = None try: result = f(*args, **kwargs) except gevent.Timeout as t: logger_gevent_timeout_deco.error(f'函数 {f} 运行超过了 {timeout_t} 秒') if t is not timeout: nb_print(t) # raise # not my timeout finally: timeout.close() return result
def __evenlet_timeout_deco(*args, **kwargs): timeout = Timeout(timeout_t, ) # timeout.start() # 与gevent不一样,直接start了。 result = None try: result = f(*args, **kwargs) except Timeout as t: logger_evenlet_timeout_deco.error( f'函数 {f} 运行超过了 {timeout_t} 秒') if t is not timeout: nb_print(t) # raise # not my timeout finally: timeout.cancel() return result
def auto_creat_config_file_to_project_root_path(): """ 在没有使用pycahrm运行代码时候,如果实在cmd 或者 linux 运行, python xx.py, 请在临时会话窗口设置linux export PYTHONPATH=你的项目根目录 ,winwdos set PYTHONPATH=你的项目根目录 :return: """ # print(Path(sys.path[1]).as_posix()) # print((Path(__file__).parent.parent).absolute().as_posix()) # if Path(sys.path[1]).as_posix() in Path(__file__).parent.parent.absolute().as_posix(): # nb_print('不希望在本项目里面创建') # return if '/lib/python' in sys.path[1] or r'\lib\python' in sys.path[ 1] or '.zip' in sys.path[1]: return # 当没设置pythonpath时候,也不要在 /lib/python36.zip这样的地方创建配置文件。 file_name = Path(sys.path[1]) / Path('distributed_frame_config.py') with (file_name).open(mode='w', encoding='utf8') as f: nb_print(f'在 {file_name} 目录下自动生成了一个文件, 请查看或修改 \n "{file_name}:1" 文件') f.write(config_file_content)
def show_frame_config(): nb_print('显示当前的项目中间件配置参数') for var_name in dir(frame_config): if var_name.isupper(): var_value = getattr(frame_config, var_name) if var_name == 'MONGO_CONNECT_URL': if re.match('mongodb://.*?:.*?@.*?/.*', var_value): mongo_pass = re.search('mongodb://.*?:(.*?)@', var_value).group(1) mongo_pass_encryption = f'{"*" * (len(mongo_pass) - 2)}{mongo_pass[-1]}' if len( mongo_pass) > 3 else mongo_pass var_value_encryption = re.sub(r':(\w+)@', f':{mongo_pass_encryption}@', var_value) nb_print(f'{var_name}: {var_value_encryption}') continue if 'PASS' in var_name and var_value is not None and len(var_value) > 3: # 对密码打* nb_print(f'{var_name}: {var_value[0]}{"*" * (len(var_value) - 2)}{var_value[-1]}') else: nb_print(f'{var_name}: {var_value}') print('\n')
def f2(x): time.sleep(3) nb_print(x * 10)
class CustomEventletPoolExecutor(greenpool.GreenPool): def __init__(self, *args, **kwargs): super().__init__(*args, **kwargs) check_evenlet_monkey_patch() atexit.register(self.shutdown) def submit(self, *args, **kwargs): # 保持为一直的公有用法。 # nb_print(args) self.spawn_n(*args, **kwargs) # self.spawn_n(*args, **kwargs) def shutdown(self): self.waitall() if __name__ == '__main__': # greenpool.GreenPool.waitall() monkey_patch(all=True) def f2(x): time.sleep(2) nb_print(x) pool = CustomEventletPoolExecutor(4) for i in range(15): nb_print(f'放入{i}') pool.submit(evenlet_timeout_deco(8)(f2), i)
def f2(x): time.sleep(2) nb_print(x)
return threading.active_count() if __name__ == '__main__': from function_scheduling_distributed_framework.utils import decorators from function_scheduling_distributed_framework.concurrent_pool.bounded_threadpoolexcutor import BoundedThreadPoolExecutor # @decorators.keep_circulating(1) def f1(a): time.sleep(0.2) nb_print(f'{a} 。。。。。。。') # raise Exception('抛个错误测试') # show_current_threads_num() pool = CustomThreadPoolExecutor(200).set_log_level(10).set_min_workers() # pool = BoundedThreadPoolExecutor(200) # 测试对比原来写的BoundedThreadPoolExecutor show_current_threads_num(sleep_time=5) for i in range(300): time.sleep( 0.3 ) # 这里的间隔时间模拟,当任务来临不密集,只需要少量线程就能搞定f1了,因为f1的消耗时间短,不需要开那么多线程,CustomThreadPoolExecutor比BoundedThreadPoolExecutor 优势之一。 pool.submit(f1, str(i)) nb_print(6666) # pool.shutdown(wait=True) pool.submit(f1, 'yyyy') # 下面测试阻塞主线程退出的情况。注释掉可以测主线程退出的情况。 while True: time.sleep(10)
def _show_current_threads_num(): while True: # logger_show_current_threads_num.info(f'{process_name} 进程 的 并发数量是 --> {threading.active_count()}') nb_print( f'{process_name} 进程 的 线程数量是 --> {threading.active_count()}') time.sleep(sleep_time)
def use_config_form_distributed_frame_config_module(): """ 自动读取配置。会优先读取启动脚本的目录的distributed_frame_config.py文件。没有则读取项目根目录下的distributed_frame_config.py :return: """ current_script_path = sys.path[0].replace('\\', '/') project_root_path = sys.path[1].replace('\\', '/') inspect_msg = f""" 分布式函数调度框架,设置配置有两种方式。两种方式的目的相同,就是使用猴子补丁的方式修改此框架的frame_config模中块的变量。 1)第一种方式,自动读取配置文件方式 分布式函数调度框架会尝试自动导入distributed_frame_config模块 请在你的python当前项目的根目录下 {project_root_path} 或 当前文件夹 {current_script_path} 文件夹下,创建一个名为 distributed_frame_config.py 的文件,并在文件中定义例子里面的python常量。 自动读取配置,会优先读取启动脚本的所在目录 {current_script_path} 的distributed_frame_config.py文件, 如果没有 {current_script_path}/distributed_frame_config.py 文件,则读取项目根目录 {project_root_path} 下的distributed_frame_config.py做配置。 内容例子如下,distributed_frame_config模块需要按需必须包含以下变量,需要按需重新设置要使用到的中间件的键和值,例如没有使用rabbitmq而是使用redis做中间件,则不需要配置rabbitmq。 \033[0;97;40m MONGO_CONNECT_URL = f'mongodb://*****:*****@127.0.01:27017/admin' RABBITMQ_USER = '******' RABBITMQ_PASS = '******' RABBITMQ_HOST = '127.0.0.1' RABBITMQ_PORT = 5672 RABBITMQ_VIRTUAL_HOST = 'rabbitmq_virtual_host' REDIS_HOST = '127.0.0.1' REDIS_PASSWORD = '******' REDIS_PORT = 6379 REDIS_DB = 7 NSQD_TCP_ADDRESSES = ['127.0.0.1:4150'] NSQD_HTTP_CLIENT_HOST = '127.0.0.1' NSQD_HTTP_CLIENT_PORT = 4151 KAFKA_BOOTSTRAP_SERVERS = ['127.0.0.1:9092'] SQLACHEMY_ENGINE_URL ='sqlite:////sqlachemy_queues/queues.db' ROCKETMQ_NAMESRV_ADDR = '192.168.199.202:9876' # nb_log包的第几个日志模板,内置了7个模板,可以在你当前项目根目录下的nb_log_config.py文件扩展模板。 NB_LOG_FORMATER_INDEX_FOR_CONSUMER_AND_PUBLISHER = 7 # 7是简短的不可跳转,5是可点击跳转的 TIMEZONE = 'Asia/Shanghai' \033[0m \033[0;93;100m 2)第二种方式,手动调用猴子补丁函数的方式 如果你没有在python当前项目的根目录下 {project_root_path} 或 当前文件夹 {current_script_path} 文件夹下建立 distributed_frame_config.py 这个文件, 也可以使用第二种配置方式,调用 patch_frame_config 函数进行框架配置设置 \033[0m \033[0;97;40m from function_scheduling_distributed_framework import patch_frame_config, show_frame_config # 初次接触使用,可以不安装任何中间件,使用本地持久化队列。正式墙裂推荐安装rabbitmq。 # 使用打猴子补丁的方式修改框架配置。这里为了演示,列举了所有中间件的参数, # 实际是只需要对使用到的中间件的配置进行赋值即可。 patch_frame_config(MONGO_CONNECT_URL='mongodb://*****:*****@xx.90.89.xx:27016/admin', RABBITMQ_USER='******', RABBITMQ_PASS='******', RABBITMQ_HOST='1xx.90.89.xx', RABBITMQ_PORT=5672, RABBITMQ_VIRTUAL_HOST='test_host', REDIS_HOST='1xx.90.89.xx', REDIS_PASSWORD='******', REDIS_PORT=6543, REDIS_DB=7, NSQD_TCP_ADDRESSES=['xx.112.34.56:4150'], NSQD_HTTP_CLIENT_HOST='12.34.56.78', NSQD_HTTP_CLIENT_PORT=4151, KAFKA_BOOTSTRAP_SERVERS=['12.34.56.78:9092'], SQLACHEMY_ENGINE_URL = 'mysql+pymysql://root:[email protected]:3306/sqlachemy_queues?charset=utf8', ROCKETMQ_NAMESRV_ADDR = '192.168.199.202:9876', NB_LOG_FORMATER_INDEX_FOR_CONSUMER_AND_PUBLISHER = 7 # 7是简短的不可跳转,5是可点击跳转的 ) show_frame_config() \033[0m """ # sys.stdout.write(f'\033[0;33m{time.strftime("%H:%M:%S")}\033[0m "{__file__}:{sys._getframe().f_lineno}" \033[0;30;43m{inspect_msg}\033[0m\n') # noinspection PyProtectedMember sys.stdout.write( f'\033[0;93m{time.strftime("%H:%M:%S")}\033[0m "{__file__}:{sys._getframe().f_lineno}" \033[0;93;100m{inspect_msg}\033[0m\n' ) try: # noinspection PyUnresolvedReferences # import distributed_frame_config m = importlib.import_module('distributed_frame_config') # nb_print(m.__dict__) nb_print(f'分布式函数调度框架 读取到\n "{m.__file__}:1" 文件里面的变量作为配置了\n') for var_namex, var_valuex in m.__dict__.items(): if var_namex.isupper(): setattr(frame_config, var_namex, var_valuex) except ModuleNotFoundError: nb_print( f'''分布式函数调度框架检测到 你的项目根目录 {project_root_path} 和当前文件夹 {current_script_path} 下没有 distributed_frame_config.py 文件, 无法使用第一种方式做配置。 请你务必使用第二种方式,调用 patch_frame_config 函数打猴子补丁进行框架的配置进行设置, patch_frame_config 函数要放在生成消费者 发布者之前运行\n\n''') auto_creat_config_file_to_project_root_path() nb_print( f'在 {project_root_path} 目录下自动生成了一个文件, 请查看或修改 \n "{project_root_path}/distributed_frame_config.py:1" 文件' ) show_frame_config()
def f1(a): time.sleep(0.2) nb_print(f'{a} 。。。。。。。')
def f1(a): time.sleep(2) # 可修改这个数字测试多线程数量调节功能。 nb_print(f'{a} 。。。。。。。') return a * 10
def submit(self, fn: Callable, *args, **kwargs): self._q.put((fn, args, kwargs)) def joinall(self): gevent.joinall(self.g_list) def joinall_in_new_thread(self): threading.Thread(target=self.joinall) def __atexit(self): self.logger.critical('想即将退出程序。') self.joinall() if __name__ == '__main__': monkey.patch_all(thread=False) def f2(x): time.sleep(3) nb_print(x * 10) pool = GeventPoolExecutor3(40) for i in range(20): time.sleep(0.1) nb_print(f'放入{i}') pool.submit(gevent_timeout_deco(8)(f2), i) # pool.joinall_in_new_thread() nb_print(66666666)
def f1(a): time.sleep(0.2) # 可修改这个数字测试多线程数量调节功能。 nb_print(f'{a} 。。。。。。。')
def __init__(self, x, y, z): in_param = copy.deepcopy(locals()) nb_print(f'执行初始化啦, {in_param}')