funboost 48.9__py3-none-any.whl → 49.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 CHANGED
@@ -13,7 +13,7 @@ set_frame_config这个模块的 use_config_form_funboost_config_module() 是核
13
13
  这段注释说明和使用的用户无关,只和框架开发人员有关.
14
14
  '''
15
15
 
16
- __version__ = "48.9"
16
+ __version__ = "49.1"
17
17
 
18
18
  from funboost.set_frame_config import show_frame_config
19
19
 
@@ -480,9 +480,9 @@ class AbstractConsumer(LoggerLevelSetterMixin, metaclass=abc.ABCMeta, ):
480
480
  return
481
481
  function_only_params = delete_keys_and_return_new_dict(kw['body'], )
482
482
  if self._get_priority_conf(kw, 'do_task_filtering') and self._redis_filter.check_value_exists(
483
- function_only_params): # 对函数的参数进行检查,过滤已经执行过并且成功的任务。
483
+ function_only_params,self._get_priority_conf(kw, 'filter_str')): # 对函数的参数进行检查,过滤已经执行过并且成功的任务。
484
484
  self.logger.warning(f'redis的 [{self._redis_filter_key_name}] 键 中 过滤任务 {kw["body"]}')
485
- self._confirm_consume(kw)
485
+ self._confirm_consume(kw) # 不运行就必须确认消费,否则会发不能确认消费,导致消息队列中间件认为消息没有被消费。
486
486
  return
487
487
  publish_time = get_publish_time(kw['body'])
488
488
  msg_expire_senconds_priority = self._get_priority_conf(kw, 'msg_expire_senconds')
@@ -678,7 +678,7 @@ class AbstractConsumer(LoggerLevelSetterMixin, metaclass=abc.ABCMeta, ):
678
678
  current_function_result_status.run_status = RunStatus.finish
679
679
  self._result_persistence_helper.save_function_result_to_mongo(current_function_result_status)
680
680
  if self._get_priority_conf(kw, 'do_task_filtering'):
681
- self._redis_filter.add_a_value(function_only_params) # 函数执行成功后,添加函数的参数排序后的键值对字符串到set中。
681
+ self._redis_filter.add_a_value(function_only_params,self._get_priority_conf(kw, 'filter_str')) # 函数执行成功后,添加函数的参数排序后的键值对字符串到set中。
682
682
  if current_function_result_status.success is False and current_retry_times == max_retry_times:
683
683
  log_msg = f'函数 {self.consuming_function.__name__} 达到最大重试次数 {self._get_priority_conf(kw, "max_retry_times")} 后,仍然失败, 入参是 {function_only_params} '
684
684
  if self.consumer_params.is_push_to_dlx_queue_when_retry_max_times:
@@ -832,7 +832,7 @@ class AbstractConsumer(LoggerLevelSetterMixin, metaclass=abc.ABCMeta, ):
832
832
  await simple_run_in_executor(self._result_persistence_helper.save_function_result_to_mongo, current_function_result_status)
833
833
  if self._get_priority_conf(kw, 'do_task_filtering'):
834
834
  # self._redis_filter.add_a_value(function_only_params) # 函数执行成功后,添加函数的参数排序后的键值对字符串到set中。
835
- await simple_run_in_executor(self._redis_filter.add_a_value, function_only_params)
835
+ await simple_run_in_executor(self._redis_filter.add_a_value, function_only_params,self._get_priority_conf(kw, 'filter_str'))
836
836
  if current_function_result_status.success is False and current_retry_times == max_retry_times:
837
837
  log_msg = f'函数 {self.consuming_function.__name__} 达到最大重试次数 {self._get_priority_conf(kw, "max_retry_times")} 后,仍然失败, 入参是 {function_only_params} '
838
838
  if self.consumer_params.is_push_to_dlx_queue_when_retry_max_times:
@@ -32,23 +32,36 @@ class RedisFilter(RedisMixin, FunboostFileLoggerMixin):
32
32
  self._redis_key_name = redis_key_name
33
33
  self._redis_filter_task_expire_seconds = redis_filter_task_expire_seconds
34
34
 
35
+ # @staticmethod
36
+ # def _get_ordered_str(value):
37
+ # """对json的键值对在redis中进行过滤,需要先把键值对排序,否则过滤会不准确如 {"a":1,"b":2} 和 {"b":2,"a":1}"""
38
+ # value = Serialization.to_dict(value)
39
+ # ordered_dict = OrderedDict()
40
+ # for k in sorted(value):
41
+ # ordered_dict[k] = value[k]
42
+ # return json.dumps(ordered_dict)
43
+
35
44
  @staticmethod
36
- def _get_ordered_str(value):
45
+ def generate_filter_str(value: typing.Union[str, dict], filter_str: typing.Optional[str] = None):
37
46
  """对json的键值对在redis中进行过滤,需要先把键值对排序,否则过滤会不准确如 {"a":1,"b":2} 和 {"b":2,"a":1}"""
47
+ if filter_str: # 如果用户单独指定了过滤字符串,就使用使用户指定的过滤字符串,否则使用排序后的键值对字符串
48
+ return filter_str
38
49
  value = Serialization.to_dict(value)
39
50
  ordered_dict = OrderedDict()
40
51
  for k in sorted(value):
41
52
  ordered_dict[k] = value[k]
53
+ # print(ordered_dict,filter_str)
42
54
  return json.dumps(ordered_dict)
43
55
 
44
- def add_a_value(self, value: typing.Union[str, dict]):
45
- self.redis_db_filter_and_rpc_result.sadd(self._redis_key_name, self._get_ordered_str(value))
46
56
 
47
- def manual_delete_a_value(self, value: typing.Union[str, dict]):
48
- self.redis_db_filter_and_rpc_result.srem(self._redis_key_name, self._get_ordered_str(value))
57
+ def add_a_value(self, value: typing.Union[str, dict], filter_str: typing.Optional[str] = None):
58
+ self.redis_db_filter_and_rpc_result.sadd(self._redis_key_name, self.generate_filter_str(value, filter_str))
49
59
 
50
- def check_value_exists(self, value):
51
- return self.redis_db_filter_and_rpc_result.sismember(self._redis_key_name, self._get_ordered_str(value))
60
+ def manual_delete_a_value(self, value: typing.Union[str, dict], filter_str: typing.Optional[str] = None):
61
+ self.redis_db_filter_and_rpc_result.srem(self._redis_key_name, self.generate_filter_str(value, filter_str))
62
+
63
+ def check_value_exists(self, value, filter_str: typing.Optional[str] = None):
64
+ return self.redis_db_filter_and_rpc_result.sismember(self._redis_key_name, self.generate_filter_str(value, filter_str))
52
65
 
53
66
  def delete_expire_filter_task_cycle(self):
54
67
  pass
@@ -61,15 +74,17 @@ class RedisImpermanencyFilter(RedisFilter):
61
74
  如果是30分钟内发布过这个任务,则不执行1 + 2,现在把这个逻辑集成到框架,一般用于接口缓存。
62
75
  """
63
76
 
64
- def add_a_value(self, value: typing.Union[str, dict]):
65
- self.redis_db_filter_and_rpc_result.zadd(self._redis_key_name, {self._get_ordered_str(value):time.time()})
77
+ def add_a_value(self, value: typing.Union[str, dict], filter_str: typing.Optional[str] = None):
78
+ self.redis_db_filter_and_rpc_result.zadd(self._redis_key_name, {self.generate_filter_str(value, filter_str):time.time()})
66
79
 
67
- def manual_delete_a_value(self, value: typing.Union[str, dict]):
68
- self.redis_db_filter_and_rpc_result.zrem(self._redis_key_name, self._get_ordered_str(value))
80
+ def manual_delete_a_value(self, value: typing.Union[str, dict], filter_str: typing.Optional[str] = None):
81
+ self.redis_db_filter_and_rpc_result.zrem(self._redis_key_name, self.generate_filter_str(value, filter_str))
69
82
 
70
- def check_value_exists(self, value):
71
- # print(self.redis_db_filter_and_rpc_result.zrank(self._redis_key_name, self._get_ordered_str(value)))
72
- return False if self.redis_db_filter_and_rpc_result.zrank(self._redis_key_name, self._get_ordered_str(value)) is None else True
83
+ def check_value_exists(self, value, filter_str: typing.Optional[str] = None):
84
+ # print(self.redis_db_filter_and_rpc_result.zrank(self._redis_key_name, self.generate_filter_str(value, filter_str)))
85
+ is_exists = False if self.redis_db_filter_and_rpc_result.zscore(self._redis_key_name, self.generate_filter_str(value, filter_str)) is None else True
86
+ # print(is_exists,value,filter_str,self.generate_filter_str(value, filter_str))
87
+ return is_exists
73
88
 
74
89
  @decorators.keep_circulating(60, block=False)
75
90
  def delete_expire_filter_task_cycle000(self):
@@ -111,16 +126,16 @@ class RedisImpermanencyFilterUsingRedisKey(RedisFilter):
111
126
  """
112
127
  return f'{self._redis_key_name}:{value.replace(":", ":")}' # 任务是json,带有:会形成很多树,换成中文冒号。
113
128
 
114
- def add_a_value(self, value: typing.Union[str, dict]):
115
- redis_key = self.__add_dir_prefix(self._get_ordered_str(value))
129
+ def add_a_value(self, value: typing.Union[str, dict], filter_str: typing.Optional[str] = None):
130
+ redis_key = self.__add_dir_prefix(self.generate_filter_str(value, filter_str))
116
131
  self.redis_db_filter_and_rpc_result.set(redis_key, 1)
117
132
  self.redis_db_filter_and_rpc_result.expire(redis_key, self._redis_filter_task_expire_seconds)
118
133
 
119
- def manual_delete_a_value(self, value: typing.Union[str, dict]):
120
- self.redis_db_filter_and_rpc_result.delete(self.__add_dir_prefix(self._get_ordered_str(value)))
134
+ def manual_delete_a_value(self, value: typing.Union[str, dict], filter_str: typing.Optional[str] = None):
135
+ self.redis_db_filter_and_rpc_result.delete(self.__add_dir_prefix(self.generate_filter_str(value, filter_str)))
121
136
 
122
- def check_value_exists(self, value):
123
- return True if self.redis_db_filter_and_rpc_result.exists(self.__add_dir_prefix(self._get_ordered_str(value))) else True
137
+ def check_value_exists(self, value, filter_str: typing.Optional[str] = None):
138
+ return True if self.redis_db_filter_and_rpc_result.exists(self.__add_dir_prefix(self.generate_filter_str(value, filter_str))) else True
124
139
 
125
140
  def delete_expire_filter_task_cycle(self):
126
141
  """
@@ -131,16 +146,17 @@ class RedisImpermanencyFilterUsingRedisKey(RedisFilter):
131
146
 
132
147
 
133
148
  if __name__ == '__main__':
134
- # filter = RedisFilter('filter_set:abcdefgh', 120)
135
- params_filter = RedisImpermanencyFilter('filter_zset:abcdef', 120)
149
+ # params_filter = RedisFilter('filter_set:abcdefgh2', 120)
150
+ params_filter = RedisImpermanencyFilter('filter_zset:abcdef2', 120)
136
151
  # params_filter = RedisImpermanencyFilterUsingRedisKey('filter_dir', 300)
137
152
  for i in range(10):
138
- params_filter.add_a_value({'x': i, 'y': i * 2})
139
-
140
- params_filter.manual_delete_a_value({'a': 1, 'b': 2})
141
- print(params_filter.check_value_exists({'a': 1, 'b': 2}))
142
- params_filter.delete_expire_filter_task_cycle()
143
- params_filter.add_a_value({'a': 1, 'b': 5})
144
- print(params_filter.check_value_exists({'a': 1, 'b': 2}))
145
- time.sleep(130)
146
- print(params_filter.check_value_exists({'a': 1, 'b': 2}))
153
+ # params_filter.add_a_value({'x': i, 'y': i * 2},str(i))
154
+ params_filter.add_a_value({'x': i, 'y': i * 2},None)
155
+
156
+ # params_filter.manual_delete_a_value({'a': 1, 'b': 2})
157
+ print(params_filter.check_value_exists({'x': 1, 'y': 2}))
158
+ # params_filter.delete_expire_filter_task_cycle()
159
+ # params_filter.add_a_value({'a': 1, 'b': 5})
160
+ # print(params_filter.check_value_exists({'a': 1, 'b': 2}))
161
+ # time.sleep(130)
162
+ # print(params_filter.check_value_exists({'a': 1, 'b': 2}))
@@ -308,6 +308,15 @@ class PriorityConsumingControlConfig(BaseJsonAbleModel):
308
308
  misfire_grace_time: typing.Union[int, None] = None
309
309
 
310
310
  other_extra_params: dict = None # 其他参数, 例如消息优先级 , priority_control_config=PriorityConsumingControlConfig(other_extra_params={'priroty': priorityxx}),
311
+
312
+ """filter_str:
313
+ 用户指定过滤字符串, 例如函数入参是 def fun(userid,username,sex,user_description),
314
+ 默认是所有入参一起组成json来过滤,但其实只把userid的值来过滤就好了。所以如果需要精准的按照什么过滤,用户来灵活指定一个字符串就好了
315
+
316
+ 用法见文档4.35
317
+ f3.publish(msg={'a':i,'b':i*2},priority_control_config=PriorityConsumingControlConfig(filter_str=str(i)))
318
+ """
319
+ filter_str :typing.Optional[str] = None
311
320
 
312
321
  @root_validator(skip_on_failure=True)
313
322
  def cehck_values(cls, values: dict):
@@ -221,10 +221,7 @@ def hearbeat_info_partion_by_ip():
221
221
  def get_queue_params_and_active_consumers():
222
222
  return jsonify(QueueConusmerParamsGetter().get_queue_params_and_active_consumers())
223
223
 
224
- @app.route('/queue/message_count/<broker_kind>/<queue_name>')
225
- def get_message_count(broker_kind,queue_name):
226
- publisher = BoostersManager.get_cross_project_publisher(PublisherParams(queue_name=queue_name, broker_kind=broker_kind, publish_msg_log_use_full_msg=True))
227
- return jsonify({'count':publisher.get_message_count(),'success':True})
224
+
228
225
 
229
226
 
230
227
  @app.route('/queue/clear/<broker_kind>/<queue_name>',methods=['POST'])
@@ -243,9 +240,27 @@ def resume_consume(queue_name):
243
240
  RedisMixin().redis_db_frame.hset(RedisKeys.REDIS_KEY_PAUSE_FLAG, queue_name,'0')
244
241
  return jsonify({'success':True})
245
242
 
246
- @app.route('/queue/get_msg_num',methods=['GET'])
247
- def get_msg_num():
243
+ @app.route('/queue/get_msg_num_all_queues',methods=['GET'])
244
+ def get_msg_num_all_queues():
245
+ """这个是通过消费者周期每隔10秒上报到redis的,性能好。不需要实时获取每个消息队列,直接从redis读取所有队列的消息数量"""
248
246
  return jsonify(QueueConusmerParamsGetter().get_msg_num(ignore_report_ts=True))
247
+
248
+ @app.route('/queue/message_count/<broker_kind>/<queue_name>')
249
+ def get_message_count(broker_kind,queue_name):
250
+ """这个是实时获取每个消息队列的消息数量,性能差,但是可以实时获取每个消息队列的消息数量"""
251
+ queue_params = QueueConusmerParamsGetter().get_queue_params()
252
+ for queue_namex,params in queue_params.items():
253
+ if params['broker_kind'] == broker_kind and queue_namex == queue_name:
254
+ publisher = BoostersManager.get_cross_project_publisher(
255
+ PublisherParams(queue_name=queue_name,
256
+ broker_kind=broker_kind,
257
+ broker_exclusive_config=params['broker_exclusive_config'],
258
+ publish_msg_log_use_full_msg=True))
259
+ return jsonify({'count':publisher.get_message_count(),'success':True})
260
+ return jsonify({'success':False,'msg':f'队列{queue_name}不存在'})
261
+
262
+ publisher = BoostersManager.get_cross_project_publisher(PublisherParams(queue_name=queue_name, broker_kind=broker_kind, publish_msg_log_use_full_msg=True))
263
+ return jsonify({'count':publisher.get_message_count(),'success':True})
249
264
 
250
265
  @app.route('/rpc/rpc_call',methods=['POST'])
251
266
  def rpc_call():
@@ -135,7 +135,7 @@
135
135
  // 页面加载完成后立即获取所有队列
136
136
  $(document).ready(function () {
137
137
  $.ajax({
138
- url: "{{ url_for('get_msg_num')}}",
138
+ url: "{{ url_for('get_msg_num_all_queues')}}",
139
139
  data: {},
140
140
  async: true,
141
141
  success: function (result) {
@@ -14,7 +14,7 @@ class RedisPriorityPublisher(FlushRedisQueueMixin,AbstractPublisher, RedisMixin,
14
14
 
15
15
  def custom_init(self):
16
16
  queue_list = [self._queue_name]
17
- x_max_priority = self.publisher_params.broker_exclusive_config['x-max-priority']
17
+ x_max_priority = self.publisher_params.broker_exclusive_config.get('x-max-priority')
18
18
  if x_max_priority:
19
19
  for i in range(1, x_max_priority + 1):
20
20
  queue_list.append(f'{self.queue_name}:{i}')
@@ -28,7 +28,7 @@ class RedisPriorityPublisher(FlushRedisQueueMixin,AbstractPublisher, RedisMixin,
28
28
  :return:
29
29
  """
30
30
  priority = self._get_from_other_extra_params('priroty', msg)
31
- x_max_priority = self.publisher_params.broker_exclusive_config['x-max-priority']
31
+ x_max_priority = self.publisher_params.broker_exclusive_config('x-max-priority')
32
32
  queue_name = self.queue_name
33
33
  if x_max_priority and priority:
34
34
  priority = min(priority, x_max_priority) # 防止有傻瓜发布消息的优先级priroty比最大支持的优先级还高。
@@ -59,7 +59,7 @@ class ThreadPoolExecutorForAps(BasePoolExecutor):
59
59
  ThreadPoolExecutor constructor
60
60
  """
61
61
 
62
- def __init__(self, max_workers=10, pool_kwargs=None):
62
+ def __init__(self, max_workers=100, pool_kwargs=None):
63
63
  pool = ThreadPoolExecutorShrinkAble(int(max_workers), )
64
64
  super().__init__(pool)
65
65
 
@@ -76,9 +76,23 @@ class ApsJobAdder:
76
76
  next_run_time=undefined, jobstore='default', executor='default',
77
77
  replace_existing=False, **trigger_args, ):
78
78
  """
79
- 这里的入参都是和apscheduler的add_job的入参一样的,funboost作者没有创造新的入参。
79
+ 1. 这里的入参都是和apscheduler的add_job的入参一样的,funboost作者没有创造新的入参。
80
80
  但是官方apscheduler的入参第一个入参是函数,
81
81
  funboost的ApsJobAdder对象.add_push_job入参去掉了函数,因为类的实例化时候会把函数传进来,不需要再麻烦用户一次了。
82
+
83
+
84
+ 2. add_push_job目的是 定时运行 消费函数.push方法发布消息到消费队列, 而不是 定时直接运行 消费函数自身。
85
+
86
+ 相当于 aps_obj.add_job(消费函数.push, trigger, args, kwargs, id, name, .....)
87
+ 那为什么 不直接使用 aps_obj.add_job(消费函数.push, trigger, args, kwargs, id, name, .....) 呢?因为 消费函数.push是实例方法,
88
+ 如果redis作为 jobstore, 消费函数.push 会报错,因为 消费函数.push 是实例方法,不能被序列化。只有普通函数和静态方法才能被序列化。
89
+ 所以开发了一个 add_push_job方法, 里面再去用 add_job, 使用 push_fun_params_to_broker 这个普通函数作为 add_job 的第一个入参,
90
+ 这个普通函数里面再去调用 消费函数.push 方法, 相当于是曲线救国避免 aps_obj.add_job(消费函数.push 不可序列化问题。
91
+
92
+
93
+ 3. 用户也可以自己定义一个普通函数my_push,你这个普通函数my_push 里面去调用消费函数.push方法;然后使用 aps_obj.add_job 使用你自己定义的这个my_push作为第一个入参。
94
+ 这种方式更容易你去理解,和apscheduler 官方库的原生写法一模一样。 但是不如 add_push_job 方便,因为 需要你亲自给每个消费函数分别定义一个普通函数my_push。
95
+
82
96
  """
83
97
 
84
98
  # if not getattr(self.aps_obj, 'has_started_flag', False):
@@ -118,14 +132,16 @@ if __name__ == '__main__':
118
132
  ApsJobAdder(sum_two_numbers, job_store_kind='redis').add_push_job(
119
133
  trigger='date',
120
134
  run_date='2025-01-17 23:25:40',
121
- args=(7, 8)
135
+ args=(7, 8),
136
+ id='date_job1'
122
137
  )
123
138
 
124
139
  # 方式2:固定间隔执行
125
140
  ApsJobAdder(sum_two_numbers, job_store_kind='memory').add_push_job(
126
141
  trigger='interval',
127
142
  seconds=5,
128
- args=(4, 6)
143
+ args=(4, 6),
144
+ id='interval_job1'
129
145
  )
130
146
 
131
147
  # 方式3:使用cron表达式定时执行
@@ -1,5 +1,5 @@
1
1
  ---
2
- noteId: "b2093fd13c5611f09da1a79051113d13"
2
+ noteId: "6d96e13045e411f0ab0bf59d9f569ed4"
3
3
  tags: []
4
4
 
5
5
  ---
@@ -1,5 +1,5 @@
1
1
  ---
2
- noteId: "b2093fd03c5611f09da1a79051113d13"
2
+ noteId: "6d97084045e411f0ab0bf59d9f569ed4"
3
3
  tags: []
4
4
 
5
5
  ---
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: funboost
3
- Version: 48.9
3
+ Version: 49.1
4
4
  Summary: pip install funboost,python全功能分布式函数调度框架,funboost的功能是全面性重量级,用户能想得到的功能99%全都有;funboost的使用方式是轻量级,只有@boost一行代码需要写。支持python所有类型的并发模式和一切知名消息队列中间件,支持如 celery dramatiq等框架整体作为funboost中间件,python函数加速器,框架包罗万象,用户能想到的控制功能全都有。一统编程思维,兼容50% python业务场景,适用范围广。只需要一行代码即可分布式执行python一切函数,funboost web manager 方便查看和管理消费函数;99%用过funboost的pythoner 感受是 简易 方便 强劲 强大,相见恨晚
5
5
  Home-page: https://github.com/ydf0509/funboost
6
6
  Author: bfzs
@@ -28,7 +28,7 @@ Classifier: Topic :: Software Development :: Libraries
28
28
  Description-Content-Type: text/markdown
29
29
  License-File: LICENSE
30
30
  Requires-Dist: nb-log >=13.2
31
- Requires-Dist: nb-libs >=1.8
31
+ Requires-Dist: nb-libs >=1.9
32
32
  Requires-Dist: nb-time >=2.4
33
33
  Requires-Dist: pymongo >=4.6.3
34
34
  Requires-Dist: AMQPStorm ==2.10.6
@@ -149,8 +149,8 @@ funboost的神奇之处在于它同时拥有"轻量级使用方式"和"重量级
149
149
  python函数加速器,框架包罗万象,一统编程思维,兼容50% python编程业务场景,适用范围广。
150
150
  python万能分布式函数调度框架,支持5种并发模式,30+种消息队列中间件
151
151
  (不仅支持几乎所有你能想到的消息队列中间件,还支持本地磁盘队列、数据库队列 (SQLAlchemy, Peewee)、
152
- 内存队列、甚至是 HTTP 请求、WebSocket 等作为任务队列,甚至是将 Celery、Dramatiq、Huey 等其他框架整体作为其 Broker。
153
- funboost源码高扩展性的设计,造成“万物皆可为Broker”,并不是有30种传统意义上的经典消息队列,
152
+ 内存队列、甚至是 HTTP 请求、WebSocket 等作为任务队列,甚至是将 Celery、Dramatiq、Huey 等其他框架整体作为其
153
+ Broker。funboost源码高扩展性的设计,造成“万物皆可为Broker”,并不是有30种传统意义上的经典消息队列,
154
154
  因为世界上总共都没有30种知名的经典消息队列),
155
155
  30种任务控制功能。给任意python函数赋能。
156
156
  用途概念就是常规经典的 生产者 + 消息队列中间件 + 消费者 编程思想。
@@ -630,15 +630,21 @@ if __name__ == '__main__':
630
630
 
631
631
 
632
632
  队列操作:查看和操作队列,包括 清空清空 暂停消费 恢复消费 调整qps和并发
633
- [![pEJC6m9.png](https://s21.ax1x.com/2025/03/04/pEJC6m9.png)](https://imgse.com/i/pEJC6m9)
633
+ <!-- [![pEJC6m9.png](https://s21.ax1x.com/2025/03/04/pEJC6m9.png)](https://imgse.com/i/pEJC6m9) -->
634
+ [![pVSOJcq.png](https://s21.ax1x.com/2025/05/27/pVSOJcq.png)](https://imgse.com/i/pVSOJcq)
634
635
 
635
636
  队列操作,查看消费者详情:查看队列的所有消费者详情
636
637
  [![pEJCgT1.png](https://s21.ax1x.com/2025/03/04/pEJCgT1.png)](https://imgse.com/i/pEJCgT1)
637
638
 
639
+ 队列操作:查看消费曲线图,查看各种消费指标。
640
+ [![pVpr7sP.png](https://s21.ax1x.com/2025/05/29/pVpr7sP.png)](https://imgse.com/i/pVpr7sP)
641
+
638
642
  rpc调用:在网页上对30种消息队列发布消息并获取消息的函数执行结;根据taskid获取结果。
639
643
  <!-- [![pETq8hj.png](https://s21.ax1x.com/2025/04/28/pETq8hj.png)](https://imgse.com/i/pETq8hj) -->
640
644
  [![pE7y8oT.png](https://s21.ax1x.com/2025/04/29/pE7y8oT.png)](https://imgse.com/i/pE7y8oT)
641
645
 
646
+
647
+
642
648
  ## 1.4 python分布式函数执行为什么重要?
643
649
 
644
650
  ```text
@@ -1,4 +1,4 @@
1
- funboost/__init__.py,sha256=s8FmFpSlhWb3iWADex2-Fjrh8VAEwHu03uko_z0mMFc,4093
1
+ funboost/__init__.py,sha256=w8fr_Qucjc_R_SrKCTAz3uB4kfMM_gSboSPNpvMX-LI,4093
2
2
  funboost/__init__old.py,sha256=9Kv3cPLnPkbzMRnuJFVkPsuDdx1CdcSIuITkpdncZSc,20382
3
3
  funboost/__main__.py,sha256=BetXBv7PkVeeK-UENiFq_KEEIzvObMI0rd8S1DHRdLU,1139
4
4
  funboost/constant.py,sha256=LreGf6HJ0TCj0HQKvfP4T5ZR9-KJUU7-SkoH1BM1HFM,9164
@@ -36,7 +36,7 @@ funboost/concurrent_pool/backup/async_pool_executor_back.py,sha256=KL6zEQaa1KkZO
36
36
  funboost/concurrent_pool/backup/async_pool_executor_janus.py,sha256=OHMWJ9l3EYTpPpcrPrGGKd4K0tmQ2PN8HiX0Dta0EOo,5728
37
37
  funboost/concurrent_pool/backup/grok_async_pool.py,sha256=3DgyB2aT0iHakb-pxd51WRKGIF7EKNNcX_ogDTF2hik,5825
38
38
  funboost/consumers/__init__.py,sha256=ZXY_6Kut1VYNQiF5aWEgIWobsW1ht9YUP0TdRZRWFqI,126
39
- funboost/consumers/base_consumer.py,sha256=_SyfHrpiyZBfK7bgtnP_vat5ko2ka4XVSclulCqo-VA,85912
39
+ funboost/consumers/base_consumer.py,sha256=5Z1T0NBdouC96Qs4b7uyaKEAxpYP5WzLbZfBtAMVstU,86164
40
40
  funboost/consumers/celery_consumer.py,sha256=nQpSkzPBJ4bRpxn4i9ms0axrJiq9RWhb4lG2nAdCIig,9254
41
41
  funboost/consumers/confirm_mixin.py,sha256=5xC9AAQr_MY4tbSed8U-M6tOVmh69Qv9X0ld0JLT9Tk,6185
42
42
  funboost/consumers/dramatiq_consumer.py,sha256=ozmeAfeF0U-YNYHK4suQB0N264h5AZdfMH0O45Mh-8A,2229
@@ -69,7 +69,7 @@ funboost/consumers/redis_consumer_ack_able.py,sha256=CiPIo-ToujSUy1uvznWc1QuxBfe
69
69
  funboost/consumers/redis_consumer_ack_using_timeout.py,sha256=E-7Ci3yb1-THvGoqDNyHJm_zYThIcacHEuUQbPU5e-0,4228
70
70
  funboost/consumers/redis_consumer_priority.py,sha256=C-ftnlGPPoB7YV3GvwLu9POVGDn_GKlBqO6NlsZ-hdY,5566
71
71
  funboost/consumers/redis_consumer_simple.py,sha256=trPrMHSVU1Y_fY-xz5tFFmt1zjV-9_joPYRHqvyZnL8,874
72
- funboost/consumers/redis_filter.py,sha256=UlkTQSZQSwb98nqkAHkrEKVri_XxHF0T7HmpghZ597s,7316
72
+ funboost/consumers/redis_filter.py,sha256=GYx4dTgTv05oyP-2fKLpOeRdzot-J9ZO7H_8fsbhHfQ,8810
73
73
  funboost/consumers/redis_pubsub_consumer.py,sha256=8V8EqobKpEXzpsQx_H3A-JBKLsWOsE0n16g62tUMMYY,1122
74
74
  funboost/consumers/redis_stream_consumer.py,sha256=gkdP-BxU8U6cE5bVBVQCfgAMeQmsz6gQyQgqi6r2kf8,6553
75
75
  funboost/consumers/rocketmq_consumer.py,sha256=23KLRz8iO9e_x7asrceRJYhwJFMruUgmKw7m3pHVkw0,1692
@@ -93,7 +93,7 @@ funboost/core/exceptions.py,sha256=pLF7BkRJAfDiWp2_xGZqencmwdPiSQI1NENbImExknY,1
93
93
  funboost/core/fabric_deploy_helper.py,sha256=foieeqlNySuU9axJzNF6TavPjIUSYBx9UO3syVKUiyY,9999
94
94
  funboost/core/funboost_config_getter.py,sha256=b5nAdAmUxahskY-ohB7ptf2gKywFlDA0Fq1cWroxxbs,384
95
95
  funboost/core/funboost_time.py,sha256=a0MacbUBfYk8mf7D3UUyCxH5QJsu8YiGVXwJqPnSQH0,1779
96
- funboost/core/func_params_model.py,sha256=-gSYCyMwOh-f_lEfpaanqPKWFuW7XzABIkc1nApBLCk,22942
96
+ funboost/core/func_params_model.py,sha256=MuTv_AnQdFKpH2qdsoVFTcUlWWQ9YGqsjEeFCJIH1SQ,23469
97
97
  funboost/core/function_result_status_config.py,sha256=PyjqAQOiwsLt28sRtH-eYRjiI3edPFO4Nde0ILFRReE,1764
98
98
  funboost/core/function_result_status_saver.py,sha256=oG8dEK_APy6FL5oqQI0RPU6OLuWqp4Gv09hwOxPvvNs,9293
99
99
  funboost/core/helper_funs.py,sha256=SsMa7A3iJyLek6v1qRK02kINebDp6kuAmlYkrYLXwQ0,2369
@@ -112,10 +112,10 @@ funboost/factories/__init__.py,sha256=s7kKKjR1HU5eMjPD6r5b-SXTVMo1zBp2JjOAtkyt5Y
112
112
  funboost/factories/broker_kind__publsiher_consumer_type_map.py,sha256=-kKhV65KnRf86b353PJuaEMXMolfV4B2CtTF1wD1kFQ,10189
113
113
  funboost/factories/consumer_factory.py,sha256=EaAw3OZJkGepkQxKkDvMshHjIVOQva_N6nUEhLO4JwU,1500
114
114
  funboost/factories/publisher_factotry.py,sha256=4651sxnbIAi6sFEUQdlUuv8UkbMQIE_Pbzm-1DimAs8,2535
115
- funboost/function_result_web/app.py,sha256=a10Ctvdt_OgjFMe4MrSUdC9edhvb2dDp4YWGfMzT3b0,11624
115
+ funboost/function_result_web/app.py,sha256=pFtWgnndjOucnJlf4HDhPKyBcF3wD2WbTbrtVs1JZ5Y,12673
116
116
  funboost/function_result_web/app_debug_start.py,sha256=OP79Wp3QWRjaKNAKXeW4WZ4-UCGgeYyT5ppykblMK8E,152
117
117
  funboost/function_result_web/functions.py,sha256=iPaYcg7reUFXe2HclG6jzavQPcjwLOt3rUdZ7-FghXE,11286
118
- funboost/function_result_web/__pycache__/app.cpython-37.pyc,sha256=xOqvwi3wEmHCKMhLYwkKIk0mG5MEKrQ4mljb1fOiMVI,9809
118
+ funboost/function_result_web/__pycache__/app.cpython-37.pyc,sha256=6457vOwd4y-_-zAfT_3hRhxuTbareCucMlczqDdkYwk,10388
119
119
  funboost/function_result_web/__pycache__/app.cpython-39.pyc,sha256=qdvJ3NlUvov7tAmCl0WfNTOUh0NVpeAST4b8OLHpLXw,9828
120
120
  funboost/function_result_web/__pycache__/functions.cpython-37.pyc,sha256=NDy7JvbD5Q_VCWkXSV2jTpgdbOWNKDpHQbqm3c7523Q,5940
121
121
  funboost/function_result_web/__pycache__/functions.cpython-39.pyc,sha256=HY1LCsLzLfDgzgYc5_IgpQ8LFhWP9PnKh5V05XM_rJ4,6026
@@ -164,7 +164,7 @@ funboost/function_result_web/templates/index.html,sha256=2eHJau1boHAu1N3VMOax5bz
164
164
  funboost/function_result_web/templates/index_backup.html,sha256=qwUWHyQFrbSVSnZUjU5E9Zxy6a9mEJF_BdH0HeUxXOU,20045
165
165
  funboost/function_result_web/templates/login.html,sha256=q37dj7O0LeyiV38Zd5P1Qn_qmhjdFomuYTRY1Yk48Bo,2007
166
166
  funboost/function_result_web/templates/queue_op.html,sha256=8de8jX47zIJ-g-zi6AgcsvfXY3IFpjuoQCzmzsjfK-k,46925
167
- funboost/function_result_web/templates/rpc_call.html,sha256=CpKeOOlaLhkn4v7iNY8J9PAp-38j32-La9JIkaQVig0,15600
167
+ funboost/function_result_web/templates/rpc_call.html,sha256=qFznWysEFTvJKUQYqnJaapB833XTjfa8UlEKNk2nFxI,15611
168
168
  funboost/function_result_web/templates/running_consumer_by_ip.html,sha256=2Rcxbi80c1JEIRCnNe1MG55axdb0vBlkB6yL9rxw53c,10248
169
169
  funboost/function_result_web/templates/running_consumer_by_queue_name.html,sha256=r5EYlfp0fE8RFWzI0k3K571EUmirwcPX9NdnSEfAWiQ,10301
170
170
  funboost/publishers/__init__.py,sha256=xqBHlvsJQVPfbdvP84G0LHmVB7-pFBS7vDnX1Uo9pVY,131
@@ -195,7 +195,7 @@ funboost/publishers/rabbitmq_pika_publisher.py,sha256=QygZv96tYmfJcs8Dukv3J_x134
195
195
  funboost/publishers/rabbitmq_rabbitpy_publisher.py,sha256=GGXPKxE6-mAjqMIKqwvR9d7L-zuJQcQoU9uRsQLNGas,1953
196
196
  funboost/publishers/redis_publisher.py,sha256=EDnQSt73FE2PGq0WHyvsMuaoD_6wp_tuZveWrMF7iLY,3439
197
197
  funboost/publishers/redis_publisher_lpush.py,sha256=xEWuCTtbDcKFLTxGrECrbIVapFfUwqX2-GHenMClu-Q,278
198
- funboost/publishers/redis_publisher_priority.py,sha256=7qiGBZN4k-YQ1EML8kAXCYlpcOC8Yl5qNfPUeMgevlw,2205
198
+ funboost/publishers/redis_publisher_priority.py,sha256=_xbrRaaA3upouUuvmw1pJl4WsIm2zSNsEE6PdxqKuj4,2209
199
199
  funboost/publishers/redis_publisher_simple.py,sha256=CxUqi2_ykOhKhQFByNJ8nUaFjjchA-YzSE72ng1_OiM,760
200
200
  funboost/publishers/redis_pubsub_publisher.py,sha256=GA3TzN91OT2d-Ei76rp_Sd3Au8IcihFupo36izB8dis,737
201
201
  funboost/publishers/redis_queue_flush_mixin.py,sha256=d-AgJiCuX25mOwP2gMorGbXCaLuFpqWO4jAJXJDBfk0,732
@@ -214,8 +214,8 @@ funboost/queues/sqla_queue.py,sha256=b-2QPvvuMxklm41ibZhGKehaAV9trXBQFCOHzgThx_s
214
214
  funboost/timing_job/__init__.py,sha256=_rIiU7pMXe-IwUCeok50hSiWXoUoMBar22u6-pZLlAI,265
215
215
  funboost/timing_job/apscheduler_use_mysql_store.py,sha256=ss92DiSLzbWuVIo19sTLgC99GessltWLOlqqOd4AIL4,471
216
216
  funboost/timing_job/apscheduler_use_redis_store.py,sha256=cGqwULlYhoApzheDliTVn1CN3Ks_cSGDjObEhbSV9IU,2990
217
- funboost/timing_job/timing_job_base.py,sha256=IlTZBmq53wftsQfLl2M-jLJb7adGXQr5e1jCyGlKF0M,9582
218
- funboost/timing_job/timing_push.py,sha256=0inlo-XtHcEWQJVzGQHkTB-40rdIoduREdP9Ux8Yuh8,5930
217
+ funboost/timing_job/timing_job_base.py,sha256=Pd_U2Ot-ds91x-2kzznXS8zx_oVAE16x7V0KJUAtljw,9583
218
+ funboost/timing_job/timing_push.py,sha256=QzIg8_apyYZBpkGX2G_mmAHRcY-VbyhftK-Bdr5CKm4,7382
219
219
  funboost/utils/__init__.py,sha256=rAyXE7lgCo_3VdMvGrIJiqsTHv2nZPTJDTj1f6s_KgE,586
220
220
  funboost/utils/apscheduler_monkey.py,sha256=CcUISbqX6nMWSxr_QjZ26IvvhUk_ojYZWRaKenpsKfE,3124
221
221
  funboost/utils/block_exit.py,sha256=BnfxNYo3lnmhk686RAEoc4u3D4RU_iEMMMgu5L8gIuI,96
@@ -251,7 +251,7 @@ funboost/utils/dependency_packages/mongomq/test.py,sha256=Tcmme3U3KXFSkdknO71bge
251
251
  funboost/utils/dependency_packages/mongomq/utils.py,sha256=ljhcLhNf3yOc7IgnuRdFqLtwTGynRNd2uXZNRvStAL0,377
252
252
  funboost/utils/dependency_packages_in_pythonpath/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
253
253
  funboost/utils/dependency_packages_in_pythonpath/add_to_pythonpath.py,sha256=eOaK0Cr1yAmLcHhOM5-nV9XxXhQFZQkiaBECY65sFuc,341
254
- funboost/utils/dependency_packages_in_pythonpath/readme.md,sha256=gNWQveLrJxQrKnWGlcCT0jkO-tc13I6zwdUOU1ftsxA,982
254
+ funboost/utils/dependency_packages_in_pythonpath/readme.md,sha256=uONAElG6s0hHEOB3U6ubvkrCXoaTgpPFjcxmVTL-1y8,982
255
255
  funboost/utils/dependency_packages_in_pythonpath/__pycache__/__init__.cpython-311.pyc,sha256=XpiufbSphxt8UXSMqXz9vOGejT4noRAKE3dnfsEGvAg,187
256
256
  funboost/utils/dependency_packages_in_pythonpath/__pycache__/__init__.cpython-313.pyc,sha256=n9egw99NJyOXwjRjq5tgNKC0GlCjFb54efJ1XeBnZC0,175
257
257
  funboost/utils/dependency_packages_in_pythonpath/__pycache__/__init__.cpython-37.pyc,sha256=Oy1-_q-VLcFLQ7RRp8B-fbBkNOb2SepePLYW4L5DQ6U,165
@@ -268,7 +268,7 @@ funboost/utils/dependency_packages_in_pythonpath/aioredis/exceptions.py,sha256=S
268
268
  funboost/utils/dependency_packages_in_pythonpath/aioredis/lock.py,sha256=CnB9LpvykAEXEdQyBEcgUU7iHxwNF3xuGzX7UYuUbiQ,11651
269
269
  funboost/utils/dependency_packages_in_pythonpath/aioredis/log.py,sha256=qTxLRo5EqoHZIGqMguzLm90mtThBRYje_FaZz-fDbhg,427
270
270
  funboost/utils/dependency_packages_in_pythonpath/aioredis/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
271
- funboost/utils/dependency_packages_in_pythonpath/aioredis/readme.md,sha256=gxkZR770OAlGDCx541CAam-xxScp2CCkDTogbHrMI-E,679
271
+ funboost/utils/dependency_packages_in_pythonpath/aioredis/readme.md,sha256=ALgrpiwrKqq8SMrWN2cwtEZ9rO67ju_UWsN3hqsVvbs,679
272
272
  funboost/utils/dependency_packages_in_pythonpath/aioredis/sentinel.py,sha256=ldPwRIOWVskKsItBwFqbqPfraMlFsGs42_ZDXtxza5U,12536
273
273
  funboost/utils/dependency_packages_in_pythonpath/aioredis/utils.py,sha256=90Dgj62Q9ABGVAwAPwufo3OKM2s0ws6LeQMZg8ifJb0,1284
274
274
  funboost/utils/dependency_packages_in_pythonpath/aioredis/__pycache__/__init__.cpython-311.pyc,sha256=PGpc0JrGU6UjKZ7WgxlpsCL2OuhJhWpE9WrxMHREtPc,1696
@@ -327,9 +327,9 @@ funboost/utils/pysnooper_ydf/utils.py,sha256=evSmGi_Oul7vSP47AJ0DLjFwoCYCfunJZ1m
327
327
  funboost/utils/pysnooper_ydf/variables.py,sha256=QejRDESBA06KG9OH4sBT4J1M55eaU29EIHg8K_igaXo,3693
328
328
  funboost/utils/times/__init__.py,sha256=Y4bQD3SIA_E7W2YvHq2Qdi0dGM4H2DxyFNdDOuFOq1w,2417
329
329
  funboost/utils/times/version.py,sha256=11XfnZVVzOgIhXXdeN_mYfdXThfrsbQHpA0wCjz-hpg,17
330
- funboost-48.9.dist-info/LICENSE,sha256=9EPP2ktG_lAPB8PjmWV-c9BiaJHc_FP6pPLcUrUwx0E,11562
331
- funboost-48.9.dist-info/METADATA,sha256=JxCma5abiSNXdfWjYodgKxq6QPjxzKwhEj0eO2U4y9k,39540
332
- funboost-48.9.dist-info/WHEEL,sha256=yQN5g4mg4AybRjkgi-9yy4iQEFibGQmlz78Pik5Or-A,92
333
- funboost-48.9.dist-info/entry_points.txt,sha256=yMSSAGRzRAAhGyNNQHw24MooKlDZsaJ499_D6fPl58A,96
334
- funboost-48.9.dist-info/top_level.txt,sha256=K8WuKnS6MRcEWxP1NvbmCeujJq6TEfbsB150YROlRw0,9
335
- funboost-48.9.dist-info/RECORD,,
330
+ funboost-49.1.dist-info/LICENSE,sha256=9EPP2ktG_lAPB8PjmWV-c9BiaJHc_FP6pPLcUrUwx0E,11562
331
+ funboost-49.1.dist-info/METADATA,sha256=PGql-fv48PjcLBvgtQRrIqM90RBirSAv_0E-4tWJ2rQ,39804
332
+ funboost-49.1.dist-info/WHEEL,sha256=yQN5g4mg4AybRjkgi-9yy4iQEFibGQmlz78Pik5Or-A,92
333
+ funboost-49.1.dist-info/entry_points.txt,sha256=yMSSAGRzRAAhGyNNQHw24MooKlDZsaJ499_D6fPl58A,96
334
+ funboost-49.1.dist-info/top_level.txt,sha256=K8WuKnS6MRcEWxP1NvbmCeujJq6TEfbsB150YROlRw0,9
335
+ funboost-49.1.dist-info/RECORD,,