funboost 50.3__py3-none-any.whl → 50.4__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.

Potentially problematic release.


This version of funboost might be problematic. Click here for more details.

Files changed (45) hide show
  1. funboost/__init__.py +1 -1
  2. funboost/constant.py +4 -0
  3. funboost/consumers/base_consumer.py +93 -98
  4. funboost/consumers/celery_consumer.py +1 -1
  5. funboost/consumers/dramatiq_consumer.py +0 -5
  6. funboost/consumers/grpc_consumer.py +1 -1
  7. funboost/consumers/http_consumer.py +1 -1
  8. funboost/consumers/http_consumer_aiohttp_old.py +1 -1
  9. funboost/consumers/huey_consumer.py +2 -5
  10. funboost/consumers/kafka_consumer.py +0 -2
  11. funboost/consumers/kafka_consumer_manually_commit.py +0 -1
  12. funboost/consumers/kombu_consumer.py +0 -39
  13. funboost/consumers/mysql_cdc_consumer.py +1 -3
  14. funboost/consumers/pulsar_consumer.py +10 -5
  15. funboost/consumers/rabbitmq_amqpstorm_consumer.py +7 -8
  16. funboost/consumers/rabbitmq_complex_routing_consumer.py +54 -0
  17. funboost/consumers/redis_consumer.py +1 -1
  18. funboost/consumers/redis_consumer_ack_able.py +1 -1
  19. funboost/consumers/redis_consumer_ack_using_timeout.py +2 -6
  20. funboost/consumers/redis_consumer_priority.py +1 -1
  21. funboost/consumers/redis_stream_consumer.py +1 -3
  22. funboost/consumers/tcp_consumer.py +1 -1
  23. funboost/consumers/udp_consumer.py +1 -1
  24. funboost/consumers/zeromq_consumer.py +1 -1
  25. funboost/contrib/save_function_result_status/__init__.py +0 -0
  26. funboost/contrib/{save_result_status_to_sqldb.py → save_function_result_status/save_result_status_to_sqldb.py} +8 -41
  27. funboost/contrib/save_function_result_status/save_result_status_use_dataset.py +47 -0
  28. funboost/core/booster.py +7 -1
  29. funboost/core/broker_kind__exclusive_config_default_define.py +229 -0
  30. funboost/core/funboost_time.py +3 -82
  31. funboost/core/func_params_model.py +9 -3
  32. funboost/core/helper_funs.py +2 -2
  33. funboost/factories/broker_kind__publsiher_consumer_type_map.py +5 -0
  34. funboost/funboost_config_deafult.py +0 -3
  35. funboost/function_result_web/templates/fun_result_table.html +1 -1
  36. funboost/publishers/base_publisher.py +3 -2
  37. funboost/publishers/rabbitmq_amqpstorm_publisher.py +8 -7
  38. funboost/publishers/rabbitmq_complex_routing_publisher.py +84 -0
  39. funboost/utils/redis_manager.py +11 -5
  40. {funboost-50.3.dist-info → funboost-50.4.dist-info}/METADATA +156 -97
  41. {funboost-50.3.dist-info → funboost-50.4.dist-info}/RECORD +44 -40
  42. {funboost-50.3.dist-info → funboost-50.4.dist-info}/WHEEL +1 -1
  43. funboost-50.3.dist-info/LICENSE +0 -203
  44. {funboost-50.3.dist-info → funboost-50.4.dist-info}/entry_points.txt +0 -0
  45. {funboost-50.3.dist-info → funboost-50.4.dist-info}/top_level.txt +0 -0
funboost/__init__.py CHANGED
@@ -13,7 +13,7 @@ set_frame_config这个模块的 use_config_form_funboost_config_module() 是核
13
13
  这段注释说明和使用的用户无关,只和框架开发人员有关.
14
14
  '''
15
15
 
16
- __version__ = "50.3"
16
+ __version__ = "50.4"
17
17
 
18
18
  from funboost.set_frame_config import show_frame_config
19
19
 
funboost/constant.py CHANGED
@@ -27,6 +27,10 @@ class BrokerEnum:
27
27
  RABBITMQ_AMQPSTORM = 'RABBITMQ_AMQPSTORM' # 使用 amqpstorm 包操作rabbitmq 作为 分布式消息队列,支持消费确认.强烈推荐这个作为funboost中间件。
28
28
  RABBITMQ = RABBITMQ_AMQPSTORM
29
29
 
30
+ # 2025-10 内置新增, 支持rabbitmq 所有路由模式,包括 fanout,direct,topic,headers. 使用概念更复杂
31
+ # 用法见 test_frame/test_broker_rabbitmq/test_rabbitmq_complex_routing 中的demo代码.
32
+ RABBITMQ_COMPLEX_ROUTING = 'RABBITMQ_COMPLEX_ROUTING'
33
+
30
34
  RABBITMQ_RABBITPY = 'RABBITMQ_RABBITPY' # 使用 rabbitpy 包操作rabbitmq 作为 分布式消息队列,支持消费确认,不建议使用
31
35
 
32
36
  """
@@ -13,6 +13,7 @@ import typing
13
13
  import abc
14
14
  import copy
15
15
  from apscheduler.jobstores.memory import MemoryJobStore
16
+ from funboost.core.broker_kind__exclusive_config_default_define import generate_broker_exclusive_config
16
17
  from funboost.core.funboost_time import FunboostTime
17
18
  from pathlib import Path
18
19
  # from multiprocessing import Process
@@ -41,7 +42,6 @@ from funboost.core.serialization import PickleHelper, Serialization
41
42
  from funboost.core.task_id_logger import TaskIdLogger
42
43
  from funboost.constant import FunctionKind
43
44
 
44
-
45
45
  from nb_libs.path_helper import PathHelper
46
46
  from nb_log import (get_logger, LoggerLevelSetterMixin, LogManager, is_main_process,
47
47
  nb_log_config_default)
@@ -74,7 +74,7 @@ from funboost.consumers.redis_filter import RedisFilter, RedisImpermanencyFilter
74
74
  from funboost.factories.publisher_factotry import get_publisher
75
75
 
76
76
  from funboost.utils import decorators, time_util, redis_manager
77
- from funboost.constant import ConcurrentModeEnum, BrokerEnum, ConstStrForClassMethod,RedisKeys
77
+ from funboost.constant import ConcurrentModeEnum, BrokerEnum, ConstStrForClassMethod, RedisKeys
78
78
  from funboost.core import kill_remote_task
79
79
  from funboost.core.exceptions import ExceptionForRequeue, ExceptionForPushToDlxqueue
80
80
 
@@ -93,7 +93,6 @@ class GlobalVars:
93
93
  class AbstractConsumer(LoggerLevelSetterMixin, metaclass=abc.ABCMeta, ):
94
94
  time_interval_for_check_do_not_run_time = 60
95
95
  BROKER_KIND = None
96
- BROKER_EXCLUSIVE_CONFIG_DEFAULT = {} # 每种中间件的概念有所不同,用户可以从 broker_exclusive_config 中传递该种中间件特有的配置意义参数。
97
96
 
98
97
  @property
99
98
  @decorators.synchronized
@@ -164,11 +163,11 @@ class AbstractConsumer(LoggerLevelSetterMixin, metaclass=abc.ABCMeta, ):
164
163
  filter_class = RedisFilter if consumer_params.task_filtering_expire_seconds == 0 else RedisImpermanencyFilter
165
164
  self._redis_filter = filter_class(self._redis_filter_key_name, consumer_params.task_filtering_expire_seconds)
166
165
  self._redis_filter.delete_expire_filter_task_cycle()
167
-
166
+
168
167
  # if self.consumer_params.concurrent_mode == ConcurrentModeEnum.ASYNC and self.consumer_params.specify_async_loop is None:
169
168
  # self.consumer_params.specify_async_loop= get_or_create_event_loop()
170
169
  self._lock_for_count_execute_task_times_every_unit_time = Lock()
171
-
170
+
172
171
  # self._unit_time_for_count = 10 # 每隔多少秒计数,显示单位时间内执行多少次,暂时固定为10秒。
173
172
  # self._execute_task_times_every_unit_time = 0 # 每单位时间执行了多少次任务。
174
173
  # self._execute_task_times_every_unit_time_fail =0 # 每单位时间执行了多少次任务失败。
@@ -189,12 +188,7 @@ class AbstractConsumer(LoggerLevelSetterMixin, metaclass=abc.ABCMeta, ):
189
188
  self.metric_calculation = MetricCalculation(self)
190
189
 
191
190
  self._result_persistence_helper: ResultPersistenceHelper
192
- self._check_broker_exclusive_config()
193
- broker_exclusive_config_merge = dict()
194
- broker_exclusive_config_merge.update(self.BROKER_EXCLUSIVE_CONFIG_DEFAULT)
195
- broker_exclusive_config_merge.update(self.consumer_params.broker_exclusive_config)
196
- # print(broker_exclusive_config_merge)
197
- self.consumer_params.broker_exclusive_config = broker_exclusive_config_merge
191
+ self.consumer_params.broker_exclusive_config = generate_broker_exclusive_config(self.consumer_params.broker_kind,self.consumer_params.broker_exclusive_config,self.logger)
198
192
 
199
193
  self._stop_flag = None
200
194
  self._pause_flag = threading.Event() # 暂停消费标志,从reids读取
@@ -233,7 +227,7 @@ class AbstractConsumer(LoggerLevelSetterMixin, metaclass=abc.ABCMeta, ):
233
227
 
234
228
  self._has_start_delay_task_scheduler = False
235
229
  self._consuming_function_is_asyncio = inspect.iscoroutinefunction(self.consuming_function)
236
- self.custom_init()
230
+
237
231
  # develop_logger.warning(consumer_params._log_filename)
238
232
  # self.publisher_params = PublisherParams(queue_name=consumer_params.queue_name, consuming_function=consumer_params.consuming_function,
239
233
  # broker_kind=self.BROKER_KIND, log_level=consumer_params.log_level,
@@ -244,6 +238,7 @@ class AbstractConsumer(LoggerLevelSetterMixin, metaclass=abc.ABCMeta, ):
244
238
  # broker_exclusive_config=self.consumer_params.broker_exclusive_config)
245
239
  self.publisher_params = BaseJsonAbleModel.init_by_another_model(PublisherParams, self.consumer_params)
246
240
  # print(self.publisher_params)
241
+ self.custom_init()
247
242
  if is_main_process:
248
243
  self.logger.info(f'{self.queue_name} consumer 的消费者配置:\n {self.consumer_params.json_str_value()}')
249
244
 
@@ -261,12 +256,11 @@ class AbstractConsumer(LoggerLevelSetterMixin, metaclass=abc.ABCMeta, ):
261
256
  :return:
262
257
  """
263
258
  if self.consumer_params.is_send_consumer_hearbeat_to_redis:
264
- RedisMixin().redis_db_frame.sadd(RedisKeys.FUNBOOST_ALL_QUEUE_NAMES,self.queue_name)
259
+ RedisMixin().redis_db_frame.sadd(RedisKeys.FUNBOOST_ALL_QUEUE_NAMES, self.queue_name)
265
260
  RedisMixin().redis_db_frame.hmset(RedisKeys.FUNBOOST_QUEUE__CONSUMER_PARAMS,
266
- {self.queue_name: self.consumer_params.json_str_value()})
267
- RedisMixin().redis_db_frame.sadd(RedisKeys.FUNBOOST_ALL_IPS,nb_log_config_default.computer_ip)
268
-
269
-
261
+ {self.queue_name: self.consumer_params.json_str_value()})
262
+ RedisMixin().redis_db_frame.sadd(RedisKeys.FUNBOOST_ALL_IPS, nb_log_config_default.computer_ip)
263
+
270
264
  def _build_logger(self):
271
265
  logger_prefix = self.consumer_params.logger_prefix
272
266
  if logger_prefix != '':
@@ -282,13 +276,6 @@ class AbstractConsumer(LoggerLevelSetterMixin, metaclass=abc.ABCMeta, ):
282
276
  formatter_template=FunboostCommonConfig.NB_LOG_FORMATER_INDEX_FOR_CONSUMER_AND_PUBLISHER, )
283
277
  self.logger.info(f'队列 {self.queue_name} 的日志写入到 {nb_log_config_default.LOG_PATH} 文件夹的 {log_filename} 和 {nb_log.generate_error_file_name(log_filename)} 文件中')
284
278
 
285
- def _check_broker_exclusive_config(self):
286
- broker_exclusive_config_keys = self.BROKER_EXCLUSIVE_CONFIG_DEFAULT.keys()
287
- if self.consumer_params.broker_exclusive_config:
288
- if set(self.consumer_params.broker_exclusive_config.keys()).issubset(broker_exclusive_config_keys):
289
- self.logger.info(f'当前消息队列中间件能支持特殊独有配置 {self.consumer_params.broker_exclusive_config.keys()}')
290
- else:
291
- self.logger.warning(f'当前消息队列中间件含有不支持的特殊配置 {self.consumer_params.broker_exclusive_config.keys()},能支持的特殊独有配置包括 {broker_exclusive_config_keys}')
292
279
 
293
280
  def _check_monkey_patch(self):
294
281
  if self.consumer_params.concurrent_mode == ConcurrentModeEnum.GEVENT:
@@ -406,20 +393,19 @@ class AbstractConsumer(LoggerLevelSetterMixin, metaclass=abc.ABCMeta, ):
406
393
  )
407
394
  }
408
395
  self._delay_task_scheduler = FunboostBackgroundSchedulerProcessJobsWithinRedisLock(timezone=FunboostCommonConfig.TIMEZONE, daemon=False,
409
- jobstores=jobstores # push 方法的序列化带thredignn.lock
410
- )
396
+ jobstores=jobstores # push 方法的序列化带thredignn.lock
397
+ )
411
398
  self._delay_task_scheduler.set_process_jobs_redis_lock_key(
412
399
  RedisKeys.gen_funboost_apscheduler_redis_lock_key_by_queue_name(self.queue_name))
413
400
  elif self.consumer_params.delay_task_apscheduler_jobstores_kind == 'memory':
414
401
  jobstores = {"default": MemoryJobStore()}
415
402
  self._delay_task_scheduler = FsdfBackgroundScheduler(timezone=FunboostCommonConfig.TIMEZONE, daemon=False,
416
- jobstores=jobstores # push 方法的序列化带thredignn.lock
417
- )
403
+ jobstores=jobstores # push 方法的序列化带thredignn.lock
404
+ )
418
405
 
419
406
  else:
420
407
  raise Exception(f'delay_task_apsscheduler_jobstores_kind is error: {self.consumer_params.delay_task_apscheduler_jobstores_kind}')
421
408
 
422
-
423
409
  # self._delay_task_scheduler.add_executor(ApschedulerThreadPoolExecutor(2)) # 只是运行submit任务到并发池,不需要很多线程。
424
410
  # self._delay_task_scheduler.add_listener(self._apscheduler_job_miss, EVENT_JOB_MISSED)
425
411
  self._delay_task_scheduler.start()
@@ -431,7 +417,7 @@ class AbstractConsumer(LoggerLevelSetterMixin, metaclass=abc.ABCMeta, ):
431
417
  @classmethod
432
418
  def _push_apscheduler_task_to_broker(cls, queue_name, msg):
433
419
  funboost_lazy_impoter.BoostersManager.get_or_create_booster_by_queue_name(queue_name).publish(msg)
434
-
420
+
435
421
  @abc.abstractmethod
436
422
  def _shedual_task(self):
437
423
  """
@@ -441,10 +427,23 @@ class AbstractConsumer(LoggerLevelSetterMixin, metaclass=abc.ABCMeta, ):
441
427
  调用 self._submit_task(msg) 方法把它交给我处理就行。”
442
428
 
443
429
  所以无论获取消息是 拉模式 还是推模式 还是轮询模式,无论是是单条获取 还是多条批量多条获取,
444
- 都能轻松扩展任意东西作为funboost的中间件。
430
+ 无论是传统mq,kafka,还是数据库,还是socket grpc tcp,还是kombu,还是python任务框架 celery rq dramtiq,
431
+ 还是文件系统 ,以及火热的 mysql cdc(数据变更捕获) ,都能轻松扩展任意东西作为funboost的中间件。
432
+
433
+ _shedual_task 是万物可作为broker的核心,没有任何东西作为不了broker,扩展性无敌.
445
434
 
446
435
  :return:
447
436
  """
437
+
438
+ """
439
+ 反观celery,由于kombu强行模拟靠拢经典amqp协议,只有rabbitmq作为broker在celery最完美,
440
+ redis在celery作为broker,消费确认ack 使用visibility_timeout,方案简直太糟糕了,
441
+ 强制断电重启程序,要么孤儿消息重回不及时,要么把长耗时消息错误的当做是孤儿消息无限懵逼死循环重新入队.
442
+
443
+ celery实现kafka作为broker,这个issue 提了十几年一直无法完美实现,这就是celery+kombu 的局限性.
444
+ 更别说把 mysql cdc作为celery的broker 了,funboost的设计在这方面是吊打celery.
445
+ """
446
+
448
447
  raise NotImplementedError
449
448
 
450
449
  def _convert_msg_before_run(self, msg: typing.Union[str, dict]) -> dict:
@@ -481,7 +480,7 @@ class AbstractConsumer(LoggerLevelSetterMixin, metaclass=abc.ABCMeta, ):
481
480
  if 'publish_time_format':
482
481
  extra['publish_time_format'] = MsgGenerater.generate_publish_time_format()
483
482
  return msg
484
-
483
+
485
484
  def _user_convert_msg_before_run(self, msg: typing.Union[str, dict]) -> dict:
486
485
  """
487
486
  用户也可以提前清洗数据
@@ -499,9 +498,9 @@ class AbstractConsumer(LoggerLevelSetterMixin, metaclass=abc.ABCMeta, ):
499
498
  function_only_params = delete_keys_and_return_new_dict(kw['body'], )
500
499
  kw['function_only_params'] = function_only_params
501
500
  if self._get_priority_conf(kw, 'do_task_filtering') and self._redis_filter.check_value_exists(
502
- function_only_params,self._get_priority_conf(kw, 'filter_str')): # 对函数的参数进行检查,过滤已经执行过并且成功的任务。
501
+ function_only_params, self._get_priority_conf(kw, 'filter_str')): # 对函数的参数进行检查,过滤已经执行过并且成功的任务。
503
502
  self.logger.warning(f'redis的 [{self._redis_filter_key_name}] 键 中 过滤任务 {kw["body"]}')
504
- self._confirm_consume(kw) # 不运行就必须确认消费,否则会发不能确认消费,导致消息队列中间件认为消息没有被消费。
503
+ self._confirm_consume(kw) # 不运行就必须确认消费,否则会发不能确认消费,导致消息队列中间件认为消息没有被消费。
505
504
  return
506
505
  publish_time = get_publish_time(kw['body'])
507
506
  msg_expire_senconds_priority = self._get_priority_conf(kw, 'msg_expire_senconds')
@@ -520,7 +519,6 @@ class AbstractConsumer(LoggerLevelSetterMixin, metaclass=abc.ABCMeta, ):
520
519
  if msg_countdown:
521
520
  run_date = FunboostTime(kw['body']['extra']['publish_time']).datetime_obj + datetime.timedelta(seconds=msg_countdown)
522
521
  if msg_eta:
523
-
524
522
  run_date = FunboostTime(msg_eta).datetime_obj
525
523
  # print(run_date,time_util.DatetimeConverter().datetime_obj)
526
524
  # print(run_date.timestamp(),time_util.DatetimeConverter().datetime_obj.timestamp())
@@ -543,7 +541,7 @@ class AbstractConsumer(LoggerLevelSetterMixin, metaclass=abc.ABCMeta, ):
543
541
  self._delay_task_scheduler.add_job(self._push_apscheduler_task_to_broker, 'date', run_date=run_date,
544
542
  kwargs={'queue_name': self.queue_name, 'msg': msg_no_delay, },
545
543
  misfire_grace_time=misfire_grace_time,
546
- )
544
+ )
547
545
  self._confirm_consume(kw)
548
546
 
549
547
  else: # 普通任务
@@ -636,21 +634,21 @@ class AbstractConsumer(LoggerLevelSetterMixin, metaclass=abc.ABCMeta, ):
636
634
  """
637
635
  self._do_not_delete_extra_from_msg = True
638
636
 
639
- def _frame_custom_record_process_info_func(self,current_function_result_status: FunctionResultStatus,kw:dict):
637
+ def _frame_custom_record_process_info_func(self, current_function_result_status: FunctionResultStatus, kw: dict):
640
638
  pass
641
639
 
642
- async def _aio_frame_custom_record_process_info_func(self,current_function_result_status: FunctionResultStatus,kw:dict):
640
+ async def _aio_frame_custom_record_process_info_func(self, current_function_result_status: FunctionResultStatus, kw: dict):
643
641
  pass
644
642
 
645
- def user_custom_record_process_info_func(self, current_function_result_status: FunctionResultStatus,): # 这个可以继承
643
+ def user_custom_record_process_info_func(self, current_function_result_status: FunctionResultStatus, ): # 这个可以继承
646
644
  pass
647
645
 
648
- async def aio_user_custom_record_process_info_func(self, current_function_result_status: FunctionResultStatus,): # 这个可以继承
646
+ async def aio_user_custom_record_process_info_func(self, current_function_result_status: FunctionResultStatus, ): # 这个可以继承
649
647
  pass
650
648
 
651
- def _convert_real_function_only_params_by_conusuming_function_kind(self, function_only_params: dict,extra_params:dict):
649
+ def _convert_real_function_only_params_by_conusuming_function_kind(self, function_only_params: dict, extra_params: dict):
652
650
  """对于实例方法和classmethod 方法, 从消息队列的消息恢复第一个入参, self 和 cls"""
653
- can_not_json_serializable_keys = extra_params.get('can_not_json_serializable_keys',[])
651
+ can_not_json_serializable_keys = extra_params.get('can_not_json_serializable_keys', [])
654
652
  if self.consumer_params.consuming_function_kind in [FunctionKind.CLASS_METHOD, FunctionKind.INSTANCE_METHOD]:
655
653
  real_function_only_params = copy.copy(function_only_params)
656
654
  method_first_param_name = None
@@ -710,7 +708,7 @@ class AbstractConsumer(LoggerLevelSetterMixin, metaclass=abc.ABCMeta, ):
710
708
  current_function_result_status.run_status = RunStatus.finish
711
709
  self._result_persistence_helper.save_function_result_to_mongo(current_function_result_status)
712
710
  if self._get_priority_conf(kw, 'do_task_filtering'):
713
- self._redis_filter.add_a_value(function_only_params,self._get_priority_conf(kw, 'filter_str')) # 函数执行成功后,添加函数的参数排序后的键值对字符串到set中。
711
+ self._redis_filter.add_a_value(function_only_params, self._get_priority_conf(kw, 'filter_str')) # 函数执行成功后,添加函数的参数排序后的键值对字符串到set中。
714
712
  if current_function_result_status.success is False and current_retry_times == max_retry_times:
715
713
  log_msg = f'函数 {self.consuming_function.__name__} 达到最大重试次数 {self._get_priority_conf(kw, "max_retry_times")} 后,仍然失败, 入参是 {function_only_params} '
716
714
  if self.consumer_params.is_push_to_dlx_queue_when_retry_max_times:
@@ -733,11 +731,11 @@ class AbstractConsumer(LoggerLevelSetterMixin, metaclass=abc.ABCMeta, ):
733
731
  p.execute()
734
732
 
735
733
  with self._lock_for_count_execute_task_times_every_unit_time:
736
- self.metric_calculation.cal(t_start_run_fun,current_function_result_status)
737
- self._frame_custom_record_process_info_func(current_function_result_status,kw)
738
- self.user_custom_record_process_info_func(current_function_result_status,) # 两种方式都可以自定义,记录结果,建议继承方式,不使用boost中指定 user_custom_record_process_info_func
734
+ self.metric_calculation.cal(t_start_run_fun, current_function_result_status)
735
+ self._frame_custom_record_process_info_func(current_function_result_status, kw)
736
+ self.user_custom_record_process_info_func(current_function_result_status, ) # 两种方式都可以自定义,记录结果,建议继承方式,不使用boost中指定 user_custom_record_process_info_func
739
737
  if self.consumer_params.user_custom_record_process_info_func:
740
- self.consumer_params.user_custom_record_process_info_func(current_function_result_status,)
738
+ self.consumer_params.user_custom_record_process_info_func(current_function_result_status, )
741
739
  except BaseException as e:
742
740
  log_msg = f' error 严重错误 {type(e)} {e} '
743
741
  # self.logger.critical(msg=f'{log_msg} \n', exc_info=True)
@@ -752,12 +750,12 @@ class AbstractConsumer(LoggerLevelSetterMixin, metaclass=abc.ABCMeta, ):
752
750
  function_only_params = kw['function_only_params'] if self._do_not_delete_extra_from_msg is False else kw['body']
753
751
  task_id = kw['body']['extra']['task_id']
754
752
  t_start = time.time()
755
-
753
+
756
754
  fct = funboost_current_task()
757
755
  fct_context = FctContext(function_params=function_only_params,
758
756
  full_msg=kw['body'],
759
757
  function_result_status=function_result_status,
760
- logger=self.logger, queue_name=self.queue_name,)
758
+ logger=self.logger, queue_name=self.queue_name, )
761
759
 
762
760
  try:
763
761
  function_run = self.consuming_function
@@ -779,7 +777,7 @@ class AbstractConsumer(LoggerLevelSetterMixin, metaclass=abc.ABCMeta, ):
779
777
  self.logger.warning(f'取消运行 {task_id} {function_only_params}')
780
778
  return function_result_status
781
779
  function_run = kill_remote_task.kill_fun_deco(task_id)(function_run) # 用杀死装饰器包装起来在另一个线程运行函数,以便等待远程杀死。
782
- function_result_status.result = function_run(**self._convert_real_function_only_params_by_conusuming_function_kind(function_only_params,kw['body']['extra']))
780
+ function_result_status.result = function_run(**self._convert_real_function_only_params_by_conusuming_function_kind(function_only_params, kw['body']['extra']))
783
781
  # if asyncio.iscoroutine(function_result_status.result):
784
782
  # log_msg = f'''异步的协程消费函数必须使用 async 并发模式并发,请设置消费函数 {self.consuming_function.__name__} 的concurrent_mode 为 ConcurrentModeEnum.ASYNC 或 4'''
785
783
  # # self.logger.critical(msg=f'{log_msg} \n')
@@ -839,11 +837,11 @@ class AbstractConsumer(LoggerLevelSetterMixin, metaclass=abc.ABCMeta, ):
839
837
 
840
838
  function_result_status.result = FunctionResultStatus.FUNC_RUN_ERROR
841
839
  return function_result_status
842
-
840
+
843
841
  def _gen_asyncio_objects(self):
844
842
  if getattr(self, '_async_lock_for_count_execute_task_times_every_unit_time', None) is None:
845
843
  self._async_lock_for_count_execute_task_times_every_unit_time = asyncio.Lock()
846
-
844
+
847
845
  # noinspection PyProtectedMember
848
846
  async def _async_run(self, kw: dict, ):
849
847
  """
@@ -898,7 +896,7 @@ class AbstractConsumer(LoggerLevelSetterMixin, metaclass=abc.ABCMeta, ):
898
896
  await simple_run_in_executor(self._result_persistence_helper.save_function_result_to_mongo, current_function_result_status)
899
897
  if self._get_priority_conf(kw, 'do_task_filtering'):
900
898
  # self._redis_filter.add_a_value(function_only_params) # 函数执行成功后,添加函数的参数排序后的键值对字符串到set中。
901
- await simple_run_in_executor(self._redis_filter.add_a_value, function_only_params,self._get_priority_conf(kw, 'filter_str'))
899
+ await simple_run_in_executor(self._redis_filter.add_a_value, function_only_params, self._get_priority_conf(kw, 'filter_str'))
902
900
  if current_function_result_status.success is False and current_retry_times == max_retry_times:
903
901
  log_msg = f'函数 {self.consuming_function.__name__} 达到最大重试次数 {self._get_priority_conf(kw, "max_retry_times")} 后,仍然失败, 入参是 {function_only_params} '
904
902
  if self.consumer_params.is_push_to_dlx_queue_when_retry_max_times:
@@ -923,12 +921,12 @@ class AbstractConsumer(LoggerLevelSetterMixin, metaclass=abc.ABCMeta, ):
923
921
  async with self._async_lock_for_count_execute_task_times_every_unit_time:
924
922
  self.metric_calculation.cal(t_start_run_fun, current_function_result_status)
925
923
 
926
- self._frame_custom_record_process_info_func(current_function_result_status,kw)
927
- await self._aio_frame_custom_record_process_info_func(current_function_result_status,kw)
928
- self.user_custom_record_process_info_func(current_function_result_status,) # 两种方式都可以自定义,记录结果.建议使用文档4.21.b的方式继承来重写
929
- await self.aio_user_custom_record_process_info_func(current_function_result_status,)
924
+ self._frame_custom_record_process_info_func(current_function_result_status, kw)
925
+ await self._aio_frame_custom_record_process_info_func(current_function_result_status, kw)
926
+ self.user_custom_record_process_info_func(current_function_result_status, ) # 两种方式都可以自定义,记录结果.建议使用文档4.21.b的方式继承来重写
927
+ await self.aio_user_custom_record_process_info_func(current_function_result_status, )
930
928
  if self.consumer_params.user_custom_record_process_info_func:
931
- self.consumer_params.user_custom_record_process_info_func(current_function_result_status,)
929
+ self.consumer_params.user_custom_record_process_info_func(current_function_result_status, )
932
930
 
933
931
  except BaseException as e:
934
932
  log_msg = f' error 严重错误 {type(e)} {e} '
@@ -944,17 +942,17 @@ class AbstractConsumer(LoggerLevelSetterMixin, metaclass=abc.ABCMeta, ):
944
942
  """虽然和上面有点大面积重复相似,这个是为了asyncio模式的,asyncio模式真的和普通同步模式的代码思维和形式区别太大,
945
943
  框架实现兼容async的消费函数很麻烦复杂,连并发池都要单独写"""
946
944
  function_only_params = kw['function_only_params'] if self._do_not_delete_extra_from_msg is False else kw['body']
947
-
945
+
948
946
  # noinspection PyBroadException
949
947
  t_start = time.time()
950
948
  fct = funboost_current_task()
951
949
  fct_context = FctContext(function_params=function_only_params,
952
950
  full_msg=kw['body'],
953
951
  function_result_status=function_result_status,
954
- logger=self.logger,queue_name=self.queue_name,)
952
+ logger=self.logger, queue_name=self.queue_name, )
955
953
  fct.set_fct_context(fct_context)
956
954
  try:
957
- corotinue_obj = self.consuming_function(**self._convert_real_function_only_params_by_conusuming_function_kind(function_only_params,kw['body']['extra']))
955
+ corotinue_obj = self.consuming_function(**self._convert_real_function_only_params_by_conusuming_function_kind(function_only_params, kw['body']['extra']))
958
956
  if not asyncio.iscoroutine(corotinue_obj):
959
957
  log_msg = f'''当前设置的并发模式为 async 并发模式,但消费函数不是异步协程函数,请不要把消费函数 {self.consuming_function.__name__} 的 concurrent_mode 设置错误'''
960
958
  # self.logger.critical(msg=f'{log_msg} \n')
@@ -1058,11 +1056,11 @@ class AbstractConsumer(LoggerLevelSetterMixin, metaclass=abc.ABCMeta, ):
1058
1056
 
1059
1057
  def pause_consume(self):
1060
1058
  """从远程机器可以设置队列为暂停消费状态,funboost框架会自动停止消费,此功能需要配置好redis"""
1061
- RedisMixin().redis_db_frame.hset(RedisKeys.REDIS_KEY_PAUSE_FLAG, self.queue_name,'1')
1059
+ RedisMixin().redis_db_frame.hset(RedisKeys.REDIS_KEY_PAUSE_FLAG, self.queue_name, '1')
1062
1060
 
1063
1061
  def continue_consume(self):
1064
1062
  """从远程机器可以设置队列为暂停消费状态,funboost框架会自动继续消费,此功能需要配置好redis"""
1065
- RedisMixin().redis_db_frame.hset(RedisKeys.REDIS_KEY_PAUSE_FLAG, self.queue_name,'0')
1063
+ RedisMixin().redis_db_frame.hset(RedisKeys.REDIS_KEY_PAUSE_FLAG, self.queue_name, '0')
1066
1064
 
1067
1065
  @decorators.FunctionResultCacher.cached_function_result_for_a_time(120)
1068
1066
  def _judge_is_daylight(self):
@@ -1164,8 +1162,8 @@ class ConcurrentModeDispatcher(FunboostFileLoggerMixin):
1164
1162
  if self._concurrent_mode == ConcurrentModeEnum.ASYNC:
1165
1163
  self.consumer._concurrent_pool = self.consumer.consumer_params.specify_concurrent_pool or pool_type(
1166
1164
  self.consumer.consumer_params.concurrent_num,
1167
- specify_async_loop=self.consumer.consumer_params.specify_async_loop,
1168
- is_auto_start_specify_async_loop_in_child_thread=self.consumer.consumer_params.is_auto_start_specify_async_loop_in_child_thread)
1165
+ specify_async_loop=self.consumer.consumer_params.specify_async_loop,
1166
+ is_auto_start_specify_async_loop_in_child_thread=self.consumer.consumer_params.is_auto_start_specify_async_loop_in_child_thread)
1169
1167
  else:
1170
1168
  # print(pool_type)
1171
1169
  self.consumer._concurrent_pool = self.consumer.consumer_params.specify_concurrent_pool or pool_type(self.consumer.consumer_params.concurrent_num)
@@ -1212,14 +1210,14 @@ def wait_for_possible_has_finish_all_tasks_by_conusmer_list(consumer_list: typin
1212
1210
 
1213
1211
 
1214
1212
  class MetricCalculation:
1215
- UNIT_TIME_FOR_COUNT = 10 # 这个不要随意改,需要其他地方配合,每隔多少秒计数,显示单位时间内执行多少次,暂时固定为10秒。
1213
+ UNIT_TIME_FOR_COUNT = 10 # 这个不要随意改,需要其他地方配合,每隔多少秒计数,显示单位时间内执行多少次,暂时固定为10秒。
1216
1214
 
1217
- def __init__(self,conusmer:AbstractConsumer) -> None:
1215
+ def __init__(self, conusmer: AbstractConsumer) -> None:
1218
1216
  self.consumer = conusmer
1219
1217
 
1220
1218
  self.unit_time_for_count = self.UNIT_TIME_FOR_COUNT #
1221
1219
  self.execute_task_times_every_unit_time_temp = 0 # 每单位时间执行了多少次任务。
1222
- self.execute_task_times_every_unit_time_temp_fail =0 # 每单位时间执行了多少次任务失败。
1220
+ self.execute_task_times_every_unit_time_temp_fail = 0 # 每单位时间执行了多少次任务失败。
1223
1221
  self.current_time_for_execute_task_times_every_unit_time = time.time()
1224
1222
  self.consuming_function_cost_time_total_every_unit_time_tmp = 0
1225
1223
  self.last_execute_task_time = time.time() # 最近一次执行任务的时间。
@@ -1233,22 +1231,22 @@ class MetricCalculation:
1233
1231
  self.last_timestamp_when_has_task_in_queue = 0
1234
1232
  self.last_timestamp_print_msg_num = 0
1235
1233
 
1236
- self.total_consume_count_from_start =0
1237
- self.total_consume_count_from_start_fail =0
1234
+ self.total_consume_count_from_start = 0
1235
+ self.total_consume_count_from_start_fail = 0
1238
1236
  self.total_cost_time_from_start = 0 # 函数运行累计花费时间
1239
1237
  self.last_x_s_total_cost_time = None
1240
1238
 
1241
- def cal(self,t_start_run_fun:float,current_function_result_status:FunctionResultStatus):
1239
+ def cal(self, t_start_run_fun: float, current_function_result_status: FunctionResultStatus):
1242
1240
  self.last_execute_task_time = time.time()
1243
1241
  current_msg_cost_time = time.time() - t_start_run_fun
1244
1242
  self.execute_task_times_every_unit_time_temp += 1
1245
- self.total_consume_count_from_start +=1
1243
+ self.total_consume_count_from_start += 1
1246
1244
  self.total_cost_time_from_start += current_msg_cost_time
1247
1245
  if current_function_result_status.success is False:
1248
1246
  self.execute_task_times_every_unit_time_temp_fail += 1
1249
- self.total_consume_count_from_start_fail +=1
1247
+ self.total_consume_count_from_start_fail += 1
1250
1248
  self.consuming_function_cost_time_total_every_unit_time_tmp += current_msg_cost_time
1251
-
1249
+
1252
1250
  if time.time() - self.current_time_for_execute_task_times_every_unit_time > self.unit_time_for_count:
1253
1251
  self.last_x_s_execute_count = self.execute_task_times_every_unit_time_temp
1254
1252
  self.last_x_s_execute_count_fail = self.execute_task_times_every_unit_time_temp_fail
@@ -1267,30 +1265,30 @@ class MetricCalculation:
1267
1265
  self.consumer.logger.info(msg)
1268
1266
  self.last_show_remaining_execution_time = time.time()
1269
1267
  if self.consumer.consumer_params.is_send_consumer_hearbeat_to_redis is True:
1270
- RedisMixin().redis_db_frame.hincrby(RedisKeys.FUNBOOST_QUEUE__RUN_COUNT_MAP,self.consumer.queue_name,self.execute_task_times_every_unit_time_temp)
1271
- RedisMixin().redis_db_frame.hincrby(RedisKeys.FUNBOOST_QUEUE__RUN_FAIL_COUNT_MAP,self.consumer.queue_name,self.execute_task_times_every_unit_time_temp_fail)
1268
+ RedisMixin().redis_db_frame.hincrby(RedisKeys.FUNBOOST_QUEUE__RUN_COUNT_MAP, self.consumer.queue_name, self.execute_task_times_every_unit_time_temp)
1269
+ RedisMixin().redis_db_frame.hincrby(RedisKeys.FUNBOOST_QUEUE__RUN_FAIL_COUNT_MAP, self.consumer.queue_name, self.execute_task_times_every_unit_time_temp_fail)
1272
1270
 
1273
1271
  self.current_time_for_execute_task_times_every_unit_time = time.time()
1274
1272
  self.consuming_function_cost_time_total_every_unit_time_tmp = 0
1275
1273
  self.execute_task_times_every_unit_time_temp = 0
1276
1274
  self.execute_task_times_every_unit_time_temp_fail = 0
1277
1275
 
1278
- def get_report_hearbeat_info(self) ->dict:
1276
+ def get_report_hearbeat_info(self) -> dict:
1279
1277
  return {
1280
- 'unit_time_for_count':self.unit_time_for_count,
1281
- 'last_x_s_execute_count':self.last_x_s_execute_count,
1282
- 'last_x_s_execute_count_fail':self.last_x_s_execute_count_fail,
1283
- 'last_execute_task_time':self.last_execute_task_time,
1284
- 'last_x_s_avarage_function_spend_time':self.last_x_s_avarage_function_spend_time,
1278
+ 'unit_time_for_count': self.unit_time_for_count,
1279
+ 'last_x_s_execute_count': self.last_x_s_execute_count,
1280
+ 'last_x_s_execute_count_fail': self.last_x_s_execute_count_fail,
1281
+ 'last_execute_task_time': self.last_execute_task_time,
1282
+ 'last_x_s_avarage_function_spend_time': self.last_x_s_avarage_function_spend_time,
1285
1283
  # 'last_show_remaining_execution_time':self.last_show_remaining_execution_time,
1286
- 'msg_num_in_broker':self.msg_num_in_broker,
1287
- 'current_time_for_execute_task_times_every_unit_time':self.current_time_for_execute_task_times_every_unit_time,
1288
- 'last_timestamp_when_has_task_in_queue':self.last_timestamp_when_has_task_in_queue,
1289
- 'total_consume_count_from_start':self.total_consume_count_from_start,
1290
- 'total_consume_count_from_start_fail':self.total_consume_count_from_start_fail,
1291
- 'total_cost_time_from_start':self.total_cost_time_from_start,
1292
- 'last_x_s_total_cost_time':self.last_x_s_total_cost_time,
1293
- 'avarage_function_spend_time_from_start':round(self.total_cost_time_from_start / self.total_consume_count_from_start,3) if self.total_consume_count_from_start else None,
1284
+ 'msg_num_in_broker': self.msg_num_in_broker,
1285
+ 'current_time_for_execute_task_times_every_unit_time': self.current_time_for_execute_task_times_every_unit_time,
1286
+ 'last_timestamp_when_has_task_in_queue': self.last_timestamp_when_has_task_in_queue,
1287
+ 'total_consume_count_from_start': self.total_consume_count_from_start,
1288
+ 'total_consume_count_from_start_fail': self.total_consume_count_from_start_fail,
1289
+ 'total_cost_time_from_start': self.total_cost_time_from_start,
1290
+ 'last_x_s_total_cost_time': self.last_x_s_total_cost_time,
1291
+ 'avarage_function_spend_time_from_start': round(self.total_cost_time_from_start / self.total_consume_count_from_start, 3) if self.total_consume_count_from_start else None,
1294
1292
  }
1295
1293
 
1296
1294
 
@@ -1351,11 +1349,10 @@ class DistributedConsumerStatistics(RedisMixin, FunboostFileLoggerMixin):
1351
1349
  p.sadd(redis_key, value)
1352
1350
  p.execute()
1353
1351
 
1354
-
1355
1352
  def _send_msg_num(self):
1356
- dic = {'msg_num_in_broker':self._consumer.metric_calculation.msg_num_in_broker,
1357
- 'last_get_msg_num_ts':self._consumer.metric_calculation.last_get_msg_num_ts,
1358
- 'report_ts':time.time(),
1353
+ dic = {'msg_num_in_broker': self._consumer.metric_calculation.msg_num_in_broker,
1354
+ 'last_get_msg_num_ts': self._consumer.metric_calculation.last_get_msg_num_ts,
1355
+ 'report_ts': time.time(),
1359
1356
  }
1360
1357
  self.redis_db_frame.hset(RedisKeys.QUEUE__MSG_COUNT_MAP, self._consumer.queue_name, json.dumps(dic))
1361
1358
 
@@ -1391,16 +1388,14 @@ class DistributedConsumerStatistics(RedisMixin, FunboostFileLoggerMixin):
1391
1388
 
1392
1389
  # noinspection PyProtectedMember
1393
1390
  def _get_stop_and_pause_flag_from_redis(self):
1394
- stop_flag = self.redis_db_frame.hget(RedisKeys.REDIS_KEY_STOP_FLAG,self._consumer.queue_name)
1391
+ stop_flag = self.redis_db_frame.hget(RedisKeys.REDIS_KEY_STOP_FLAG, self._consumer.queue_name)
1395
1392
  if stop_flag is not None and int(stop_flag) == 1:
1396
1393
  self._consumer._stop_flag = 1
1397
1394
  else:
1398
1395
  self._consumer._stop_flag = 0
1399
1396
 
1400
- pause_flag = self.redis_db_frame.hget(RedisKeys.REDIS_KEY_PAUSE_FLAG,self._consumer.queue_name)
1397
+ pause_flag = self.redis_db_frame.hget(RedisKeys.REDIS_KEY_PAUSE_FLAG, self._consumer.queue_name)
1401
1398
  if pause_flag is not None and int(pause_flag) == 1:
1402
1399
  self._consumer._pause_flag.set()
1403
1400
  else:
1404
1401
  self._consumer._pause_flag.clear()
1405
-
1406
-
@@ -13,7 +13,7 @@ class CeleryConsumer(AbstractConsumer):
13
13
  celery作为中间件实现的。
14
14
  """
15
15
 
16
- BROKER_EXCLUSIVE_CONFIG_DEFAULT = {'celery_task_config': {}}
16
+
17
17
 
18
18
  # celery的可以配置项大全 https://docs.celeryq.dev/en/stable/userguide/configuration.html#new-lowercase-settings
19
19
  # celery @app.task() 所有可以配置项可以看 D:\ProgramData\Miniconda3\Lib\site-packages\celery\app\task.py
@@ -11,11 +11,6 @@ class DramatiqConsumer(AbstractConsumer):
11
11
  dramatiq作为中间件实现的。
12
12
  """
13
13
 
14
- BROKER_EXCLUSIVE_CONFIG_DEFAULT = {'dramatiq_actor_options': {}}
15
- """
16
- dramatiq_actor_options 的值可以是:
17
- {'max_age', 'throws', 'pipe_target', 'pipe_ignore', 'on_success', 'retry_when', 'time_limit', 'min_backoff', 'max_retries', 'max_backoff', 'notify_shutdown', 'on_failure'}
18
- """
19
14
 
20
15
  def custom_init(self):
21
16
  # 这就是核心,
@@ -23,7 +23,7 @@ class GrpcConsumer(AbstractConsumer, ):
23
23
  """
24
24
  grpc as broker
25
25
  """
26
- BROKER_EXCLUSIVE_CONFIG_DEFAULT = {'host': '127.0.0.1', 'port': None}
26
+
27
27
 
28
28
  def custom_init(self):
29
29
  class FunboostGrpcServicer(funboost_grpc_pb2_grpc.FunboostBrokerServiceServicer):
@@ -19,7 +19,7 @@ class HTTPConsumer(AbstractConsumer, ):
19
19
  """
20
20
  flask 作为消息队列实现 consumer
21
21
  """
22
- BROKER_EXCLUSIVE_CONFIG_DEFAULT = {'host': '127.0.0.1', 'port': None}
22
+
23
23
 
24
24
  # noinspection PyAttributeOutsideInit
25
25
  def custom_init(self):
@@ -35,7 +35,7 @@ class HTTPConsumer(AbstractConsumer, ):
35
35
  """
36
36
  aiohttp 实现消息队列,不支持持久化,但不需要安装软件。
37
37
  """
38
- BROKER_EXCLUSIVE_CONFIG_DEFAULT = {'host': '127.0.0.1', 'port': None}
38
+
39
39
 
40
40
  # noinspection PyAttributeOutsideInit
41
41
  def custom_init(self):
@@ -12,11 +12,8 @@ class HueyConsumer(AbstractConsumer):
12
12
  huey作为中间件实现的。
13
13
  """
14
14
 
15
- BROKER_EXCLUSIVE_CONFIG_DEFAULT = {'huey_task_kwargs': {}}
16
- """
17
- retries=0, retry_delay=0, priority=None, context=False,
18
- name=None, expires=None, **kwargs
19
- """
15
+
16
+
20
17
 
21
18
  def custom_init(self):
22
19
  # 这就是核心,
@@ -22,8 +22,6 @@ class KafkaConsumer(AbstractConsumer):
22
22
  可以让消费函数内部 sleep60秒,突然停止消费代码,使用 kafka-consumer-groups.sh --bootstrap-server 127.0.0.1:9092 --describe --group funboost 来证实自动确认消费和手动确认消费的区别。
23
23
  """
24
24
 
25
- BROKER_EXCLUSIVE_CONFIG_DEFAULT = {'group_id': 'funboost_kafka', 'auto_offset_reset': 'earliest','num_partitions':10,'replication_factor':1,}
26
- # not_all_brokers_general_settings配置 ,支持独立的中间件配置参数是 group_id 和 auto_offset_reset
27
25
  """
28
26
  auto_offset_reset 介绍
29
27
  auto_offset_reset (str): A policy for resetting offsets on
@@ -31,7 +31,6 @@ class KafkaConsumerManuallyCommit(AbstractConsumer):
31
31
  可以让消费函数内部 sleep 60秒,突然停止消费代码,使用 kafka-consumer-groups.sh --bootstrap-server 127.0.0.1:9092 --describe --group frame_group 来证实自动确认消费和手动确认消费的区别。
32
32
  """
33
33
 
34
- BROKER_EXCLUSIVE_CONFIG_DEFAULT = {'group_id': 'funboost_kafka', 'auto_offset_reset': 'earliest','num_partitions':10,'replication_factor':1,}
35
34
 
36
35
  def custom_init(self):
37
36
  self._lock_for_operate_offset_dict = threading.Lock()