codosdk 1.0.42__tar.gz → 1.0.44__tar.gz
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.
- {codosdk-1.0.42 → codosdk-1.0.44}/PKG-INFO +1 -1
- {codosdk-1.0.42 → codosdk-1.0.44}/codosdk.egg-info/PKG-INFO +1 -1
- {codosdk-1.0.42 → codosdk-1.0.44}/setup.py +1 -1
- {codosdk-1.0.42 → codosdk-1.0.44}/websdk2/mqhelper.py +90 -53
- {codosdk-1.0.42 → codosdk-1.0.44}/LICENSE +0 -0
- {codosdk-1.0.42 → codosdk-1.0.44}/README.md +0 -0
- {codosdk-1.0.42 → codosdk-1.0.44}/codosdk.egg-info/SOURCES.txt +0 -0
- {codosdk-1.0.42 → codosdk-1.0.44}/codosdk.egg-info/dependency_links.txt +0 -0
- {codosdk-1.0.42 → codosdk-1.0.44}/codosdk.egg-info/requires.txt +0 -0
- {codosdk-1.0.42 → codosdk-1.0.44}/codosdk.egg-info/top_level.txt +0 -0
- {codosdk-1.0.42 → codosdk-1.0.44}/opssdk/__init__.py +0 -0
- {codosdk-1.0.42 → codosdk-1.0.44}/opssdk/utils/__init__.py +0 -0
- {codosdk-1.0.42 → codosdk-1.0.44}/pyproject.toml +0 -0
- {codosdk-1.0.42 → codosdk-1.0.44}/setup.cfg +0 -0
- {codosdk-1.0.42 → codosdk-1.0.44}/websdk2/__init__.py +0 -0
- {codosdk-1.0.42 → codosdk-1.0.44}/websdk2/api_set.py +0 -0
- {codosdk-1.0.42 → codosdk-1.0.44}/websdk2/apis/__init__.py +0 -0
- {codosdk-1.0.42 → codosdk-1.0.44}/websdk2/apis/admin_apis.py +0 -0
- {codosdk-1.0.42 → codosdk-1.0.44}/websdk2/apis/agent_apis.py +0 -0
- {codosdk-1.0.42 → codosdk-1.0.44}/websdk2/apis/cmdb_apis.py +0 -0
- {codosdk-1.0.42 → codosdk-1.0.44}/websdk2/apis/kerrigan_apis.py +0 -0
- {codosdk-1.0.42 → codosdk-1.0.44}/websdk2/apis/mgv4_apis.py +0 -0
- {codosdk-1.0.42 → codosdk-1.0.44}/websdk2/apis/notice_apis.py +0 -0
- {codosdk-1.0.42 → codosdk-1.0.44}/websdk2/apis/task_apis.py +0 -0
- {codosdk-1.0.42 → codosdk-1.0.44}/websdk2/application.py +0 -0
- {codosdk-1.0.42 → codosdk-1.0.44}/websdk2/base_handler.py +0 -0
- {codosdk-1.0.42 → codosdk-1.0.44}/websdk2/cache.py +0 -0
- {codosdk-1.0.42 → codosdk-1.0.44}/websdk2/cache_context.py +0 -0
- {codosdk-1.0.42 → codosdk-1.0.44}/websdk2/client.py +0 -0
- {codosdk-1.0.42 → codosdk-1.0.44}/websdk2/cloud/__init__.py +0 -0
- {codosdk-1.0.42 → codosdk-1.0.44}/websdk2/cloud/qcloud_api.py +0 -0
- {codosdk-1.0.42 → codosdk-1.0.44}/websdk2/cloud/ucloud_api.py +0 -0
- {codosdk-1.0.42 → codosdk-1.0.44}/websdk2/cloud_utils.py +0 -0
- {codosdk-1.0.42 → codosdk-1.0.44}/websdk2/configs.py +0 -0
- {codosdk-1.0.42 → codosdk-1.0.44}/websdk2/consts.py +0 -0
- {codosdk-1.0.42 → codosdk-1.0.44}/websdk2/crud_utils.py +0 -0
- {codosdk-1.0.42 → codosdk-1.0.44}/websdk2/db_context.py +0 -0
- {codosdk-1.0.42 → codosdk-1.0.44}/websdk2/error.py +0 -0
- {codosdk-1.0.42 → codosdk-1.0.44}/websdk2/fetch_coroutine.py +0 -0
- {codosdk-1.0.42 → codosdk-1.0.44}/websdk2/jwt_token.py +0 -0
- {codosdk-1.0.42 → codosdk-1.0.44}/websdk2/ldap.py +0 -0
- {codosdk-1.0.42 → codosdk-1.0.44}/websdk2/logger.py +0 -0
- {codosdk-1.0.42 → codosdk-1.0.44}/websdk2/model_utils.py +0 -0
- {codosdk-1.0.42 → codosdk-1.0.44}/websdk2/program.py +0 -0
- {codosdk-1.0.42 → codosdk-1.0.44}/websdk2/salt_api.py +0 -0
- {codosdk-1.0.42 → codosdk-1.0.44}/websdk2/sqlalchemy_pagination.py +0 -0
- {codosdk-1.0.42 → codosdk-1.0.44}/websdk2/tools.py +0 -0
- {codosdk-1.0.42 → codosdk-1.0.44}/websdk2/utils/__init__.py +0 -0
- {codosdk-1.0.42 → codosdk-1.0.44}/websdk2/utils/cc_crypto.py +0 -0
- {codosdk-1.0.42 → codosdk-1.0.44}/websdk2/utils/date_format.py +0 -0
- {codosdk-1.0.42 → codosdk-1.0.44}/websdk2/utils/pydantic_utils.py +0 -0
- {codosdk-1.0.42 → codosdk-1.0.44}/websdk2/web_logs.py +0 -0
|
@@ -9,7 +9,7 @@ desc : CODO SDK
|
|
|
9
9
|
import sys
|
|
10
10
|
from setuptools import setup, find_packages
|
|
11
11
|
|
|
12
|
-
VERSION = '1.0.
|
|
12
|
+
VERSION = '1.0.44'
|
|
13
13
|
|
|
14
14
|
if sys.version_info < (2, 7) or (3, 0) <= sys.version_info < (3, 6):
|
|
15
15
|
print('This program requires at least Python 2.7 or 3.6 to run.')
|
|
@@ -9,6 +9,7 @@ role : rabbitMQ 操作类
|
|
|
9
9
|
import logging
|
|
10
10
|
import traceback
|
|
11
11
|
import pika
|
|
12
|
+
import time
|
|
12
13
|
import threading
|
|
13
14
|
from typing import Optional, List, Dict, Any, Callable
|
|
14
15
|
from .consts import const
|
|
@@ -120,7 +121,6 @@ logger.setLevel(logging.WARNING)
|
|
|
120
121
|
# else:
|
|
121
122
|
# self.__channel.basic_publish(exchange=self.__exchange, routing_key=self.__routing_key, body=body)
|
|
122
123
|
# logging.info('Publish message %s sucessfuled.' % body)
|
|
123
|
-
|
|
124
124
|
class RabbitMQConnectionPool:
|
|
125
125
|
"""RabbitMQ连接池管理器 - 实现一个应用一个连接的最佳实践"""
|
|
126
126
|
|
|
@@ -165,13 +165,7 @@ class RabbitMQConnectionPool:
|
|
|
165
165
|
socket_timeout=30, # 添加socket超时
|
|
166
166
|
connection_attempts=3, # 连接重试次数
|
|
167
167
|
retry_delay=1, # 重试延迟
|
|
168
|
-
stack_timeout=30
|
|
169
|
-
# 添加TCP连接参数 某些系统上可能不被支持 删除
|
|
170
|
-
# tcp_options={
|
|
171
|
-
# 'TCP_KEEPIDLE': 60,
|
|
172
|
-
# 'TCP_KEEPINTVL': 30,
|
|
173
|
-
# 'TCP_KEEPCNT': 3
|
|
174
|
-
# }
|
|
168
|
+
stack_timeout=30 # 栈超时
|
|
175
169
|
)
|
|
176
170
|
|
|
177
171
|
connection = pika.BlockingConnection(connection_params)
|
|
@@ -270,52 +264,87 @@ class MessageQueueBase:
|
|
|
270
264
|
f"you must provide a queue_name to avoid anonymous queues."
|
|
271
265
|
)
|
|
272
266
|
|
|
273
|
-
# def _get_channel(self) -> Any:
|
|
274
|
-
# """获取新的通道"""
|
|
275
|
-
# connection = RabbitMQConnectionPool.get_connection(self._mq_key)
|
|
276
|
-
# return connection.channel()
|
|
277
|
-
|
|
278
267
|
def _get_channel(self) -> Any:
|
|
279
|
-
"""获取新的通道"""
|
|
280
|
-
max_attempts =
|
|
268
|
+
"""获取新的通道 - 使用更安全的方式避免pika并发Bug"""
|
|
269
|
+
max_attempts = 2 # 减少重试次数避免过度重试
|
|
270
|
+
|
|
281
271
|
for attempt in range(max_attempts):
|
|
282
272
|
try:
|
|
273
|
+
# 每次都创建新连接,避免连接复用导致的并发问题
|
|
274
|
+
if attempt > 0:
|
|
275
|
+
# 强制清理并等待
|
|
276
|
+
with RabbitMQConnectionPool._connection_lock:
|
|
277
|
+
try:
|
|
278
|
+
if self._mq_key in RabbitMQConnectionPool._connections:
|
|
279
|
+
RabbitMQConnectionPool._cleanup_connection(self._mq_key)
|
|
280
|
+
RabbitMQConnectionPool._connections[self._mq_key] = None
|
|
281
|
+
except:
|
|
282
|
+
pass
|
|
283
|
+
|
|
284
|
+
# 等待稍长时间让连接完全清理
|
|
285
|
+
time.sleep(0.5)
|
|
286
|
+
|
|
283
287
|
connection = RabbitMQConnectionPool.get_connection(self._mq_key)
|
|
284
|
-
if not connection
|
|
285
|
-
raise Exception("
|
|
288
|
+
if not connection:
|
|
289
|
+
raise Exception("Failed to get connection")
|
|
286
290
|
|
|
287
|
-
#
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
connection.process_data_events(time_limit=0)
|
|
291
|
-
except:
|
|
292
|
-
raise Exception("Connection heartbeat failed")
|
|
291
|
+
# 简化状态检查,避免触发pika内部Bug
|
|
292
|
+
if hasattr(connection, 'is_open') and not connection.is_open:
|
|
293
|
+
raise Exception("Connection is not open")
|
|
293
294
|
|
|
295
|
+
# 直接创建channel,不做额外的心跳检查
|
|
294
296
|
channel = connection.channel()
|
|
295
|
-
if not channel
|
|
296
|
-
raise Exception("
|
|
297
|
+
if not channel:
|
|
298
|
+
raise Exception("Failed to create channel")
|
|
299
|
+
|
|
300
|
+
if hasattr(channel, 'is_open') and not channel.is_open:
|
|
301
|
+
raise Exception("Channel is not open")
|
|
302
|
+
|
|
297
303
|
return channel
|
|
298
304
|
|
|
299
305
|
except Exception as e:
|
|
300
|
-
self.logger.warning(f"Failed to get
|
|
301
|
-
if attempt
|
|
302
|
-
#
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
306
|
+
self.logger.warning(f"Failed to get channel (attempt {attempt + 1}/{max_attempts}): {e}")
|
|
307
|
+
if attempt == max_attempts - 1:
|
|
308
|
+
# 最后一次失败,创建一个临时连接
|
|
309
|
+
try:
|
|
310
|
+
return self._create_emergency_channel()
|
|
311
|
+
except Exception as emergency_e:
|
|
312
|
+
raise Exception(f"All channel attempts failed. Last error: {e}, Emergency error: {emergency_e}")
|
|
313
|
+
|
|
314
|
+
raise Exception("Unexpected error in channel creation")
|
|
315
|
+
|
|
316
|
+
def _create_emergency_channel(self) -> Any:
|
|
317
|
+
"""创建紧急通道 - 直接连接,不使用连接池"""
|
|
318
|
+
try:
|
|
319
|
+
mq_config = configs[const.MQ_CONFIG_ITEM][self._mq_key]
|
|
320
|
+
|
|
321
|
+
credentials = pika.PlainCredentials(
|
|
322
|
+
mq_config[const.MQ_USER],
|
|
323
|
+
mq_config[const.MQ_PWD]
|
|
324
|
+
)
|
|
325
|
+
|
|
326
|
+
# 使用最简单的连接参数
|
|
327
|
+
connection_params = pika.ConnectionParameters(
|
|
328
|
+
host=mq_config[const.MQ_ADDR],
|
|
329
|
+
port=int(mq_config[const.MQ_PORT]),
|
|
330
|
+
virtual_host=mq_config[const.MQ_VHOST],
|
|
331
|
+
credentials=credentials,
|
|
332
|
+
heartbeat=0, # 禁用心跳
|
|
333
|
+
socket_timeout=10
|
|
334
|
+
)
|
|
335
|
+
|
|
336
|
+
connection = pika.BlockingConnection(connection_params)
|
|
337
|
+
channel = connection.channel()
|
|
338
|
+
|
|
339
|
+
self.logger.info("Created emergency channel")
|
|
340
|
+
return channel
|
|
341
|
+
|
|
342
|
+
except Exception as e:
|
|
343
|
+
self.logger.error(f"Failed to create emergency channel: {e}")
|
|
344
|
+
raise
|
|
316
345
|
|
|
317
346
|
def _with_retry(self, operation: Callable, *args, **kwargs) -> Any:
|
|
318
|
-
"""带重试机制执行操作"""
|
|
347
|
+
"""带重试机制执行操作 - 优化版本"""
|
|
319
348
|
last_exception = None
|
|
320
349
|
|
|
321
350
|
for attempt in range(self._max_retries + 1):
|
|
@@ -333,7 +362,7 @@ class MessageQueueBase:
|
|
|
333
362
|
error_str = str(e).lower()
|
|
334
363
|
if any(keyword in error_str for keyword in [
|
|
335
364
|
'connection', 'channel', 'socket', 'transport',
|
|
336
|
-
'callback', 'deque', 'abort', 'closed'
|
|
365
|
+
'callback', 'deque', 'abort', 'closed', 'asynctransport'
|
|
337
366
|
]):
|
|
338
367
|
# 安全地清理连接
|
|
339
368
|
with RabbitMQConnectionPool._connection_lock:
|
|
@@ -344,9 +373,8 @@ class MessageQueueBase:
|
|
|
344
373
|
except Exception as cleanup_e:
|
|
345
374
|
self.logger.debug(f"Error during connection cleanup: {cleanup_e}")
|
|
346
375
|
|
|
347
|
-
#
|
|
348
|
-
|
|
349
|
-
time.sleep(0.1 * (2 ** attempt))
|
|
376
|
+
# 递增延迟重试,但不要太长
|
|
377
|
+
time.sleep(min(0.1 * (2 ** attempt), 2.0))
|
|
350
378
|
|
|
351
379
|
raise last_exception
|
|
352
380
|
|
|
@@ -374,7 +402,7 @@ class MessageQueueBase:
|
|
|
374
402
|
|
|
375
403
|
# 如果有队列名,声明并绑定队列
|
|
376
404
|
if self._queue_name:
|
|
377
|
-
channel.queue_declare(queue=self._queue_name)
|
|
405
|
+
channel.queue_declare(queue=self._queue_name, durable=True)
|
|
378
406
|
channel.queue_bind(
|
|
379
407
|
exchange=self._exchange,
|
|
380
408
|
queue=self._queue_name,
|
|
@@ -394,8 +422,11 @@ class MessageQueueBase:
|
|
|
394
422
|
f'Published message to exchange:{self._exchange}, routing_key:{actual_routing_key}')
|
|
395
423
|
|
|
396
424
|
finally:
|
|
397
|
-
if channel and channel.is_open:
|
|
398
|
-
|
|
425
|
+
if channel and hasattr(channel, 'is_open') and channel.is_open:
|
|
426
|
+
try:
|
|
427
|
+
channel.close()
|
|
428
|
+
except:
|
|
429
|
+
pass # 忽略关闭时的错误
|
|
399
430
|
|
|
400
431
|
self._with_retry(_publish_operation)
|
|
401
432
|
|
|
@@ -445,11 +476,17 @@ class MessageQueueBase:
|
|
|
445
476
|
|
|
446
477
|
except KeyboardInterrupt:
|
|
447
478
|
self.logger.info("Received interrupt signal, stopping consumption")
|
|
448
|
-
if channel and channel.is_open:
|
|
449
|
-
|
|
479
|
+
if channel and hasattr(channel, 'is_open') and channel.is_open:
|
|
480
|
+
try:
|
|
481
|
+
channel.stop_consuming()
|
|
482
|
+
except:
|
|
483
|
+
pass
|
|
450
484
|
finally:
|
|
451
|
-
if channel and channel.is_open:
|
|
452
|
-
|
|
485
|
+
if channel and hasattr(channel, 'is_open') and channel.is_open:
|
|
486
|
+
try:
|
|
487
|
+
channel.close()
|
|
488
|
+
except:
|
|
489
|
+
pass
|
|
453
490
|
|
|
454
491
|
self._with_retry(_consume_operation)
|
|
455
492
|
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|