funboost 47.9__py3-none-any.whl → 48.1__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.
- funboost/__init__.py +3 -3
- funboost/assist/celery_helper.py +1 -1
- funboost/constant.py +7 -0
- funboost/consumers/base_consumer.py +132 -75
- funboost/consumers/redis_consumer_ack_able.py +1 -1
- funboost/core/active_cousumer_info_getter.py +60 -0
- funboost/core/current_task.py +37 -0
- funboost/core/func_params_model.py +2 -2
- funboost/core/function_result_status_saver.py +1 -1
- funboost/function_result_web/__pycache__/functions.cpython-37.pyc +0 -0
- funboost/function_result_web/__pycache__/functions.cpython-39.pyc +0 -0
- funboost/function_result_web/app.py +104 -7
- funboost/function_result_web/functions.py +17 -4
- funboost/function_result_web/static/css/content_page_style.css +39 -0
- funboost/function_result_web/static/images/favicon.ico +0 -0
- funboost/function_result_web/static/js/bootstrap-datetimepicker.min.js +2 -0
- funboost/function_result_web/static/js/echarts.min.js +32478 -0
- funboost/function_result_web/static/js/moment-with-locales.min.js +1 -0
- funboost/function_result_web/static/js/select2.min.js +2 -0
- funboost/function_result_web/templates/about.html +67 -0
- funboost/function_result_web/templates/conusme_speed.html +217 -0
- funboost/function_result_web/templates/fun_result_table.html +433 -0
- funboost/function_result_web/templates/index.html +194 -423
- funboost/function_result_web/templates/index_backup.html +475 -0
- funboost/function_result_web/templates/index_/321/204/342/225/225/320/235/321/205/320/237/320/277/321/206/320/232/320/250/321/205/320/237/320/260.html +153 -0
- funboost/function_result_web/templates/queue_op.html +490 -0
- funboost/function_result_web/templates/running_consumer_by_ip.html +220 -0
- funboost/function_result_web/templates/running_consumer_by_queue_name.html +216 -0
- funboost/timing_job/__init__.py +3 -218
- funboost/timing_job/apscheduler_use_redis_store.py +6 -1
- funboost/timing_job/timing_job_base.py +213 -0
- funboost/timing_job/timing_push.py +136 -0
- funboost/utils/ctrl_c_end.py +1 -1
- {funboost-47.9.dist-info → funboost-48.1.dist-info}/METADATA +78 -76
- {funboost-47.9.dist-info → funboost-48.1.dist-info}/RECORD +39 -22
- {funboost-47.9.dist-info → funboost-48.1.dist-info}/WHEEL +1 -1
- {funboost-47.9.dist-info → funboost-48.1.dist-info}/LICENSE +0 -0
- {funboost-47.9.dist-info → funboost-48.1.dist-info}/entry_points.txt +0 -0
- {funboost-47.9.dist-info → funboost-48.1.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__ = "
|
|
16
|
+
__version__ = "48.1"
|
|
17
17
|
|
|
18
18
|
from funboost.set_frame_config import show_frame_config
|
|
19
19
|
|
|
@@ -42,7 +42,7 @@ from funboost.factories.broker_kind__publsiher_consumer_type_map import register
|
|
|
42
42
|
from funboost.factories.publisher_factotry import get_publisher
|
|
43
43
|
from funboost.factories.consumer_factory import get_consumer
|
|
44
44
|
|
|
45
|
-
from funboost.timing_job import fsdf_background_scheduler, timing_publish_deco, funboost_aps_scheduler
|
|
45
|
+
from funboost.timing_job import fsdf_background_scheduler, timing_publish_deco, funboost_aps_scheduler,ApsJobAdder
|
|
46
46
|
from funboost.constant import BrokerEnum, ConcurrentModeEnum
|
|
47
47
|
|
|
48
48
|
from funboost.core.booster import boost, Booster, BoostersManager
|
|
@@ -58,7 +58,7 @@ from funboost.utils.ctrl_c_end import ctrl_c_recv
|
|
|
58
58
|
from funboost.utils.redis_manager import RedisMixin
|
|
59
59
|
from funboost.concurrent_pool.custom_threadpool_executor import show_current_threads_num
|
|
60
60
|
|
|
61
|
-
from funboost.core.current_task import funboost_current_task
|
|
61
|
+
from funboost.core.current_task import funboost_current_task,fct,get_current_taskid
|
|
62
62
|
|
|
63
63
|
|
|
64
64
|
# atexit.register(ctrl_c_recv) # 还是需要用户自己在代码末尾加才可以.
|
funboost/assist/celery_helper.py
CHANGED
|
@@ -85,7 +85,7 @@ class CeleryHelper:
|
|
|
85
85
|
|
|
86
86
|
if is_start_consume_all_queues is False:
|
|
87
87
|
to_be_start_work_celery_queue_name_set_new = copy.copy(cls.to_be_start_work_celery_queue_name_set)
|
|
88
|
-
to_be_start_work_celery_queue_name_set_new.update(set(start_consume_queue_name_list))
|
|
88
|
+
to_be_start_work_celery_queue_name_set_new.update(set(start_consume_queue_name_list or []))
|
|
89
89
|
else:
|
|
90
90
|
from funboost import BoostersManager
|
|
91
91
|
# print(BoostersManager.get_all_queues())
|
funboost/constant.py
CHANGED
|
@@ -1,4 +1,7 @@
|
|
|
1
1
|
# coding= utf-8
|
|
2
|
+
from calendar import c
|
|
3
|
+
|
|
4
|
+
|
|
2
5
|
class BrokerEnum:
|
|
3
6
|
EMPTY = 'empty' # 空的实现,需要搭配 boost入参的 consumer_override_cls 和 publisher_override_cls使用,或者被继承。
|
|
4
7
|
|
|
@@ -106,3 +109,7 @@ class ConstStrForClassMethod:
|
|
|
106
109
|
CLS_MODULE = 'cls_module'
|
|
107
110
|
CLS_FILE = 'cls_file'
|
|
108
111
|
|
|
112
|
+
class RedisKeys:
|
|
113
|
+
REDIS_KEY_PAUSE_FLAG = 'funboost_pause_flag'
|
|
114
|
+
REDIS_KEY_STOP_FLAG = 'funboost_stop_flag'
|
|
115
|
+
QUEUE__MSG_COUNT_MAP = 'funboost_queue__msg_count_map'
|
|
@@ -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
|
|
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
|
|
|
@@ -164,19 +164,25 @@ class AbstractConsumer(LoggerLevelSetterMixin, metaclass=abc.ABCMeta, ):
|
|
|
164
164
|
filter_class = RedisFilter if consumer_params.task_filtering_expire_seconds == 0 else RedisImpermanencyFilter
|
|
165
165
|
self._redis_filter = filter_class(self._redis_filter_key_name, consumer_params.task_filtering_expire_seconds)
|
|
166
166
|
|
|
167
|
-
self._unit_time_for_count = 10 # 每隔多少秒计数,显示单位时间内执行多少次,暂时固定为10秒。
|
|
168
|
-
self._execute_task_times_every_unit_time = 0 # 每单位时间执行了多少次任务。
|
|
169
167
|
self._lock_for_count_execute_task_times_every_unit_time = Lock()
|
|
170
|
-
self.
|
|
171
|
-
self.
|
|
172
|
-
self.
|
|
173
|
-
|
|
174
|
-
self.
|
|
175
|
-
self.
|
|
176
|
-
|
|
177
|
-
self.
|
|
178
|
-
self.
|
|
179
|
-
|
|
168
|
+
# self._unit_time_for_count = 10 # 每隔多少秒计数,显示单位时间内执行多少次,暂时固定为10秒。
|
|
169
|
+
# self._execute_task_times_every_unit_time = 0 # 每单位时间执行了多少次任务。
|
|
170
|
+
# self._execute_task_times_every_unit_time_fail =0 # 每单位时间执行了多少次任务失败。
|
|
171
|
+
# self._lock_for_count_execute_task_times_every_unit_time = Lock()
|
|
172
|
+
# self._current_time_for_execute_task_times_every_unit_time = time.time()
|
|
173
|
+
# self._consuming_function_cost_time_total_every_unit_time = 0
|
|
174
|
+
# self._last_execute_task_time = time.time() # 最近一次执行任务的时间。
|
|
175
|
+
# self._last_10s_execute_count = 0
|
|
176
|
+
# self._last_10s_execute_count_fail = 0
|
|
177
|
+
#
|
|
178
|
+
# self._last_show_remaining_execution_time = 0
|
|
179
|
+
# self._show_remaining_execution_time_interval = 300
|
|
180
|
+
#
|
|
181
|
+
# self._msg_num_in_broker = 0
|
|
182
|
+
# self._last_timestamp_when_has_task_in_queue = 0
|
|
183
|
+
# self._last_timestamp_print_msg_num = 0
|
|
184
|
+
|
|
185
|
+
self.metric_calculation = MetricCalculation(self)
|
|
180
186
|
|
|
181
187
|
self._result_persistence_helper: ResultPersistenceHelper
|
|
182
188
|
self._check_broker_exclusive_config()
|
|
@@ -189,8 +195,8 @@ class AbstractConsumer(LoggerLevelSetterMixin, metaclass=abc.ABCMeta, ):
|
|
|
189
195
|
self._stop_flag = None
|
|
190
196
|
self._pause_flag = None # 暂停消费标志,从reids读取
|
|
191
197
|
self._last_show_pause_log_time = 0
|
|
192
|
-
self._redis_key_stop_flag = f'funboost_stop_flag
|
|
193
|
-
self._redis_key_pause_flag =
|
|
198
|
+
# self._redis_key_stop_flag = f'funboost_stop_flag'
|
|
199
|
+
# self._redis_key_pause_flag = RedisKeys.REDIS_KEY_PAUSE_FLAG
|
|
194
200
|
|
|
195
201
|
# 控频要用到的成员变量
|
|
196
202
|
self._last_submit_task_timestamp = 0
|
|
@@ -358,7 +364,7 @@ class AbstractConsumer(LoggerLevelSetterMixin, metaclass=abc.ABCMeta, ):
|
|
|
358
364
|
self._distributed_consumer_statistics.run()
|
|
359
365
|
self.logger.warning(f'启动了分布式环境 使用 redis 的键 hearbeat:{self._queue_name} 统计活跃消费者 ,当前消费者唯一标识为 {self.consumer_identification}')
|
|
360
366
|
|
|
361
|
-
self.keep_circulating(60, block=False, daemon=False)(self.check_heartbeat_and_message_count)()
|
|
367
|
+
self.keep_circulating(60, block=False, daemon=False)(self.check_heartbeat_and_message_count)()
|
|
362
368
|
if self.consumer_params.is_support_remote_kill_task:
|
|
363
369
|
kill_remote_task.RemoteTaskKiller(self.queue_name, None).start_cycle_kill_task()
|
|
364
370
|
self.consumer_params.is_show_message_get_from_broker = True # 方便用户看到从消息队列取出来的消息的task_id,然后使用task_id杀死运行中的消息。
|
|
@@ -403,13 +409,9 @@ class AbstractConsumer(LoggerLevelSetterMixin, metaclass=abc.ABCMeta, ):
|
|
|
403
409
|
logger_apscheduler = get_logger('push_for_apscheduler_use_database_store', log_filename='push_for_apscheduler_use_database_store.log')
|
|
404
410
|
|
|
405
411
|
@classmethod
|
|
406
|
-
def _push_apscheduler_task_to_broker(cls, queue_name, msg
|
|
412
|
+
def _push_apscheduler_task_to_broker(cls, queue_name, msg):
|
|
407
413
|
funboost_lazy_impoter.BoostersManager.get_or_create_booster_by_queue_name(queue_name).publish(msg)
|
|
408
|
-
|
|
409
|
-
# if RedisMixin().redis_db_frame.sadd(key, runonce_uuid): # 这样可以阻止多次启动同队列名消费者 redis jobstore多次运行函数.
|
|
410
|
-
# cls.logger_apscheduler.debug(f'延时任务用普通消息重新发布到普通队列 {msg}')
|
|
411
|
-
# funboost_lazy_impoter.BoostersManager.get_or_create_booster_by_queue_name(queue_name).publish(msg)
|
|
412
|
-
|
|
414
|
+
|
|
413
415
|
@abc.abstractmethod
|
|
414
416
|
def _shedual_task(self):
|
|
415
417
|
"""
|
|
@@ -512,7 +514,7 @@ class AbstractConsumer(LoggerLevelSetterMixin, metaclass=abc.ABCMeta, ):
|
|
|
512
514
|
# print(msg_no_delay)
|
|
513
515
|
# 数据库作为apscheduler的jobstores时候, 不能用 self.pbulisher_of_same_queue.publish,self不能序列化
|
|
514
516
|
self._delay_task_scheduler.add_job(self._push_apscheduler_task_to_broker, 'date', run_date=run_date,
|
|
515
|
-
kwargs={'queue_name': self.queue_name, 'msg': msg_no_delay,
|
|
517
|
+
kwargs={'queue_name': self.queue_name, 'msg': msg_no_delay, },
|
|
516
518
|
misfire_grace_time=misfire_grace_time,
|
|
517
519
|
)
|
|
518
520
|
self._confirm_consume(kw)
|
|
@@ -681,26 +683,7 @@ class AbstractConsumer(LoggerLevelSetterMixin, metaclass=abc.ABCMeta, ):
|
|
|
681
683
|
p.execute()
|
|
682
684
|
|
|
683
685
|
with self._lock_for_count_execute_task_times_every_unit_time:
|
|
684
|
-
self.
|
|
685
|
-
self._consuming_function_cost_time_total_every_unit_time += time.time() - t_start_run_fun
|
|
686
|
-
self._last_execute_task_time = time.time()
|
|
687
|
-
if time.time() - self._current_time_for_execute_task_times_every_unit_time > self._unit_time_for_count:
|
|
688
|
-
avarage_function_spend_time = round(self._consuming_function_cost_time_total_every_unit_time / self._execute_task_times_every_unit_time, 4)
|
|
689
|
-
msg = f'{self._unit_time_for_count} 秒内执行了 {self._execute_task_times_every_unit_time} 次函数 [ {self.consuming_function.__name__} ] ,' \
|
|
690
|
-
f'函数平均运行耗时 {avarage_function_spend_time} 秒。 '
|
|
691
|
-
self.logger.info(msg)
|
|
692
|
-
if time.time() - self._last_show_remaining_execution_time > self._show_remaining_execution_time_interval:
|
|
693
|
-
self._msg_num_in_broker = self.publisher_of_same_queue.get_message_count()
|
|
694
|
-
if self._msg_num_in_broker != -1: # 有的中间件无法统计或没实现统计队列剩余数量的,统一返回的是-1,不显示这句话。
|
|
695
|
-
# msg += f''' ,预计还需要 {time_util.seconds_to_hour_minute_second(self._msg_num_in_broker * avarage_function_spend_time / active_consumer_num)} 时间 才能执行完成 {self._msg_num_in_broker}个剩余的任务'''
|
|
696
|
-
need_time = time_util.seconds_to_hour_minute_second(self._msg_num_in_broker / (self._execute_task_times_every_unit_time / self._unit_time_for_count) /
|
|
697
|
-
self._distributed_consumer_statistics.active_consumer_num)
|
|
698
|
-
msg += f''' 预计还需要 {need_time} 时间 才能执行完成 队列 {self.queue_name} 中的 {self._msg_num_in_broker} 个剩余任务'''
|
|
699
|
-
self.logger.info(msg)
|
|
700
|
-
self._last_show_remaining_execution_time = time.time()
|
|
701
|
-
self._current_time_for_execute_task_times_every_unit_time = time.time()
|
|
702
|
-
self._consuming_function_cost_time_total_every_unit_time = 0
|
|
703
|
-
self._execute_task_times_every_unit_time = 0
|
|
686
|
+
self.metric_calculation.cal(t_start_run_fun,current_function_result_status)
|
|
704
687
|
self.user_custom_record_process_info_func(current_function_result_status) # 两种方式都可以自定义,记录结果,建议继承方式,不使用boost中指定 user_custom_record_process_info_func
|
|
705
688
|
if self.consumer_params.user_custom_record_process_info_func:
|
|
706
689
|
self.consumer_params.user_custom_record_process_info_func(current_function_result_status)
|
|
@@ -854,24 +837,7 @@ class AbstractConsumer(LoggerLevelSetterMixin, metaclass=abc.ABCMeta, ):
|
|
|
854
837
|
await simple_run_in_executor(push_result)
|
|
855
838
|
|
|
856
839
|
# 异步执行不存在线程并发,不需要加锁。
|
|
857
|
-
self.
|
|
858
|
-
self._consuming_function_cost_time_total_every_unit_time += time.time() - t_start_run_fun
|
|
859
|
-
self._last_execute_task_time = time.time()
|
|
860
|
-
if time.time() - self._current_time_for_execute_task_times_every_unit_time > self._unit_time_for_count:
|
|
861
|
-
avarage_function_spend_time = round(self._consuming_function_cost_time_total_every_unit_time / self._execute_task_times_every_unit_time, 4)
|
|
862
|
-
msg = f'{self._unit_time_for_count} 秒内执行了 {self._execute_task_times_every_unit_time} 次函数 [ {self.consuming_function.__name__} ] ,' \
|
|
863
|
-
f'函数平均运行耗时 {avarage_function_spend_time} 秒。 '
|
|
864
|
-
self.logger.info(msg)
|
|
865
|
-
if self._msg_num_in_broker != -1 and time.time() - self._last_show_remaining_execution_time > self._show_remaining_execution_time_interval: # 有的中间件无法统计或没实现统计队列剩余数量的,统一返回的是-1,不显示这句话。
|
|
866
|
-
# msg += f''' ,预计还需要 {time_util.seconds_to_hour_minute_second(self._msg_num_in_broker * avarage_function_spend_time / active_consumer_num)} 时间 才能执行完成 {self._msg_num_in_broker}个剩余的任务'''
|
|
867
|
-
need_time = time_util.seconds_to_hour_minute_second(self._msg_num_in_broker / (self._execute_task_times_every_unit_time / self._unit_time_for_count) /
|
|
868
|
-
self._distributed_consumer_statistics.active_consumer_num)
|
|
869
|
-
msg += f''' 预计还需要 {need_time} 时间 才能执行完成 队列 {self.queue_name} 中的 {self._msg_num_in_broker} 个剩余任务'''
|
|
870
|
-
self.logger.info(msg)
|
|
871
|
-
self._last_show_remaining_execution_time = time.time()
|
|
872
|
-
self._current_time_for_execute_task_times_every_unit_time = time.time()
|
|
873
|
-
self._consuming_function_cost_time_total_every_unit_time = 0
|
|
874
|
-
self._execute_task_times_every_unit_time = 0
|
|
840
|
+
self.metric_calculation.cal(t_start_run_fun, current_function_result_status)
|
|
875
841
|
|
|
876
842
|
self.user_custom_record_process_info_func(current_function_result_status) # 两种方式都可以自定义,记录结果.建议使用文档4.21.b的方式继承来重写
|
|
877
843
|
await self.aio_user_custom_record_process_info_func(current_function_result_status)
|
|
@@ -958,14 +924,15 @@ class AbstractConsumer(LoggerLevelSetterMixin, metaclass=abc.ABCMeta, ):
|
|
|
958
924
|
raise NotImplementedError
|
|
959
925
|
|
|
960
926
|
def check_heartbeat_and_message_count(self):
|
|
961
|
-
self.
|
|
962
|
-
|
|
963
|
-
|
|
964
|
-
|
|
965
|
-
|
|
966
|
-
|
|
967
|
-
|
|
968
|
-
|
|
927
|
+
self.metric_calculation.msg_num_in_broker = self.publisher_of_same_queue.get_message_count()
|
|
928
|
+
self.metric_calculation.last_get_msg_num_ts = time.time()
|
|
929
|
+
if time.time() - self.metric_calculation.last_timestamp_print_msg_num > 600:
|
|
930
|
+
if self.metric_calculation.msg_num_in_broker != -1:
|
|
931
|
+
self.logger.info(f'队列 [{self._queue_name}] 中还有 [{self.metric_calculation.msg_num_in_broker}] 个任务')
|
|
932
|
+
self.metric_calculation.last_timestamp_print_msg_num = time.time()
|
|
933
|
+
if self.metric_calculation.msg_num_in_broker != 0:
|
|
934
|
+
self.metric_calculation.last_timestamp_when_has_task_in_queue = time.time()
|
|
935
|
+
return self.metric_calculation.msg_num_in_broker
|
|
969
936
|
|
|
970
937
|
@abc.abstractmethod
|
|
971
938
|
def _requeue(self, kw):
|
|
@@ -1003,11 +970,11 @@ class AbstractConsumer(LoggerLevelSetterMixin, metaclass=abc.ABCMeta, ):
|
|
|
1003
970
|
|
|
1004
971
|
def pause_consume(self):
|
|
1005
972
|
"""从远程机器可以设置队列为暂停消费状态,funboost框架会自动停止消费,此功能需要配置好redis"""
|
|
1006
|
-
RedisMixin().redis_db_frame.
|
|
973
|
+
RedisMixin().redis_db_frame.hset(RedisKeys.REDIS_KEY_PAUSE_FLAG, self.queue_name,'1')
|
|
1007
974
|
|
|
1008
975
|
def continue_consume(self):
|
|
1009
976
|
"""从远程机器可以设置队列为暂停消费状态,funboost框架会自动继续消费,此功能需要配置好redis"""
|
|
1010
|
-
RedisMixin().redis_db_frame.
|
|
977
|
+
RedisMixin().redis_db_frame.hset(RedisKeys.REDIS_KEY_PAUSE_FLAG, self.queue_name,'0')
|
|
1011
978
|
|
|
1012
979
|
@decorators.FunctionResultCacher.cached_function_result_for_a_time(120)
|
|
1013
980
|
def _judge_is_daylight(self):
|
|
@@ -1031,9 +998,9 @@ class AbstractConsumer(LoggerLevelSetterMixin, metaclass=abc.ABCMeta, ):
|
|
|
1031
998
|
no_task_time = 0
|
|
1032
999
|
while 1:
|
|
1033
1000
|
# noinspection PyBroadException
|
|
1034
|
-
message_count = self.
|
|
1001
|
+
message_count = self.metric_calculation.msg_num_in_broker
|
|
1035
1002
|
# print(message_count,self._last_execute_task_time,time.time() - self._last_execute_task_time,no_task_time)
|
|
1036
|
-
if message_count == 0 and self.
|
|
1003
|
+
if message_count == 0 and self.metric_calculation.last_execute_task_time != 0 and (time.time() - self.metric_calculation.last_execute_task_time) > minutes * 60:
|
|
1037
1004
|
no_task_time += 30
|
|
1038
1005
|
else:
|
|
1039
1006
|
no_task_time = 0
|
|
@@ -1154,6 +1121,77 @@ def wait_for_possible_has_finish_all_tasks_by_conusmer_list(consumer_list: typin
|
|
|
1154
1121
|
pool.submit(consumer.wait_for_possible_has_finish_all_tasks(minutes))
|
|
1155
1122
|
|
|
1156
1123
|
|
|
1124
|
+
class MetricCalculation:
|
|
1125
|
+
UNIT_TIME_FOR_COUNT = 10 # 这个不要随意改,需要其他地方配合,每隔多少秒计数,显示单位时间内执行多少次,暂时固定为10秒。
|
|
1126
|
+
|
|
1127
|
+
def __init__(self,conusmer:AbstractConsumer) -> None:
|
|
1128
|
+
self.consumer = conusmer
|
|
1129
|
+
|
|
1130
|
+
self.unit_time_for_count = self.UNIT_TIME_FOR_COUNT #
|
|
1131
|
+
self.execute_task_times_every_unit_time_temp = 0 # 每单位时间执行了多少次任务。
|
|
1132
|
+
self.execute_task_times_every_unit_time_temp_fail =0 # 每单位时间执行了多少次任务失败。
|
|
1133
|
+
self.current_time_for_execute_task_times_every_unit_time = time.time()
|
|
1134
|
+
self.consuming_function_cost_time_total_every_unit_time = 0
|
|
1135
|
+
self.last_execute_task_time = time.time() # 最近一次执行任务的时间。
|
|
1136
|
+
self.last_x_s_execute_count = 0
|
|
1137
|
+
self.last_x_s_execute_count_fail = 0
|
|
1138
|
+
self.last_show_remaining_execution_time = 0
|
|
1139
|
+
self.show_remaining_execution_time_interval = 300
|
|
1140
|
+
self.msg_num_in_broker = 0
|
|
1141
|
+
self.last_get_msg_num_ts = 0
|
|
1142
|
+
self.last_timestamp_when_has_task_in_queue = 0
|
|
1143
|
+
self.last_timestamp_print_msg_num = 0
|
|
1144
|
+
self.avarage_function_spend_time = None
|
|
1145
|
+
self.total_consume_count_from_start =0
|
|
1146
|
+
self.total_consume_count_from_start_fail =0
|
|
1147
|
+
|
|
1148
|
+
def cal(self,t_start_run_fun:float,current_function_result_status:FunctionResultStatus):
|
|
1149
|
+
self.execute_task_times_every_unit_time_temp += 1
|
|
1150
|
+
self.total_consume_count_from_start +=1
|
|
1151
|
+
if current_function_result_status.success is False:
|
|
1152
|
+
self.execute_task_times_every_unit_time_temp_fail += 1
|
|
1153
|
+
self.total_consume_count_from_start_fail +=1
|
|
1154
|
+
self.consuming_function_cost_time_total_every_unit_time += time.time() - t_start_run_fun
|
|
1155
|
+
self.last_execute_task_time = time.time()
|
|
1156
|
+
if time.time() - self.current_time_for_execute_task_times_every_unit_time > self.unit_time_for_count:
|
|
1157
|
+
self.last_x_s_execute_count = self.execute_task_times_every_unit_time_temp
|
|
1158
|
+
self.last_x_s_execute_count_fail = self.execute_task_times_every_unit_time_temp_fail
|
|
1159
|
+
self.avarage_function_spend_time = round(self.consuming_function_cost_time_total_every_unit_time / self.last_x_s_execute_count, 4)
|
|
1160
|
+
msg = f'{self.unit_time_for_count} 秒内执行了 {self.last_x_s_execute_count} 次函数 [ {self.consumer.consuming_function.__name__} ] ,' \
|
|
1161
|
+
f'失败了{self.last_x_s_execute_count_fail} 次,函数平均运行耗时 {self.avarage_function_spend_time} 秒。 '
|
|
1162
|
+
self.consumer.logger.info(msg)
|
|
1163
|
+
if time.time() - self.last_show_remaining_execution_time > self.show_remaining_execution_time_interval:
|
|
1164
|
+
self.msg_num_in_broker = self.consumer.publisher_of_same_queue.get_message_count()
|
|
1165
|
+
self.last_get_msg_num_ts = time.time()
|
|
1166
|
+
if self.msg_num_in_broker != -1: # 有的中间件无法统计或没实现统计队列剩余数量的,统一返回的是-1,不显示这句话。
|
|
1167
|
+
# msg += f''' ,预计还需要 {time_util.seconds_to_hour_minute_second(self._msg_num_in_broker * avarage_function_spend_time / active_consumer_num)} 时间 才能执行完成 {self._msg_num_in_broker}个剩余的任务'''
|
|
1168
|
+
need_time = time_util.seconds_to_hour_minute_second(self.msg_num_in_broker / (self.execute_task_times_every_unit_time_temp / self.unit_time_for_count) /
|
|
1169
|
+
self.consumer._distributed_consumer_statistics.active_consumer_num)
|
|
1170
|
+
msg += f''' 预计还需要 {need_time} 时间 才能执行完成 队列 {self.consumer.queue_name} 中的 {self.msg_num_in_broker} 个剩余任务'''
|
|
1171
|
+
self.consumer.logger.info(msg)
|
|
1172
|
+
self.last_show_remaining_execution_time = time.time()
|
|
1173
|
+
self.current_time_for_execute_task_times_every_unit_time = time.time()
|
|
1174
|
+
self.consuming_function_cost_time_total_every_unit_time = 0
|
|
1175
|
+
self.execute_task_times_every_unit_time_temp = 0
|
|
1176
|
+
self.execute_task_times_every_unit_time_temp_fail = 0
|
|
1177
|
+
|
|
1178
|
+
def get_report_hearbeat_info(self) ->dict:
|
|
1179
|
+
return {
|
|
1180
|
+
'unit_time_for_count':self.unit_time_for_count,
|
|
1181
|
+
'last_x_s_execute_count':self.last_x_s_execute_count,
|
|
1182
|
+
'last_x_s_execute_count_fail':self.last_x_s_execute_count_fail,
|
|
1183
|
+
'last_execute_task_time':self.last_execute_task_time,
|
|
1184
|
+
# 'last_show_remaining_execution_time':self.last_show_remaining_execution_time,
|
|
1185
|
+
# 'msg_num_in_broker':self.msg_num_in_broker,
|
|
1186
|
+
'current_time_for_execute_task_times_every_unit_time':self.current_time_for_execute_task_times_every_unit_time,
|
|
1187
|
+
'last_timestamp_when_has_task_in_queue':self.last_timestamp_when_has_task_in_queue,
|
|
1188
|
+
'avarage_function_spend_time':self.avarage_function_spend_time,
|
|
1189
|
+
'total_consume_count_from_start':self.total_consume_count_from_start,
|
|
1190
|
+
'total_consume_count_from_start_fail':self.total_consume_count_from_start_fail
|
|
1191
|
+
|
|
1192
|
+
}
|
|
1193
|
+
|
|
1194
|
+
|
|
1157
1195
|
class DistributedConsumerStatistics(RedisMixin, FunboostFileLoggerMixin):
|
|
1158
1196
|
"""
|
|
1159
1197
|
为了兼容模拟mq的中间件(例如redis,他没有实现amqp协议,redis的list结构和真mq差远了),获取一个队列有几个连接活跃消费者数量。
|
|
@@ -1190,9 +1228,18 @@ class DistributedConsumerStatistics(RedisMixin, FunboostFileLoggerMixin):
|
|
|
1190
1228
|
self._server__consumer_identification_map_key_name = f'funboost_hearbeat_server__dict:{nb_log_config_default.computer_ip}'
|
|
1191
1229
|
|
|
1192
1230
|
def run(self):
|
|
1231
|
+
self._send_consumer_params()
|
|
1193
1232
|
self.send_heartbeat()
|
|
1194
1233
|
self._consumer.keep_circulating(self.SEND_HEARTBEAT_INTERVAL, block=False, daemon=False)(self.send_heartbeat)()
|
|
1195
1234
|
|
|
1235
|
+
def _send_consumer_params(self):
|
|
1236
|
+
"""
|
|
1237
|
+
保存队列的消费者参数,以便在web界面查看。
|
|
1238
|
+
:return:
|
|
1239
|
+
"""
|
|
1240
|
+
self.redis_db_frame.hmset('funboost_queue__consumer_parmas',{self._consumer.queue_name: self._consumer.consumer_params.json_str_value()})
|
|
1241
|
+
|
|
1242
|
+
|
|
1196
1243
|
def _send_heartbeat_with_dict_value(self, redis_key, ):
|
|
1197
1244
|
# 发送当前消费者进程心跳的,值是字典,按一个机器或者一个队列运行了哪些进程。
|
|
1198
1245
|
|
|
@@ -1206,10 +1253,19 @@ class DistributedConsumerStatistics(RedisMixin, FunboostFileLoggerMixin):
|
|
|
1206
1253
|
p.srem(redis_key, result)
|
|
1207
1254
|
self._consumer_identification_map['hearbeat_datetime_str'] = time_util.DatetimeConverter().datetime_str
|
|
1208
1255
|
self._consumer_identification_map['hearbeat_timestamp'] = self.timestamp()
|
|
1256
|
+
self._consumer_identification_map.update(self._consumer.metric_calculation.get_report_hearbeat_info())
|
|
1209
1257
|
value = Serialization.to_json_str(self._consumer_identification_map, )
|
|
1210
1258
|
p.sadd(redis_key, value)
|
|
1211
1259
|
p.execute()
|
|
1212
1260
|
|
|
1261
|
+
|
|
1262
|
+
def _send_msg_num(self):
|
|
1263
|
+
dic = {'msg_num_in_broker':self._consumer.metric_calculation.msg_num_in_broker,
|
|
1264
|
+
'last_get_msg_num_ts':self._consumer.metric_calculation.last_get_msg_num_ts,
|
|
1265
|
+
'report_ts':time.time(),
|
|
1266
|
+
}
|
|
1267
|
+
self.redis_db_frame.hset(RedisKeys.QUEUE__MSG_COUNT_MAP, self._consumer.queue_name, json.dumps(dic))
|
|
1268
|
+
|
|
1213
1269
|
def send_heartbeat(self):
|
|
1214
1270
|
# 根据队列名心跳的,值是字符串,方便值作为其他redis的键名
|
|
1215
1271
|
|
|
@@ -1226,6 +1282,7 @@ class DistributedConsumerStatistics(RedisMixin, FunboostFileLoggerMixin):
|
|
|
1226
1282
|
self._send_heartbeat_with_dict_value(self._server__consumer_identification_map_key_name)
|
|
1227
1283
|
self._show_active_consumer_num()
|
|
1228
1284
|
self._get_stop_and_pause_flag_from_redis()
|
|
1285
|
+
self._send_msg_num()
|
|
1229
1286
|
|
|
1230
1287
|
def _show_active_consumer_num(self):
|
|
1231
1288
|
self.active_consumer_num = self.redis_db_frame.scard(self._redis_key_name) or 1
|
|
@@ -1241,13 +1298,13 @@ class DistributedConsumerStatistics(RedisMixin, FunboostFileLoggerMixin):
|
|
|
1241
1298
|
|
|
1242
1299
|
# noinspection PyProtectedMember
|
|
1243
1300
|
def _get_stop_and_pause_flag_from_redis(self):
|
|
1244
|
-
stop_flag = self.redis_db_frame.
|
|
1301
|
+
stop_flag = self.redis_db_frame.hget(RedisKeys.REDIS_KEY_STOP_FLAG,self._consumer.queue_name)
|
|
1245
1302
|
if stop_flag is not None and int(stop_flag) == 1:
|
|
1246
1303
|
self._consumer._stop_flag = 1
|
|
1247
1304
|
else:
|
|
1248
1305
|
self._consumer._stop_flag = 0
|
|
1249
1306
|
|
|
1250
|
-
pause_flag = self.redis_db_frame.
|
|
1307
|
+
pause_flag = self.redis_db_frame.hget(RedisKeys.REDIS_KEY_PAUSE_FLAG,self._consumer.queue_name)
|
|
1251
1308
|
if pause_flag is not None and int(pause_flag) == 1:
|
|
1252
1309
|
self._consumer._pause_flag = 1
|
|
1253
1310
|
else:
|
|
@@ -126,7 +126,7 @@ class RedisConsumerAckAble(ConsumerConfirmMixinWithTheHelpOfRedisByHearbeat, Abs
|
|
|
126
126
|
pull_msg_batch_size = self.consumer_params.broker_exclusive_config['pull_msg_batch_size']
|
|
127
127
|
lua = f'''
|
|
128
128
|
local task_list = redis.call("lrange", KEYS[1],0,{pull_msg_batch_size-1})
|
|
129
|
-
redis.call("ltrim", KEYS[1],
|
|
129
|
+
redis.call("ltrim", KEYS[1],{pull_msg_batch_size},-1)
|
|
130
130
|
if (#task_list > 0) then
|
|
131
131
|
for task_index,task_value in ipairs(task_list)
|
|
132
132
|
do
|
|
@@ -1,9 +1,14 @@
|
|
|
1
1
|
import json
|
|
2
|
+
import time
|
|
2
3
|
import typing
|
|
3
4
|
|
|
5
|
+
from pydantic import main
|
|
6
|
+
|
|
4
7
|
from funboost.utils.redis_manager import RedisMixin
|
|
5
8
|
|
|
6
9
|
from funboost.core.loggers import FunboostFileLoggerMixin,nb_log_config_default
|
|
10
|
+
from funboost.core.serialization import Serialization
|
|
11
|
+
from funboost.constant import RedisKeys
|
|
7
12
|
|
|
8
13
|
class ActiveCousumerProcessInfoGetter(RedisMixin, FunboostFileLoggerMixin):
|
|
9
14
|
"""
|
|
@@ -23,6 +28,9 @@ class ActiveCousumerProcessInfoGetter(RedisMixin, FunboostFileLoggerMixin):
|
|
|
23
28
|
result_dict = json.loads(result)
|
|
24
29
|
if self.timestamp() - result_dict['hearbeat_timestamp'] < 15:
|
|
25
30
|
active_consumers_processor_info_list.append(result_dict)
|
|
31
|
+
if self.timestamp() - result_dict['current_time_for_execute_task_times_every_unit_time'] > 30:
|
|
32
|
+
result_dict['last_x_s_execute_count'] = 0
|
|
33
|
+
result_dict['last_x_s_execute_count_fail'] = 0
|
|
26
34
|
return active_consumers_processor_info_list
|
|
27
35
|
|
|
28
36
|
def get_all_hearbeat_info_by_queue_name(self, queue_name) -> typing.List[typing.Dict]:
|
|
@@ -67,6 +75,9 @@ class ActiveCousumerProcessInfoGetter(RedisMixin, FunboostFileLoggerMixin):
|
|
|
67
75
|
info_dict = json.loads(info_str)
|
|
68
76
|
if self.timestamp() - info_dict['hearbeat_timestamp'] < 15:
|
|
69
77
|
infos_map[dict_key].append(info_dict)
|
|
78
|
+
if self.timestamp() - info_dict['current_time_for_execute_task_times_every_unit_time'] > 30:
|
|
79
|
+
info_dict['last_x_s_execute_count'] = 0
|
|
80
|
+
info_dict['last_x_s_execute_count_fail'] = 0
|
|
70
81
|
return infos_map
|
|
71
82
|
|
|
72
83
|
def get_all_hearbeat_info_partition_by_queue_name(self) -> typing.Dict[typing.AnyStr, typing.List[typing.Dict]]:
|
|
@@ -80,3 +91,52 @@ class ActiveCousumerProcessInfoGetter(RedisMixin, FunboostFileLoggerMixin):
|
|
|
80
91
|
infos_map = self._get_all_hearbeat_info_partition_by_redis_key_prefix('funboost_hearbeat_server__dict:')
|
|
81
92
|
self.logger.info(f'获取所有机器ip对应的活跃消费者进程信息,按机器ip划分,结果是 {json.dumps(infos_map, indent=4)}')
|
|
82
93
|
return infos_map
|
|
94
|
+
|
|
95
|
+
|
|
96
|
+
|
|
97
|
+
class QueueConusmerParamsGetter(RedisMixin, FunboostFileLoggerMixin):
|
|
98
|
+
|
|
99
|
+
def get_queue_params(self):
|
|
100
|
+
queue__consumer_params_map = self.redis_db_frame.hgetall('funboost_queue__consumer_parmas')
|
|
101
|
+
return {k:Serialization.to_dict(v) for k,v in queue__consumer_params_map.items()}
|
|
102
|
+
|
|
103
|
+
def get_pause_flag(self):
|
|
104
|
+
queue__pause_map = self.redis_db_frame.hgetall(RedisKeys.REDIS_KEY_PAUSE_FLAG)
|
|
105
|
+
return {k:int(v) for k,v in queue__pause_map.items()}
|
|
106
|
+
|
|
107
|
+
def get_msg_num(self):
|
|
108
|
+
queue__msg_count_info_map = self.redis_db_frame.hgetall(RedisKeys.QUEUE__MSG_COUNT_MAP)
|
|
109
|
+
queue__msg_count_dict = {}
|
|
110
|
+
for queue_name,info_json in queue__msg_count_info_map.items():
|
|
111
|
+
info_dict = json.loads(info_json)
|
|
112
|
+
if info_dict['report_ts'] > time.time() - 15 and info_dict['last_get_msg_num_ts'] > time.time() - 1200:
|
|
113
|
+
queue__msg_count_dict[queue_name] = info_dict['msg_num_in_broker']
|
|
114
|
+
return queue__msg_count_dict
|
|
115
|
+
|
|
116
|
+
def get_queue_params_and_active_consumers(self):
|
|
117
|
+
queue__active_consumers_map = ActiveCousumerProcessInfoGetter().get_all_hearbeat_info_partition_by_queue_name()
|
|
118
|
+
queue__consumer_params_map = self.get_queue_params()
|
|
119
|
+
queue__pause_map = self.get_pause_flag()
|
|
120
|
+
queue__msg_count_dict = self.get_msg_num()
|
|
121
|
+
queue_params_and_active_consumers = {}
|
|
122
|
+
|
|
123
|
+
for queue, consumer_params in queue__consumer_params_map.items():
|
|
124
|
+
active_consumers = queue__active_consumers_map.get(queue, [])
|
|
125
|
+
all_consumers_last_x_s_execute_count = 0
|
|
126
|
+
all_consumers_last_x_s_execute_count_fail =0
|
|
127
|
+
for c in active_consumers:
|
|
128
|
+
all_consumers_last_x_s_execute_count += c['last_x_s_execute_count']
|
|
129
|
+
all_consumers_last_x_s_execute_count_fail += c['last_x_s_execute_count_fail']
|
|
130
|
+
queue_params_and_active_consumers[queue] = {
|
|
131
|
+
'queue_params':consumer_params,
|
|
132
|
+
'active_consumers':active_consumers,
|
|
133
|
+
'pause_flag':queue__pause_map.get(queue,-1),
|
|
134
|
+
'msg_num_in_broker':queue__msg_count_dict.get(queue,None),
|
|
135
|
+
'all_consumers_last_x_s_execute_count':all_consumers_last_x_s_execute_count,
|
|
136
|
+
'all_consumers_last_x_s_execute_count_fail':all_consumers_last_x_s_execute_count_fail,
|
|
137
|
+
}
|
|
138
|
+
return queue_params_and_active_consumers
|
|
139
|
+
|
|
140
|
+
|
|
141
|
+
if __name__ == '__main__':
|
|
142
|
+
print(Serialization.to_json_str(QueueConusmerParamsGetter().get_queue_params_and_active_consumers()))
|
funboost/core/current_task.py
CHANGED
|
@@ -158,6 +158,43 @@ def funboost_current_task():
|
|
|
158
158
|
else:
|
|
159
159
|
return thread_current_task
|
|
160
160
|
|
|
161
|
+
class _FctProxy:
|
|
162
|
+
"""后来多新增这个类了,"""
|
|
163
|
+
@property
|
|
164
|
+
def fct_context(self) ->FctContext:
|
|
165
|
+
ct = funboost_current_task()
|
|
166
|
+
return ct.get_fct_context()
|
|
167
|
+
|
|
168
|
+
@property
|
|
169
|
+
def function_params(self):
|
|
170
|
+
return self.fct_context.function_params
|
|
171
|
+
|
|
172
|
+
@property
|
|
173
|
+
def full_msg(self) -> dict:
|
|
174
|
+
return self.fct_context.full_msg
|
|
175
|
+
|
|
176
|
+
@property
|
|
177
|
+
def function_result_status(self) -> FunctionResultStatus:
|
|
178
|
+
return self.fct_context.function_result_status
|
|
179
|
+
|
|
180
|
+
@property
|
|
181
|
+
def task_id(self) -> FunctionResultStatus:
|
|
182
|
+
return self.fct_context.function_result_status.task_id
|
|
183
|
+
|
|
184
|
+
@property
|
|
185
|
+
def logger(self) -> logging.Logger:
|
|
186
|
+
return self.fct_context.logger
|
|
187
|
+
|
|
188
|
+
def __str__(self):
|
|
189
|
+
return f'<{self.__class__.__name__} [{self.function_result_status.get_status_dict()}]>'
|
|
190
|
+
|
|
191
|
+
"""
|
|
192
|
+
可以直接导入这个fct,不需要 手动写 fct = funboost_current_task() 了。 直接 from funboost import fct就完了,不需要先 fct = funboost_current_task()。
|
|
193
|
+
funboost的fct 相当于flask的request那种对象 ,自动线程/协程 级别隔离。 多个线程不会互相干扰。
|
|
194
|
+
"""
|
|
195
|
+
fct = _FctProxy()
|
|
196
|
+
|
|
197
|
+
|
|
161
198
|
|
|
162
199
|
def get_current_taskid():
|
|
163
200
|
# return fct.function_result_status.task_id
|
|
@@ -207,13 +207,13 @@ class BoosterParams(BaseJsonAbleModel):
|
|
|
207
207
|
# func_params_is_pydantic_model: bool = False # funboost 兼容支持 函数娼还是 pydantic model类型,funboost在发布之前和取出来时候自己转化。
|
|
208
208
|
|
|
209
209
|
consuming_function_kind: typing.Optional[str] = None # 自动生成的信息,不需要用户主动传参,如果自动判断失误就传递。是判断消费函数是函数还是实例方法还是类方法。如果传递了,就不自动获取函数类型。
|
|
210
|
-
|
|
210
|
+
""" consuming_function_kind 可以为以下类型,
|
|
211
211
|
class FunctionKind:
|
|
212
212
|
CLASS_METHOD = 'CLASS_METHOD'
|
|
213
213
|
INSTANCE_METHOD = 'INSTANCE_METHOD'
|
|
214
214
|
STATIC_METHOD = 'STATIC_METHOD'
|
|
215
215
|
COMMON_FUNCTION = 'COMMON_FUNCTION'
|
|
216
|
-
|
|
216
|
+
"""
|
|
217
217
|
|
|
218
218
|
auto_generate_info: dict = {} # 自动生成的信息,不需要用户主动传参.
|
|
219
219
|
|
|
@@ -87,7 +87,7 @@ class FunctionResultStatus():
|
|
|
87
87
|
'insert_minutes': datetime_str[:-3],
|
|
88
88
|
})
|
|
89
89
|
if not without_datetime_obj:
|
|
90
|
-
item.update({'insert_time':
|
|
90
|
+
item.update({'insert_time': time_util.DatetimeConverter().datetime_obj,
|
|
91
91
|
'utime': datetime.datetime.utcnow(),
|
|
92
92
|
})
|
|
93
93
|
else:
|
|
Binary file
|
|
Binary file
|