funboost 50.2__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 (47) hide show
  1. funboost/__init__.py +1 -1
  2. funboost/constant.py +4 -0
  3. funboost/consumers/base_consumer.py +95 -96
  4. funboost/consumers/celery_consumer.py +1 -1
  5. funboost/consumers/dramatiq_consumer.py +0 -5
  6. funboost/consumers/grpc_consumer.py +2 -19
  7. funboost/consumers/http_consumer.py +107 -40
  8. funboost/consumers/http_consumer_aiohttp_old.py +113 -0
  9. funboost/consumers/huey_consumer.py +2 -5
  10. funboost/consumers/kafka_consumer.py +1 -6
  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 +38 -3
  29. funboost/core/broker_kind__exclusive_config_default_define.py +229 -0
  30. funboost/core/funboost_time.py +10 -45
  31. funboost/core/func_params_model.py +28 -4
  32. funboost/core/helper_funs.py +9 -8
  33. funboost/core/msg_result_getter.py +27 -0
  34. funboost/factories/broker_kind__publsiher_consumer_type_map.py +13 -3
  35. funboost/funboost_config_deafult.py +0 -3
  36. funboost/function_result_web/templates/fun_result_table.html +1 -1
  37. funboost/publishers/base_publisher.py +8 -2
  38. funboost/publishers/http_publisher.py +20 -2
  39. funboost/publishers/rabbitmq_amqpstorm_publisher.py +8 -7
  40. funboost/publishers/rabbitmq_complex_routing_publisher.py +84 -0
  41. funboost/utils/redis_manager.py +11 -5
  42. {funboost-50.2.dist-info → funboost-50.4.dist-info}/METADATA +159 -98
  43. {funboost-50.2.dist-info → funboost-50.4.dist-info}/RECORD +46 -41
  44. {funboost-50.2.dist-info → funboost-50.4.dist-info}/WHEEL +1 -1
  45. funboost-50.2.dist-info/LICENSE +0 -203
  46. {funboost-50.2.dist-info → funboost-50.4.dist-info}/entry_points.txt +0 -0
  47. {funboost-50.2.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.2"
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,18 +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
- def user_custom_record_process_info_func(self, current_function_result_status: FunctionResultStatus,): # 这个可以继承
640
+ async def _aio_frame_custom_record_process_info_func(self, current_function_result_status: FunctionResultStatus, kw: dict):
643
641
  pass
644
642
 
645
- async def aio_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
- def _convert_real_function_only_params_by_conusuming_function_kind(self, function_only_params: dict,extra_params:dict):
646
+ async def aio_user_custom_record_process_info_func(self, current_function_result_status: FunctionResultStatus, ): # 这个可以继承
647
+ pass
648
+
649
+ def _convert_real_function_only_params_by_conusuming_function_kind(self, function_only_params: dict, extra_params: dict):
649
650
  """对于实例方法和classmethod 方法, 从消息队列的消息恢复第一个入参, self 和 cls"""
650
- 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', [])
651
652
  if self.consumer_params.consuming_function_kind in [FunctionKind.CLASS_METHOD, FunctionKind.INSTANCE_METHOD]:
652
653
  real_function_only_params = copy.copy(function_only_params)
653
654
  method_first_param_name = None
@@ -707,7 +708,7 @@ class AbstractConsumer(LoggerLevelSetterMixin, metaclass=abc.ABCMeta, ):
707
708
  current_function_result_status.run_status = RunStatus.finish
708
709
  self._result_persistence_helper.save_function_result_to_mongo(current_function_result_status)
709
710
  if self._get_priority_conf(kw, 'do_task_filtering'):
710
- 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中。
711
712
  if current_function_result_status.success is False and current_retry_times == max_retry_times:
712
713
  log_msg = f'函数 {self.consuming_function.__name__} 达到最大重试次数 {self._get_priority_conf(kw, "max_retry_times")} 后,仍然失败, 入参是 {function_only_params} '
713
714
  if self.consumer_params.is_push_to_dlx_queue_when_retry_max_times:
@@ -730,11 +731,11 @@ class AbstractConsumer(LoggerLevelSetterMixin, metaclass=abc.ABCMeta, ):
730
731
  p.execute()
731
732
 
732
733
  with self._lock_for_count_execute_task_times_every_unit_time:
733
- self.metric_calculation.cal(t_start_run_fun,current_function_result_status)
734
- self._frame_custom_record_process_info_func(current_function_result_status,kw)
735
- 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
736
737
  if self.consumer_params.user_custom_record_process_info_func:
737
- 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, )
738
739
  except BaseException as e:
739
740
  log_msg = f' error 严重错误 {type(e)} {e} '
740
741
  # self.logger.critical(msg=f'{log_msg} \n', exc_info=True)
@@ -749,12 +750,12 @@ class AbstractConsumer(LoggerLevelSetterMixin, metaclass=abc.ABCMeta, ):
749
750
  function_only_params = kw['function_only_params'] if self._do_not_delete_extra_from_msg is False else kw['body']
750
751
  task_id = kw['body']['extra']['task_id']
751
752
  t_start = time.time()
752
-
753
+
753
754
  fct = funboost_current_task()
754
755
  fct_context = FctContext(function_params=function_only_params,
755
756
  full_msg=kw['body'],
756
757
  function_result_status=function_result_status,
757
- logger=self.logger, queue_name=self.queue_name,)
758
+ logger=self.logger, queue_name=self.queue_name, )
758
759
 
759
760
  try:
760
761
  function_run = self.consuming_function
@@ -776,7 +777,7 @@ class AbstractConsumer(LoggerLevelSetterMixin, metaclass=abc.ABCMeta, ):
776
777
  self.logger.warning(f'取消运行 {task_id} {function_only_params}')
777
778
  return function_result_status
778
779
  function_run = kill_remote_task.kill_fun_deco(task_id)(function_run) # 用杀死装饰器包装起来在另一个线程运行函数,以便等待远程杀死。
779
- 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']))
780
781
  # if asyncio.iscoroutine(function_result_status.result):
781
782
  # log_msg = f'''异步的协程消费函数必须使用 async 并发模式并发,请设置消费函数 {self.consuming_function.__name__} 的concurrent_mode 为 ConcurrentModeEnum.ASYNC 或 4'''
782
783
  # # self.logger.critical(msg=f'{log_msg} \n')
@@ -836,11 +837,11 @@ class AbstractConsumer(LoggerLevelSetterMixin, metaclass=abc.ABCMeta, ):
836
837
 
837
838
  function_result_status.result = FunctionResultStatus.FUNC_RUN_ERROR
838
839
  return function_result_status
839
-
840
+
840
841
  def _gen_asyncio_objects(self):
841
842
  if getattr(self, '_async_lock_for_count_execute_task_times_every_unit_time', None) is None:
842
843
  self._async_lock_for_count_execute_task_times_every_unit_time = asyncio.Lock()
843
-
844
+
844
845
  # noinspection PyProtectedMember
845
846
  async def _async_run(self, kw: dict, ):
846
847
  """
@@ -895,7 +896,7 @@ class AbstractConsumer(LoggerLevelSetterMixin, metaclass=abc.ABCMeta, ):
895
896
  await simple_run_in_executor(self._result_persistence_helper.save_function_result_to_mongo, current_function_result_status)
896
897
  if self._get_priority_conf(kw, 'do_task_filtering'):
897
898
  # self._redis_filter.add_a_value(function_only_params) # 函数执行成功后,添加函数的参数排序后的键值对字符串到set中。
898
- 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'))
899
900
  if current_function_result_status.success is False and current_retry_times == max_retry_times:
900
901
  log_msg = f'函数 {self.consuming_function.__name__} 达到最大重试次数 {self._get_priority_conf(kw, "max_retry_times")} 后,仍然失败, 入参是 {function_only_params} '
901
902
  if self.consumer_params.is_push_to_dlx_queue_when_retry_max_times:
@@ -920,11 +921,12 @@ class AbstractConsumer(LoggerLevelSetterMixin, metaclass=abc.ABCMeta, ):
920
921
  async with self._async_lock_for_count_execute_task_times_every_unit_time:
921
922
  self.metric_calculation.cal(t_start_run_fun, current_function_result_status)
922
923
 
923
- self._frame_custom_record_process_info_func(current_function_result_status,kw)
924
- self.user_custom_record_process_info_func(current_function_result_status,) # 两种方式都可以自定义,记录结果.建议使用文档4.21.b的方式继承来重写
925
- 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, )
926
928
  if self.consumer_params.user_custom_record_process_info_func:
927
- 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, )
928
930
 
929
931
  except BaseException as e:
930
932
  log_msg = f' error 严重错误 {type(e)} {e} '
@@ -940,17 +942,17 @@ class AbstractConsumer(LoggerLevelSetterMixin, metaclass=abc.ABCMeta, ):
940
942
  """虽然和上面有点大面积重复相似,这个是为了asyncio模式的,asyncio模式真的和普通同步模式的代码思维和形式区别太大,
941
943
  框架实现兼容async的消费函数很麻烦复杂,连并发池都要单独写"""
942
944
  function_only_params = kw['function_only_params'] if self._do_not_delete_extra_from_msg is False else kw['body']
943
-
945
+
944
946
  # noinspection PyBroadException
945
947
  t_start = time.time()
946
948
  fct = funboost_current_task()
947
949
  fct_context = FctContext(function_params=function_only_params,
948
950
  full_msg=kw['body'],
949
951
  function_result_status=function_result_status,
950
- logger=self.logger,queue_name=self.queue_name,)
952
+ logger=self.logger, queue_name=self.queue_name, )
951
953
  fct.set_fct_context(fct_context)
952
954
  try:
953
- 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']))
954
956
  if not asyncio.iscoroutine(corotinue_obj):
955
957
  log_msg = f'''当前设置的并发模式为 async 并发模式,但消费函数不是异步协程函数,请不要把消费函数 {self.consuming_function.__name__} 的 concurrent_mode 设置错误'''
956
958
  # self.logger.critical(msg=f'{log_msg} \n')
@@ -1054,11 +1056,11 @@ class AbstractConsumer(LoggerLevelSetterMixin, metaclass=abc.ABCMeta, ):
1054
1056
 
1055
1057
  def pause_consume(self):
1056
1058
  """从远程机器可以设置队列为暂停消费状态,funboost框架会自动停止消费,此功能需要配置好redis"""
1057
- 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')
1058
1060
 
1059
1061
  def continue_consume(self):
1060
1062
  """从远程机器可以设置队列为暂停消费状态,funboost框架会自动继续消费,此功能需要配置好redis"""
1061
- 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')
1062
1064
 
1063
1065
  @decorators.FunctionResultCacher.cached_function_result_for_a_time(120)
1064
1066
  def _judge_is_daylight(self):
@@ -1160,8 +1162,8 @@ class ConcurrentModeDispatcher(FunboostFileLoggerMixin):
1160
1162
  if self._concurrent_mode == ConcurrentModeEnum.ASYNC:
1161
1163
  self.consumer._concurrent_pool = self.consumer.consumer_params.specify_concurrent_pool or pool_type(
1162
1164
  self.consumer.consumer_params.concurrent_num,
1163
- specify_async_loop=self.consumer.consumer_params.specify_async_loop,
1164
- 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)
1165
1167
  else:
1166
1168
  # print(pool_type)
1167
1169
  self.consumer._concurrent_pool = self.consumer.consumer_params.specify_concurrent_pool or pool_type(self.consumer.consumer_params.concurrent_num)
@@ -1208,14 +1210,14 @@ def wait_for_possible_has_finish_all_tasks_by_conusmer_list(consumer_list: typin
1208
1210
 
1209
1211
 
1210
1212
  class MetricCalculation:
1211
- UNIT_TIME_FOR_COUNT = 10 # 这个不要随意改,需要其他地方配合,每隔多少秒计数,显示单位时间内执行多少次,暂时固定为10秒。
1213
+ UNIT_TIME_FOR_COUNT = 10 # 这个不要随意改,需要其他地方配合,每隔多少秒计数,显示单位时间内执行多少次,暂时固定为10秒。
1212
1214
 
1213
- def __init__(self,conusmer:AbstractConsumer) -> None:
1215
+ def __init__(self, conusmer: AbstractConsumer) -> None:
1214
1216
  self.consumer = conusmer
1215
1217
 
1216
1218
  self.unit_time_for_count = self.UNIT_TIME_FOR_COUNT #
1217
1219
  self.execute_task_times_every_unit_time_temp = 0 # 每单位时间执行了多少次任务。
1218
- self.execute_task_times_every_unit_time_temp_fail =0 # 每单位时间执行了多少次任务失败。
1220
+ self.execute_task_times_every_unit_time_temp_fail = 0 # 每单位时间执行了多少次任务失败。
1219
1221
  self.current_time_for_execute_task_times_every_unit_time = time.time()
1220
1222
  self.consuming_function_cost_time_total_every_unit_time_tmp = 0
1221
1223
  self.last_execute_task_time = time.time() # 最近一次执行任务的时间。
@@ -1229,22 +1231,22 @@ class MetricCalculation:
1229
1231
  self.last_timestamp_when_has_task_in_queue = 0
1230
1232
  self.last_timestamp_print_msg_num = 0
1231
1233
 
1232
- self.total_consume_count_from_start =0
1233
- 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
1234
1236
  self.total_cost_time_from_start = 0 # 函数运行累计花费时间
1235
1237
  self.last_x_s_total_cost_time = None
1236
1238
 
1237
- 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):
1238
1240
  self.last_execute_task_time = time.time()
1239
1241
  current_msg_cost_time = time.time() - t_start_run_fun
1240
1242
  self.execute_task_times_every_unit_time_temp += 1
1241
- self.total_consume_count_from_start +=1
1243
+ self.total_consume_count_from_start += 1
1242
1244
  self.total_cost_time_from_start += current_msg_cost_time
1243
1245
  if current_function_result_status.success is False:
1244
1246
  self.execute_task_times_every_unit_time_temp_fail += 1
1245
- self.total_consume_count_from_start_fail +=1
1247
+ self.total_consume_count_from_start_fail += 1
1246
1248
  self.consuming_function_cost_time_total_every_unit_time_tmp += current_msg_cost_time
1247
-
1249
+
1248
1250
  if time.time() - self.current_time_for_execute_task_times_every_unit_time > self.unit_time_for_count:
1249
1251
  self.last_x_s_execute_count = self.execute_task_times_every_unit_time_temp
1250
1252
  self.last_x_s_execute_count_fail = self.execute_task_times_every_unit_time_temp_fail
@@ -1263,30 +1265,30 @@ class MetricCalculation:
1263
1265
  self.consumer.logger.info(msg)
1264
1266
  self.last_show_remaining_execution_time = time.time()
1265
1267
  if self.consumer.consumer_params.is_send_consumer_hearbeat_to_redis is True:
1266
- RedisMixin().redis_db_frame.hincrby(RedisKeys.FUNBOOST_QUEUE__RUN_COUNT_MAP,self.consumer.queue_name,self.execute_task_times_every_unit_time_temp)
1267
- 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)
1268
1270
 
1269
1271
  self.current_time_for_execute_task_times_every_unit_time = time.time()
1270
1272
  self.consuming_function_cost_time_total_every_unit_time_tmp = 0
1271
1273
  self.execute_task_times_every_unit_time_temp = 0
1272
1274
  self.execute_task_times_every_unit_time_temp_fail = 0
1273
1275
 
1274
- def get_report_hearbeat_info(self) ->dict:
1276
+ def get_report_hearbeat_info(self) -> dict:
1275
1277
  return {
1276
- 'unit_time_for_count':self.unit_time_for_count,
1277
- 'last_x_s_execute_count':self.last_x_s_execute_count,
1278
- 'last_x_s_execute_count_fail':self.last_x_s_execute_count_fail,
1279
- 'last_execute_task_time':self.last_execute_task_time,
1280
- '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,
1281
1283
  # 'last_show_remaining_execution_time':self.last_show_remaining_execution_time,
1282
- 'msg_num_in_broker':self.msg_num_in_broker,
1283
- 'current_time_for_execute_task_times_every_unit_time':self.current_time_for_execute_task_times_every_unit_time,
1284
- 'last_timestamp_when_has_task_in_queue':self.last_timestamp_when_has_task_in_queue,
1285
- 'total_consume_count_from_start':self.total_consume_count_from_start,
1286
- 'total_consume_count_from_start_fail':self.total_consume_count_from_start_fail,
1287
- 'total_cost_time_from_start':self.total_cost_time_from_start,
1288
- 'last_x_s_total_cost_time':self.last_x_s_total_cost_time,
1289
- '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,
1290
1292
  }
1291
1293
 
1292
1294
 
@@ -1347,11 +1349,10 @@ class DistributedConsumerStatistics(RedisMixin, FunboostFileLoggerMixin):
1347
1349
  p.sadd(redis_key, value)
1348
1350
  p.execute()
1349
1351
 
1350
-
1351
1352
  def _send_msg_num(self):
1352
- dic = {'msg_num_in_broker':self._consumer.metric_calculation.msg_num_in_broker,
1353
- 'last_get_msg_num_ts':self._consumer.metric_calculation.last_get_msg_num_ts,
1354
- '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(),
1355
1356
  }
1356
1357
  self.redis_db_frame.hset(RedisKeys.QUEUE__MSG_COUNT_MAP, self._consumer.queue_name, json.dumps(dic))
1357
1358
 
@@ -1387,16 +1388,14 @@ class DistributedConsumerStatistics(RedisMixin, FunboostFileLoggerMixin):
1387
1388
 
1388
1389
  # noinspection PyProtectedMember
1389
1390
  def _get_stop_and_pause_flag_from_redis(self):
1390
- 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)
1391
1392
  if stop_flag is not None and int(stop_flag) == 1:
1392
1393
  self._consumer._stop_flag = 1
1393
1394
  else:
1394
1395
  self._consumer._stop_flag = 0
1395
1396
 
1396
- 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)
1397
1398
  if pause_flag is not None and int(pause_flag) == 1:
1398
1399
  self._consumer._pause_flag.set()
1399
1400
  else:
1400
1401
  self._consumer._pause_flag.clear()
1401
-
1402
-
@@ -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
  # 这就是核心,
@@ -10,29 +10,12 @@ import time
10
10
  from funboost import FunctionResultStatus
11
11
  from funboost.assist.grpc_helper import funboost_grpc_pb2_grpc, funboost_grpc_pb2
12
12
  from funboost.consumers.base_consumer import AbstractConsumer
13
+ from funboost.core.msg_result_getter import FutureStatusResult
13
14
  from funboost.core.serialization import Serialization
14
15
  from funboost.core.exceptions import FunboostWaitRpcResultTimeout
15
16
  from funboost.concurrent_pool.flexible_thread_pool import FlexibleThreadPool
16
17
 
17
18
 
18
- class FutureStatusResult:
19
- def __init__(self,call_type:str):
20
- self.execute_finish_event = threading.Event()
21
- self.staus_result_obj: FunctionResultStatus = None
22
- self.call_type = call_type # sync_call or publish
23
-
24
- def set_finish(self):
25
- self.execute_finish_event.set()
26
-
27
- def wait_finish(self,rpc_timeout):
28
- return self.execute_finish_event.wait(rpc_timeout)
29
-
30
- def set_staus_result_obj(self, staus_result_obj:FunctionResultStatus):
31
- self.staus_result_obj = staus_result_obj
32
-
33
- def get_staus_result_obj(self):
34
- return self.staus_result_obj
35
-
36
19
 
37
20
 
38
21
 
@@ -40,7 +23,7 @@ class GrpcConsumer(AbstractConsumer, ):
40
23
  """
41
24
  grpc as broker
42
25
  """
43
- BROKER_EXCLUSIVE_CONFIG_DEFAULT = {'host': '127.0.0.1', 'port': None}
26
+
44
27
 
45
28
  def custom_init(self):
46
29
  class FunboostGrpcServicer(funboost_grpc_pb2_grpc.FunboostBrokerServiceServicer):