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