funboost 50.1__py3-none-any.whl → 50.3__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.

@@ -47,6 +47,48 @@
47
47
  width: 80%; /* 宽度占屏幕的80% */
48
48
  max-width: 1400px; /* 最大宽度限制 */
49
49
  }
50
+ /* 新增: 开关切换样式 */
51
+ .toggle-switch-container {
52
+ display: flex;
53
+ align-items: center;
54
+ margin-left: 30px;
55
+ }
56
+ .switch {
57
+ position: relative;
58
+ display: inline-block;
59
+ width: 50px; /* 宽度调整 */
60
+ height: 24px; /* 高度调整 */
61
+ }
62
+ .switch input {
63
+ opacity: 0;
64
+ width: 0;
65
+ height: 0;
66
+ }
67
+ .slider {
68
+ position: absolute;
69
+ cursor: pointer;
70
+ top: 0;
71
+ left: 0;
72
+ right: 0;
73
+ bottom: 0;
74
+ background-color: #ccc;
75
+ transition: .4s;
76
+ }
77
+ .slider:before {
78
+ position: absolute;
79
+ content: "";
80
+ height: 18px; /* 滑块大小 */
81
+ width: 18px; /* 滑块大小 */
82
+ left: 3px; /* 滑块位置 */
83
+ bottom: 3px; /* 滑块位置 */
84
+ background-color: white;
85
+ transition: .4s;
86
+ }
87
+ input:checked + .slider { background-color: #4CAF50; }
88
+ input:focus + .slider { box-shadow: 0 0 1px #4CAF50; }
89
+ input:checked + .slider:before { transform: translateX(26px); }
90
+ .slider.round { border-radius: 24px; }
91
+ .slider.round:before { border-radius: 50%; }
50
92
  </style>
51
93
  </head>
52
94
  <body>
@@ -60,6 +102,14 @@
60
102
  </button>
61
103
  </span>
62
104
  </div>
105
+ <!-- 美化后的复选框 -->
106
+ <div class="toggle-switch-container">
107
+ <label class="switch">
108
+ <input type="checkbox" id="showActiveQueuesOnly" onchange="toggleActiveQueuesFilter()">
109
+ <span class="slider round"></span>
110
+ </label>
111
+ <span style="margin-left: 8px; cursor: pointer;" onclick="document.getElementById('showActiveQueuesOnly').click();">正在消费的队列</span>
112
+ </div>
63
113
  <button id="refresh-all-msg-counts" class="btn btn-info" style="margin-left: 30px;">更新所有队列的消息数量</button>
64
114
  <button id="toggle-auto-refresh" class="btn btn-success" style="margin-left: 10px;">启动自动刷新</button>
65
115
  <button id="show-explanation-btn" class="btn btn-default" style="margin-left: 10px;">说明</button>
@@ -76,12 +126,28 @@
76
126
  <button type="button" class="close" data-dismiss="modal" aria-label="Close"><span aria-hidden="true">&times;</span></button>
77
127
  </div>
78
128
  <div class="modal-body">
79
- <!-- 新增:时间范围筛选控件 -->
80
- <div style="margin-bottom: 10px; display: flex; align-items: center;">
81
- <label style="margin-right:5px;">起始时间:</label>
82
- <input type="datetime-local" id="chartStartTime" style="margin-right: 10px;">
83
- <label style="margin-right:5px;">结束时间:</label>
84
- <input type="datetime-local" id="chartEndTime" style="margin-right: 10px;">
129
+ <!-- 新增:时间范围筛选控件和采样点数选择 -->
130
+ <div style="margin-bottom: 10px; display: flex; align-items: center; flex-wrap: wrap; gap: 10px;">
131
+ <div style="display: flex; align-items: center;">
132
+ <label style="margin-right:5px;">起始时间:</label>
133
+ <input type="datetime-local" id="chartStartTime" style="margin-right: 10px;">
134
+ </div>
135
+ <div style="display: flex; align-items: center;">
136
+ <label style="margin-right:5px;">结束时间:</label>
137
+ <input type="datetime-local" id="chartEndTime" style="margin-right: 10px;">
138
+ </div>
139
+ <div style="display: flex; align-items: center;">
140
+ <label style="margin-right:5px;">采样点数:</label>
141
+ <select id="curveSamplesCount" class="form-control" style="width: 100px; margin-right: 10px;" title="推荐使用360-720个采样点以获得最佳显示效果">
142
+ <option value="60">60 (粗略)</option>
143
+ <option value="120">120 (简单)</option>
144
+ <option value="180">180 (清晰)</option>
145
+ <option value="360" selected>360 (推荐)</option>
146
+ <option value="720">720 (精细)</option>
147
+ <option value="1440">1440 (超精细)</option>
148
+ <option value="8640">8640 (极精细)</option>
149
+ </select>
150
+ </div>
85
151
  <button class="btn btn-primary btn-sm" onclick="reloadQueueChartWithTimeRange()">查询</button>
86
152
  </div>
87
153
  <canvas id="queueDataChart" style="height:600px;max-height:600px;"></canvas>
@@ -115,6 +181,14 @@
115
181
 
116
182
 
117
183
  <script>
184
+ // 检查Chart.js是否正确加载
185
+ if (typeof Chart === 'undefined') {
186
+ console.error('Chart.js未正确加载!请检查文件路径。');
187
+ alert('Chart.js库未正确加载,图表功能可能无法正常使用。');
188
+ } else {
189
+ console.log('Chart.js已正确加载,版本:', Chart.version);
190
+ }
191
+
118
192
  // 创建表格实例
119
193
  var table = new Tabulator("#queue-table", {
120
194
  theme: "bootstrap3",
@@ -269,15 +343,32 @@
269
343
  },
270
344
  });
271
345
 
346
+ function updateTableFilters() {
347
+ const searchValue = document.getElementById("searchInput").value;
348
+ const showActiveOnly = document.getElementById('showActiveQueuesOnly').checked;
349
+
350
+ const filters = [];
351
+
352
+ if (searchValue) {
353
+ filters.push({field: "queue_name", type: "like", value: searchValue});
354
+ }
355
+
356
+ if (showActiveOnly) {
357
+ filters.push({field: "consumer_count", type: ">", value: 0});
358
+ }
359
+
360
+ table.setFilter(filters);
361
+ }
362
+
272
363
  // 搜索功能实现
273
364
  document.getElementById("searchInput").addEventListener("keyup", function() {
274
- table.setFilter("queue_name", "like", this.value);
365
+ updateTableFilters();
275
366
  });
276
367
 
277
368
  // 清除搜索
278
369
  function clearSearch() {
279
370
  document.getElementById("searchInput").value = "";
280
- table.clearFilter();
371
+ updateTableFilters();
281
372
  }
282
373
 
283
374
  // 显示参数的模态框
@@ -581,11 +672,12 @@
581
672
  document.getElementById("chartEndTime").removeAttribute('max');
582
673
  document.getElementById("chartEndTime").setAttribute('min', minStart);
583
674
  endTimeUserChanged = false; // 重置
584
- loadQueueChartData(queueName, Math.floor(start.getTime() / 1000), Math.floor(now.getTime() / 1000));
675
+ loadQueueChartData(queueName, Math.floor(start.getTime() / 1000), Math.floor(now.getTime() / 1000), 360);
585
676
  }
586
677
  function reloadQueueChartWithTimeRange() {
587
678
  const start = document.getElementById("chartStartTime").value;
588
679
  const end = document.getElementById("chartEndTime").value;
680
+ const curveSamplesCount = document.getElementById("curveSamplesCount").value;
589
681
  let start_ts = start ? (new Date(start).getTime() / 1000) : null;
590
682
  let end_ts = end ? (new Date(end).getTime() / 1000) : null;
591
683
  if (endTimeUserChanged) {
@@ -593,9 +685,9 @@
593
685
  } else {
594
686
  console.log('结束时间为默认值:', end);
595
687
  }
596
- loadQueueChartData(currentChartQueueName, start_ts, end_ts);
688
+ loadQueueChartData(currentChartQueueName, start_ts, end_ts, curveSamplesCount);
597
689
  }
598
- function loadQueueChartData(queueName, start_ts, end_ts) {
690
+ function loadQueueChartData(queueName, start_ts, end_ts, curveSamplesCount) {
599
691
  if (chartInstance) chartInstance.destroy();
600
692
  const chartCanvas = document.getElementById('queueDataChart');
601
693
  const ctx = chartCanvas.getContext('2d');
@@ -607,22 +699,62 @@
607
699
  let params = [];
608
700
  if (start_ts) params.push(`start_ts=${start_ts}`);
609
701
  if (end_ts) params.push(`end_ts=${end_ts}`);
702
+ if (curveSamplesCount) params.push(`curve_samples_count=${curveSamplesCount}`);
610
703
  if (params.length > 0) url += '?' + params.join('&');
611
704
  $.get(url, function(response) {
705
+ console.log('AJAX请求成功,返回数据:', response);
706
+ console.log('数据类型:', typeof response, '数据长度:', response ? response.length : 'undefined');
707
+
612
708
  if (!response || response.length === 0) {
709
+ console.log('数据为空,显示暂无数据');
613
710
  ctx.clearRect(0, 0, chartCanvas.width, chartCanvas.height);
614
711
  ctx.fillText("暂无历史数据", chartCanvas.width / 2, chartCanvas.height / 2);
615
712
  $('#chartModal').modal('show');
616
713
  return;
617
714
  }
715
+
716
+ console.log('开始处理', response.length, '个数据点');
618
717
  // 横坐标用本地时间字符串显示
619
718
  const labels = response.map(dp => {
620
719
  const d = new Date(dp.report_ts * 1000);
621
720
  return d.toLocaleString('zh-CN', { hour12: false });
622
721
  });
722
+ console.log('时间标签生成完成,数量:', labels.length);
723
+ console.log('开始生成数据集,字段映射:', CHARTABLE_FIELDS_MAP);
724
+
725
+ // 在函数作用域中定义dataPointCount,确保在Chart配置中可以访问
726
+ const dataPointCount = response.length;
727
+ console.log('数据点数量:', dataPointCount);
728
+
623
729
  const datasets = Object.keys(CHARTABLE_FIELDS_MAP).map((fieldKey, index) => {
624
730
  const displayName = CHARTABLE_FIELDS_MAP[fieldKey];
625
731
  const isDefaultVisible = fieldKey === 'all_consumers_last_x_s_execute_count' || fieldKey === 'all_consumers_last_x_s_execute_count_fail';
732
+
733
+ // 根据数据点数量动态调整配置 - 优化平滑度
734
+ let pointRadius, tension, borderWidth;
735
+
736
+ if (dataPointCount > 2000) {
737
+ // 超大量数据点:隐藏点,高张力,细线条
738
+ pointRadius = 0;
739
+ tension = 0.7;
740
+ borderWidth = 1.2;
741
+ } else if (dataPointCount > 800) {
742
+ // 大量数据点:隐藏点,较高张力,较细线条
743
+ pointRadius = 0;
744
+ tension = 0.65;
745
+ borderWidth = 1.5;
746
+ } else if (dataPointCount > 300) {
747
+ // 中等数据点:小点,中高张力 - 针对360等常用采样点数优化
748
+ pointRadius = 0.3;
749
+ tension = 0.6;
750
+ borderWidth = 1.8;
751
+ } else {
752
+ // 少量数据点:正常配置,增加平滑度
753
+ pointRadius = 1;
754
+ tension = 0.5;
755
+ borderWidth = 2;
756
+ }
757
+
626
758
  return {
627
759
  label: displayName,
628
760
  data: response.map(dp => {
@@ -633,41 +765,109 @@
633
765
  fill: false,
634
766
  borderColor: PREDEFINED_COLORS[index % PREDEFINED_COLORS.length],
635
767
  backgroundColor: PREDEFINED_COLORS[index % PREDEFINED_COLORS.length] + '33',
636
- tension: 0.4,
637
- pointRadius: 1,
638
- pointHoverRadius: 3,
639
- borderWidth: 2,
640
- hidden: !isDefaultVisible
768
+ tension: tension,
769
+ pointRadius: pointRadius,
770
+ pointHoverRadius: Math.max(3, pointRadius + 2),
771
+ borderWidth: borderWidth,
772
+ hidden: !isDefaultVisible,
773
+ cubicInterpolationMode: 'monotone', // 单调插值,更平滑
774
+ spanGaps: true, // 跨越空值
775
+ segment: {
776
+ borderColor: ctx => dataPointCount > 300 ?
777
+ (ctx.p0.parsed.y == null || ctx.p1.parsed.y == null ? 'transparent' : undefined) : undefined
778
+ } // 优化大数据量时的线段渲染
641
779
  };
642
780
  });
643
- chartInstance = new Chart(ctx, {
644
- type: 'line',
645
- data: { labels, datasets },
646
- options: {
647
- responsive: true,
648
- maintainAspectRatio: false,
649
- scales: {
650
- x: {
651
- title: { display: true, text: '时间' },
652
- ticks: {
653
- autoSkip: true,
654
- maxTicksLimit: 20
781
+ console.log('数据集生成完成,数量:', datasets.length);
782
+ console.log('开始创建Chart实例...');
783
+ try {
784
+ chartInstance = new Chart(ctx, {
785
+ type: 'line',
786
+ data: { labels, datasets },
787
+ options: {
788
+ responsive: true,
789
+ maintainAspectRatio: false,
790
+ // 性能优化设置
791
+ animation: {
792
+ duration: dataPointCount > 800 ? 0 : 1000, // 数据点多时禁用动画
793
+ easing: 'easeInOutQuart' // 使用更平滑的缓动函数
794
+ },
795
+ // 启用数据集动画以获得更平滑的效果
796
+ datasets: {
797
+ line: {
798
+ pointHoverBackgroundColor: 'rgba(255,255,255,0.8)',
799
+ pointHoverBorderColor: 'rgba(220,220,220,1)',
800
+ pointHoverBorderWidth: 2
655
801
  }
656
802
  },
657
- y: { beginAtZero: false, title: { display: true, text: '数值' } }
658
- },
659
- plugins: {
660
- legend: { position: 'top' },
661
- title: { display: true, text: `队列 [${queueName}] 各项指标变化趋势` },
662
- tooltip: { mode: 'index', intersect: false }
663
- },
664
- interaction: { mode: 'nearest', axis: 'x', intersect: false }
665
- }
666
- });
803
+ scales: {
804
+ x: {
805
+ title: { display: true, text: '时间' },
806
+ ticks: {
807
+ autoSkip: true,
808
+ maxTicksLimit: Math.min(20, Math.max(10, Math.floor(dataPointCount / 50))) // 根据数据点数量动态调整
809
+ }
810
+ },
811
+ y: {
812
+ beginAtZero: false,
813
+ title: { display: true, text: '数值' },
814
+ // 为大量数据点添加性能优化
815
+ ticks: {
816
+ maxTicksLimit: 10
817
+ }
818
+ }
819
+ },
820
+ plugins: {
821
+ legend: { position: 'top' },
822
+ title: {
823
+ display: true,
824
+ text: `队列 [${queueName}] 各项指标变化趋势 (${dataPointCount}个数据点)`
825
+ },
826
+ tooltip: {
827
+ mode: 'index',
828
+ intersect: false
829
+ }
830
+ },
831
+ interaction: {
832
+ mode: 'nearest',
833
+ axis: 'x',
834
+ intersect: false
835
+ },
836
+ // 大量数据点时的额外优化
837
+ elements: {
838
+ line: {
839
+ borderJoinStyle: 'round', // 更平滑的线条连接
840
+ borderCapStyle: 'round', // 圆形端点
841
+ fill: false
842
+ },
843
+ point: {
844
+ hoverRadius: dataPointCount > 800 ? 2 : 4, // 根据数据量调整悬停半径
845
+ hitRadius: dataPointCount > 800 ? 3 : 6 // 根据数据量调整点击半径
846
+ }
847
+ },
848
+ // 针对大数据量的优化配置
849
+ parsing: dataPointCount > 1000 ? {
850
+ xAxisKey: false, // 禁用x轴解析以提高性能
851
+ yAxisKey: false // 禁用y轴解析以提高性能
852
+ } : undefined
853
+ }
854
+ });
855
+ console.log('Chart created successfully with', dataPointCount, 'data points');
856
+ } catch (error) {
857
+ console.error('Error creating chart:', error);
858
+ ctx.clearRect(0, 0, chartCanvas.width, chartCanvas.height);
859
+ ctx.fillText("图表创建失败: " + error.message, chartCanvas.width / 2, chartCanvas.height / 2);
860
+ }
667
861
  $('#chartModal').modal('show');
668
- }).fail(function() {
862
+ }).fail(function(xhr, status, error) {
863
+ console.error('AJAX请求失败:', {
864
+ status: status,
865
+ error: error,
866
+ responseText: xhr.responseText,
867
+ url: url
868
+ });
669
869
  ctx.clearRect(0, 0, chartCanvas.width, chartCanvas.height);
670
- ctx.fillText("获取数据失败", chartCanvas.width / 2, chartCanvas.height / 2);
870
+ ctx.fillText("获取数据失败: " + error, chartCanvas.width / 2, chartCanvas.height / 2);
671
871
  $('#chartModal').modal('show');
672
872
  });
673
873
  }
@@ -694,6 +894,17 @@
694
894
 
695
895
  // 绑定说明按钮的点击事件
696
896
  document.getElementById('show-explanation-btn').addEventListener('click', showExplanationModal);
897
+
898
+ // 新增:处理复选框状态变化的函数
899
+ function toggleActiveQueuesFilter() {
900
+ updateTableFilters();
901
+ }
902
+
903
+ // 初始化时设置默认过滤条件
904
+ document.addEventListener('DOMContentLoaded', function() {
905
+ updateTableFilters();
906
+ });
907
+
697
908
  </script>
698
909
  </body>
699
910
  </html>
@@ -703,7 +914,6 @@
703
914
  队列名字 broker_kind 消息数量 consumer数量 消费者参数 是否暂停消费状态 操作(这一列都是按钮)
704
915
  获取消息数量、清空队列消息、 暂停消费 、恢复消费
705
916
 
706
-
707
917
 
708
918
  接口 /queue/params_and_active_consumers 返回的是如下字典,字典中的key是队列名字,
709
919
  value是一个字典,字典中有两个key,一个是active_consumers,一个是 queue_params ,
@@ -848,5 +1058,3 @@
848
1058
  }
849
1059
  }
850
1060
  }
851
-
852
- -->
@@ -21,6 +21,7 @@ from threading import Lock
21
21
  import nb_log
22
22
  from funboost.constant import ConstStrForClassMethod, FunctionKind
23
23
  from funboost.core.func_params_model import PublisherParams, PriorityConsumingControlConfig
24
+ from funboost.core.function_result_status_saver import FunctionResultStatus
24
25
  from funboost.core.helper_funs import MsgGenerater
25
26
  from funboost.core.loggers import develop_logger
26
27
 
@@ -315,6 +316,10 @@ class AbstractPublisher(LoggerLevelSetterMixin, metaclass=abc.ABCMeta, ):
315
316
  def concrete_realization_of_publish(self, msg: str):
316
317
  raise NotImplementedError
317
318
 
319
+ def sync_call(self, msg_dict: dict, is_return_rpc_data_obj=True) -> typing.Union[dict, FunctionResultStatus]:
320
+ """仅有部分中间件支持同步调用并阻塞等待返回结果,不依赖AsyncResult + redis作为rpc,例如 http grpc 等"""
321
+ raise NotImplementedError(f'broker {self.publisher_params.broker_kind} not support sync_call method')
322
+
318
323
  @abc.abstractmethod
319
324
  def clear(self):
320
325
  raise NotImplementedError
@@ -1,7 +1,10 @@
1
1
  # -*- coding: utf-8 -*-
2
2
  # @Author : ydf
3
3
  # @Time : 2022/8/8 0008 12:12
4
+ import threading
4
5
 
6
+ from funboost.core.function_result_status_saver import FunctionResultStatus
7
+ from funboost.core.serialization import Serialization
5
8
  from funboost.publishers.base_publisher import AbstractPublisher
6
9
  from urllib3 import PoolManager
7
10
 
@@ -13,7 +16,7 @@ class HTTPPublisher(AbstractPublisher, ):
13
16
 
14
17
  # noinspection PyAttributeOutsideInit
15
18
  def custom_init(self):
16
- self._http = PoolManager(10)
19
+ self._http = PoolManager(maxsize=100)
17
20
  self._ip = self.publisher_params.broker_exclusive_config['host']
18
21
  self._port = self.publisher_params.broker_exclusive_config['port']
19
22
  self._ip_port_str = f'{self._ip}:{self._port}'
@@ -23,7 +26,22 @@ class HTTPPublisher(AbstractPublisher, ):
23
26
 
24
27
  def concrete_realization_of_publish(self, msg):
25
28
  url = self._ip_port_str + '/queue'
26
- self._http.request('post', url, fields={'msg': msg})
29
+ self._http.request('post', url, fields={'msg': msg,'call_type':'publish'})
30
+
31
+ def sync_call(self, msg_dict: dict, is_return_rpc_data_obj=True):
32
+ url = self._ip_port_str + '/queue'
33
+ response = self._http.request('post', url,
34
+ fields={'msg': Serialization.to_json_str(msg_dict),'call_type':'sync_call'})
35
+ json_resp = response.data.decode('utf-8')
36
+ # import requests
37
+ # response = requests.request('post', url,
38
+ # data={'msg': Serialization.to_json_str(msg_dict),'call_type':'sync_call'})
39
+ # json_resp = response.text()
40
+ if is_return_rpc_data_obj:
41
+ return FunctionResultStatus.parse_status_and_result_to_obj(Serialization.to_dict(json_resp))
42
+ else:
43
+ return Serialization.to_dict(json_resp)
44
+
27
45
 
28
46
  def clear(self):
29
47
  pass # udp没有保存消息