crawlo 1.3.6__py3-none-any.whl → 1.3.7__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 crawlo might be problematic. Click here for more details.
- crawlo/__init__.py +87 -87
- crawlo/__version__.py +1 -1
- crawlo/cli.py +75 -75
- crawlo/commands/__init__.py +14 -14
- crawlo/commands/check.py +594 -594
- crawlo/commands/genspider.py +151 -151
- crawlo/commands/help.py +138 -138
- crawlo/commands/list.py +155 -155
- crawlo/commands/run.py +341 -341
- crawlo/commands/startproject.py +436 -436
- crawlo/commands/stats.py +187 -187
- crawlo/commands/utils.py +196 -196
- crawlo/config.py +312 -312
- crawlo/config_validator.py +277 -277
- crawlo/core/__init__.py +45 -45
- crawlo/core/engine.py +439 -439
- crawlo/core/processor.py +40 -40
- crawlo/core/scheduler.py +257 -257
- crawlo/crawler.py +638 -638
- crawlo/data/__init__.py +5 -5
- crawlo/data/user_agents.py +194 -194
- crawlo/downloader/__init__.py +273 -273
- crawlo/downloader/aiohttp_downloader.py +228 -228
- crawlo/downloader/cffi_downloader.py +245 -245
- crawlo/downloader/httpx_downloader.py +259 -259
- crawlo/downloader/hybrid_downloader.py +212 -212
- crawlo/downloader/playwright_downloader.py +402 -402
- crawlo/downloader/selenium_downloader.py +472 -472
- crawlo/event.py +11 -11
- crawlo/exceptions.py +81 -81
- crawlo/extension/__init__.py +39 -39
- crawlo/extension/health_check.py +141 -141
- crawlo/extension/log_interval.py +57 -57
- crawlo/extension/log_stats.py +81 -81
- crawlo/extension/logging_extension.py +61 -61
- crawlo/extension/memory_monitor.py +104 -104
- crawlo/extension/performance_profiler.py +133 -133
- crawlo/extension/request_recorder.py +107 -107
- crawlo/factories/__init__.py +27 -27
- crawlo/factories/base.py +68 -68
- crawlo/factories/crawler.py +103 -103
- crawlo/factories/registry.py +84 -84
- crawlo/filters/__init__.py +154 -154
- crawlo/filters/aioredis_filter.py +257 -257
- crawlo/filters/memory_filter.py +269 -269
- crawlo/framework.py +292 -292
- crawlo/initialization/__init__.py +39 -39
- crawlo/initialization/built_in.py +425 -425
- crawlo/initialization/context.py +141 -141
- crawlo/initialization/core.py +193 -193
- crawlo/initialization/phases.py +148 -148
- crawlo/initialization/registry.py +145 -145
- crawlo/items/__init__.py +23 -23
- crawlo/items/base.py +23 -23
- crawlo/items/fields.py +52 -52
- crawlo/items/items.py +104 -104
- crawlo/logging/__init__.py +37 -37
- crawlo/logging/config.py +96 -96
- crawlo/logging/factory.py +128 -128
- crawlo/logging/manager.py +111 -111
- crawlo/middleware/__init__.py +21 -21
- crawlo/middleware/default_header.py +132 -132
- crawlo/middleware/download_delay.py +104 -104
- crawlo/middleware/middleware_manager.py +135 -135
- crawlo/middleware/offsite.py +123 -123
- crawlo/middleware/proxy.py +386 -386
- crawlo/middleware/request_ignore.py +86 -86
- crawlo/middleware/response_code.py +163 -163
- crawlo/middleware/response_filter.py +136 -136
- crawlo/middleware/retry.py +124 -124
- crawlo/middleware/simple_proxy.py +65 -65
- crawlo/mode_manager.py +212 -212
- crawlo/network/__init__.py +21 -21
- crawlo/network/request.py +379 -379
- crawlo/network/response.py +359 -359
- crawlo/pipelines/__init__.py +21 -21
- crawlo/pipelines/bloom_dedup_pipeline.py +156 -156
- crawlo/pipelines/console_pipeline.py +39 -39
- crawlo/pipelines/csv_pipeline.py +316 -316
- crawlo/pipelines/database_dedup_pipeline.py +222 -222
- crawlo/pipelines/json_pipeline.py +218 -218
- crawlo/pipelines/memory_dedup_pipeline.py +115 -115
- crawlo/pipelines/mongo_pipeline.py +131 -131
- crawlo/pipelines/mysql_pipeline.py +325 -325
- crawlo/pipelines/pipeline_manager.py +76 -76
- crawlo/pipelines/redis_dedup_pipeline.py +166 -166
- crawlo/project.py +327 -327
- crawlo/queue/pqueue.py +42 -42
- crawlo/queue/queue_manager.py +522 -503
- crawlo/queue/redis_priority_queue.py +367 -326
- crawlo/settings/__init__.py +7 -7
- crawlo/settings/default_settings.py +321 -321
- crawlo/settings/setting_manager.py +214 -214
- crawlo/spider/__init__.py +657 -657
- crawlo/stats_collector.py +73 -73
- crawlo/subscriber.py +129 -129
- crawlo/task_manager.py +138 -138
- crawlo/templates/crawlo.cfg.tmpl +10 -10
- crawlo/templates/project/__init__.py.tmpl +3 -3
- crawlo/templates/project/items.py.tmpl +17 -17
- crawlo/templates/project/middlewares.py.tmpl +118 -118
- crawlo/templates/project/pipelines.py.tmpl +96 -96
- crawlo/templates/project/settings.py.tmpl +170 -167
- crawlo/templates/project/settings_distributed.py.tmpl +169 -166
- crawlo/templates/project/settings_gentle.py.tmpl +166 -166
- crawlo/templates/project/settings_high_performance.py.tmpl +167 -167
- crawlo/templates/project/settings_minimal.py.tmpl +65 -65
- crawlo/templates/project/settings_simple.py.tmpl +164 -164
- crawlo/templates/project/spiders/__init__.py.tmpl +9 -9
- crawlo/templates/run.py.tmpl +34 -34
- crawlo/templates/spider/spider.py.tmpl +143 -143
- crawlo/templates/spiders_init.py.tmpl +9 -9
- crawlo/tools/__init__.py +200 -200
- crawlo/tools/anti_crawler.py +268 -268
- crawlo/tools/authenticated_proxy.py +240 -240
- crawlo/tools/data_formatter.py +225 -225
- crawlo/tools/data_validator.py +180 -180
- crawlo/tools/date_tools.py +289 -289
- crawlo/tools/distributed_coordinator.py +388 -388
- crawlo/tools/encoding_converter.py +127 -127
- crawlo/tools/network_diagnostic.py +364 -364
- crawlo/tools/request_tools.py +82 -82
- crawlo/tools/retry_mechanism.py +224 -224
- crawlo/tools/scenario_adapter.py +262 -262
- crawlo/tools/text_cleaner.py +232 -232
- crawlo/utils/__init__.py +34 -34
- crawlo/utils/batch_processor.py +259 -259
- crawlo/utils/class_loader.py +25 -25
- crawlo/utils/controlled_spider_mixin.py +439 -439
- crawlo/utils/db_helper.py +343 -343
- crawlo/utils/enhanced_error_handler.py +356 -356
- crawlo/utils/env_config.py +142 -142
- crawlo/utils/error_handler.py +165 -165
- crawlo/utils/func_tools.py +82 -82
- crawlo/utils/large_scale_config.py +286 -286
- crawlo/utils/large_scale_helper.py +344 -344
- crawlo/utils/log.py +79 -79
- crawlo/utils/performance_monitor.py +285 -285
- crawlo/utils/queue_helper.py +175 -175
- crawlo/utils/redis_connection_pool.py +388 -388
- crawlo/utils/redis_key_validator.py +198 -198
- crawlo/utils/request.py +267 -267
- crawlo/utils/request_serializer.py +225 -225
- crawlo/utils/spider_loader.py +61 -61
- crawlo/utils/system.py +11 -11
- crawlo/utils/tools.py +4 -4
- crawlo/utils/url.py +39 -39
- {crawlo-1.3.6.dist-info → crawlo-1.3.7.dist-info}/METADATA +1199 -1126
- crawlo-1.3.7.dist-info/RECORD +292 -0
- examples/__init__.py +7 -7
- tests/__init__.py +7 -7
- tests/advanced_tools_example.py +275 -275
- tests/authenticated_proxy_example.py +106 -106
- tests/baidu_performance_test.py +108 -108
- tests/baidu_test.py +59 -59
- tests/cleaners_example.py +160 -160
- tests/comprehensive_framework_test.py +212 -212
- tests/comprehensive_test.py +81 -81
- tests/comprehensive_testing_summary.md +186 -186
- tests/config_validation_demo.py +142 -142
- tests/controlled_spider_example.py +205 -205
- tests/date_tools_example.py +180 -180
- tests/debug_configure.py +69 -69
- tests/debug_framework_logger.py +84 -84
- tests/debug_log_config.py +126 -126
- tests/debug_log_levels.py +63 -63
- tests/debug_pipelines.py +66 -66
- tests/detailed_log_test.py +233 -233
- tests/distributed_test.py +66 -66
- tests/distributed_test_debug.py +76 -76
- tests/dynamic_loading_example.py +523 -523
- tests/dynamic_loading_test.py +104 -104
- tests/env_config_example.py +133 -133
- tests/error_handling_example.py +171 -171
- tests/final_comprehensive_test.py +151 -151
- tests/final_log_test.py +260 -260
- tests/final_validation_test.py +182 -182
- tests/fix_log_test.py +142 -142
- tests/framework_performance_test.py +202 -202
- tests/log_buffering_test.py +111 -111
- tests/log_generation_timing_test.py +153 -153
- tests/optimized_performance_test.py +211 -211
- tests/performance_comparison.py +245 -245
- tests/queue_blocking_test.py +113 -113
- tests/queue_test.py +89 -89
- tests/redis_key_validation_demo.py +130 -130
- tests/request_params_example.py +150 -150
- tests/response_improvements_example.py +144 -144
- tests/scrapy_comparison/ofweek_scrapy.py +138 -138
- tests/scrapy_comparison/scrapy_test.py +133 -133
- tests/simple_command_test.py +119 -119
- tests/simple_crawlo_test.py +127 -127
- tests/simple_log_test.py +57 -57
- tests/simple_log_test2.py +137 -137
- tests/simple_optimization_test.py +128 -128
- tests/simple_queue_type_test.py +41 -41
- tests/simple_spider_test.py +49 -49
- tests/simple_test.py +47 -47
- tests/spider_log_timing_test.py +177 -177
- tests/test_advanced_tools.py +148 -148
- tests/test_all_commands.py +230 -230
- tests/test_all_redis_key_configs.py +145 -145
- tests/test_authenticated_proxy.py +141 -141
- tests/test_batch_processor.py +178 -178
- tests/test_cleaners.py +54 -54
- tests/test_component_factory.py +174 -174
- tests/test_comprehensive.py +146 -146
- tests/test_config_consistency.py +80 -80
- tests/test_config_merge.py +152 -152
- tests/test_config_validator.py +182 -182
- tests/test_controlled_spider_mixin.py +79 -79
- tests/test_crawlo_proxy_integration.py +108 -108
- tests/test_date_tools.py +123 -123
- tests/test_default_header_middleware.py +158 -158
- tests/test_distributed.py +65 -65
- tests/test_double_crawlo_fix.py +204 -207
- tests/test_double_crawlo_fix_simple.py +124 -124
- tests/test_download_delay_middleware.py +221 -221
- tests/test_downloader_proxy_compatibility.py +268 -268
- tests/test_dynamic_downloaders_proxy.py +124 -124
- tests/test_dynamic_proxy.py +92 -92
- tests/test_dynamic_proxy_config.py +146 -146
- tests/test_dynamic_proxy_real.py +109 -109
- tests/test_edge_cases.py +303 -303
- tests/test_enhanced_error_handler.py +270 -270
- tests/test_enhanced_error_handler_comprehensive.py +245 -245
- tests/test_env_config.py +121 -121
- tests/test_error_handler_compatibility.py +112 -112
- tests/test_factories.py +252 -252
- tests/test_final_validation.py +153 -153
- tests/test_framework_env_usage.py +103 -103
- tests/test_framework_logger.py +66 -66
- tests/test_framework_startup.py +64 -64
- tests/test_get_component_logger.py +83 -83
- tests/test_integration.py +169 -169
- tests/test_item_dedup_redis_key.py +122 -122
- tests/test_large_scale_config.py +112 -112
- tests/test_large_scale_helper.py +235 -235
- tests/test_logging_system.py +282 -282
- tests/test_mode_change.py +72 -72
- tests/test_mode_consistency.py +51 -51
- tests/test_offsite_middleware.py +221 -221
- tests/test_parsel.py +29 -29
- tests/test_performance.py +327 -327
- tests/test_performance_monitor.py +115 -115
- tests/test_proxy_api.py +264 -264
- tests/test_proxy_health_check.py +32 -32
- tests/test_proxy_middleware.py +121 -121
- tests/test_proxy_middleware_enhanced.py +216 -216
- tests/test_proxy_middleware_integration.py +136 -136
- tests/test_proxy_middleware_refactored.py +184 -184
- tests/test_proxy_providers.py +56 -56
- tests/test_proxy_stats.py +19 -19
- tests/test_proxy_strategies.py +59 -59
- tests/test_queue_empty_check.py +41 -41
- tests/test_queue_manager_double_crawlo.py +173 -173
- tests/test_queue_manager_redis_key.py +179 -176
- tests/test_queue_naming.py +155 -0
- tests/test_queue_type.py +106 -106
- tests/test_random_user_agent.py +72 -72
- tests/test_real_scenario_proxy.py +195 -195
- tests/test_redis_config.py +28 -28
- tests/test_redis_connection_pool.py +294 -294
- tests/test_redis_key_naming.py +181 -181
- tests/test_redis_key_validator.py +123 -123
- tests/test_redis_queue.py +224 -224
- tests/test_redis_queue_name_fix.py +176 -0
- tests/test_request_ignore_middleware.py +182 -182
- tests/test_request_params.py +111 -111
- tests/test_request_serialization.py +70 -70
- tests/test_response_code_middleware.py +349 -349
- tests/test_response_filter_middleware.py +427 -427
- tests/test_response_improvements.py +152 -152
- tests/test_retry_middleware.py +241 -241
- tests/test_scheduler.py +252 -252
- tests/test_scheduler_config_update.py +133 -133
- tests/test_simple_response.py +61 -61
- tests/test_telecom_spider_redis_key.py +205 -205
- tests/test_template_content.py +87 -87
- tests/test_template_redis_key.py +134 -134
- tests/test_tools.py +159 -159
- tests/test_user_agents.py +96 -96
- tests/tools_example.py +260 -260
- tests/untested_features_report.md +138 -138
- tests/verify_debug.py +51 -51
- tests/verify_distributed.py +117 -117
- tests/verify_log_fix.py +111 -111
- crawlo-1.3.6.dist-info/RECORD +0 -290
- {crawlo-1.3.6.dist-info → crawlo-1.3.7.dist-info}/WHEEL +0 -0
- {crawlo-1.3.6.dist-info → crawlo-1.3.7.dist-info}/entry_points.txt +0 -0
- {crawlo-1.3.6.dist-info → crawlo-1.3.7.dist-info}/top_level.txt +0 -0
tests/test_redis_queue.py
CHANGED
|
@@ -1,225 +1,225 @@
|
|
|
1
|
-
#!/usr/bin/env python3
|
|
2
|
-
# -*- coding: utf-8 -*-
|
|
3
|
-
"""
|
|
4
|
-
Redis 分布式队列测试脚本
|
|
5
|
-
用于诊断和修复分布式队列问题
|
|
6
|
-
"""
|
|
7
|
-
import asyncio
|
|
8
|
-
import sys
|
|
9
|
-
import traceback
|
|
10
|
-
import time
|
|
11
|
-
from crawlo.queue.redis_priority_queue import RedisPriorityQueue
|
|
12
|
-
from crawlo.network.request import Request
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
async def test_redis_connection():
|
|
16
|
-
"""测试 Redis 连接"""
|
|
17
|
-
print("🔍 1. 测试 Redis 连接...")
|
|
18
|
-
|
|
19
|
-
# 测试不同的 Redis URL 格式
|
|
20
|
-
test_urls = [
|
|
21
|
-
"redis://localhost:6379/0",
|
|
22
|
-
"redis://:oscar&0503@127.0.0.1:6379/0", # 带密码
|
|
23
|
-
"redis://127.0.0.1:6379/0", # 无密码
|
|
24
|
-
]
|
|
25
|
-
|
|
26
|
-
for redis_url in test_urls:
|
|
27
|
-
try:
|
|
28
|
-
print(f" 尝试连接: {redis_url}")
|
|
29
|
-
queue = RedisPriorityQueue(redis_url=redis_url)
|
|
30
|
-
await queue.connect()
|
|
31
|
-
print(f" 连接成功: {redis_url}")
|
|
32
|
-
await queue.close()
|
|
33
|
-
return redis_url
|
|
34
|
-
except Exception as e:
|
|
35
|
-
print(f" 连接失败: {redis_url} - {e}")
|
|
36
|
-
|
|
37
|
-
raise ConnectionError("所有 Redis URL 都连接失败")
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
async def test_queue_operations(redis_url):
|
|
41
|
-
"""测试队列基本操作"""
|
|
42
|
-
print("🔍 2. 测试队列基本操作...")
|
|
43
|
-
|
|
44
|
-
queue = RedisPriorityQueue(
|
|
45
|
-
redis_url=redis_url,
|
|
46
|
-
queue_name="test:crawlo:requests",
|
|
47
|
-
max_retries=2
|
|
48
|
-
)
|
|
49
|
-
|
|
50
|
-
try:
|
|
51
|
-
await queue.connect()
|
|
52
|
-
|
|
53
|
-
# 测试 put 操作
|
|
54
|
-
test_request = Request(url="https://example.com", priority=5)
|
|
55
|
-
print(f" 📤 插入请求: {test_request.url}")
|
|
56
|
-
|
|
57
|
-
success = await queue.put(test_request, priority=5)
|
|
58
|
-
if success:
|
|
59
|
-
print(" 插入成功")
|
|
60
|
-
else:
|
|
61
|
-
print(" 插入失败")
|
|
62
|
-
return False
|
|
63
|
-
|
|
64
|
-
# 测试队列大小
|
|
65
|
-
size = await queue.qsize()
|
|
66
|
-
print(f" 队列大小: {size}")
|
|
67
|
-
|
|
68
|
-
# 测试 get 操作
|
|
69
|
-
print(" 📥 获取请求...")
|
|
70
|
-
retrieved_request = await queue.get(timeout=2.0)
|
|
71
|
-
|
|
72
|
-
if retrieved_request:
|
|
73
|
-
print(f" 获取成功: {retrieved_request.url}")
|
|
74
|
-
# 测试 ack
|
|
75
|
-
await queue.ack(retrieved_request)
|
|
76
|
-
print(" ACK 成功")
|
|
77
|
-
else:
|
|
78
|
-
print(" 获取失败(超时)")
|
|
79
|
-
return False
|
|
80
|
-
|
|
81
|
-
return True
|
|
82
|
-
|
|
83
|
-
except Exception as e:
|
|
84
|
-
print(f" 队列操作失败: {e}")
|
|
85
|
-
traceback.print_exc()
|
|
86
|
-
return False
|
|
87
|
-
finally:
|
|
88
|
-
await queue.close()
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
async def test_serialization():
|
|
92
|
-
"""测试序列化问题"""
|
|
93
|
-
print("🔍 3. 测试 Request 序列化...")
|
|
94
|
-
|
|
95
|
-
try:
|
|
96
|
-
import pickle
|
|
97
|
-
from crawlo.network.request import Request
|
|
98
|
-
|
|
99
|
-
# 创建测试请求
|
|
100
|
-
request = Request(
|
|
101
|
-
url="https://example.com",
|
|
102
|
-
method="GET",
|
|
103
|
-
headers={"User-Agent": "Test"},
|
|
104
|
-
meta={"test": "data"},
|
|
105
|
-
priority=5
|
|
106
|
-
)
|
|
107
|
-
|
|
108
|
-
# 测试序列化
|
|
109
|
-
serialized = pickle.dumps(request)
|
|
110
|
-
print(f" 序列化成功,大小: {len(serialized)} bytes")
|
|
111
|
-
|
|
112
|
-
# 测试反序列化
|
|
113
|
-
deserialized = pickle.loads(serialized)
|
|
114
|
-
print(f" 反序列化成功: {deserialized.url}")
|
|
115
|
-
|
|
116
|
-
return True
|
|
117
|
-
|
|
118
|
-
except Exception as e:
|
|
119
|
-
print(f" 序列化失败: {e}")
|
|
120
|
-
traceback.print_exc()
|
|
121
|
-
return False
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
async def test_concurrent_operations(redis_url):
|
|
125
|
-
"""测试并发操作"""
|
|
126
|
-
print("🔍 4. 测试并发操作...")
|
|
127
|
-
|
|
128
|
-
async def producer(queue, start_id):
|
|
129
|
-
"""生产者"""
|
|
130
|
-
try:
|
|
131
|
-
for i in range(5):
|
|
132
|
-
request = Request(url=f"https://example{start_id + i}.com", priority=i)
|
|
133
|
-
await queue.put(request, priority=i)
|
|
134
|
-
await asyncio.sleep(0.1)
|
|
135
|
-
print(f" 生产者 {start_id} 完成")
|
|
136
|
-
except Exception as e:
|
|
137
|
-
print(f" 生产者 {start_id} 失败: {e}")
|
|
138
|
-
|
|
139
|
-
async def consumer(queue, consumer_id):
|
|
140
|
-
"""消费者"""
|
|
141
|
-
consumed = 0
|
|
142
|
-
try:
|
|
143
|
-
for _ in range(3): # 每个消费者处理3个请求
|
|
144
|
-
request = await queue.get(timeout=5.0)
|
|
145
|
-
if request:
|
|
146
|
-
await queue.ack(request)
|
|
147
|
-
consumed += 1
|
|
148
|
-
await asyncio.sleep(0.05)
|
|
149
|
-
else:
|
|
150
|
-
break
|
|
151
|
-
print(f" 消费者 {consumer_id} 处理了 {consumed} 个请求")
|
|
152
|
-
except Exception as e:
|
|
153
|
-
print(f" 消费者 {consumer_id} 失败: {e}")
|
|
154
|
-
|
|
155
|
-
queue = RedisPriorityQueue(
|
|
156
|
-
redis_url=redis_url,
|
|
157
|
-
queue_name="test:concurrent:requests"
|
|
158
|
-
)
|
|
159
|
-
|
|
160
|
-
try:
|
|
161
|
-
await queue.connect()
|
|
162
|
-
|
|
163
|
-
# 并发运行生产者和消费者
|
|
164
|
-
tasks = [
|
|
165
|
-
producer(queue, 0),
|
|
166
|
-
producer(queue, 10),
|
|
167
|
-
consumer(queue, 1),
|
|
168
|
-
consumer(queue, 2),
|
|
169
|
-
]
|
|
170
|
-
|
|
171
|
-
await asyncio.gather(*tasks, return_exceptions=True)
|
|
172
|
-
|
|
173
|
-
# 检查剩余队列大小
|
|
174
|
-
final_size = await queue.qsize()
|
|
175
|
-
print(f" 最终队列大小: {final_size}")
|
|
176
|
-
|
|
177
|
-
return True
|
|
178
|
-
|
|
179
|
-
except Exception as e:
|
|
180
|
-
print(f" 并发测试失败: {e}")
|
|
181
|
-
return False
|
|
182
|
-
finally:
|
|
183
|
-
await queue.close()
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
async def main():
|
|
187
|
-
"""主测试函数"""
|
|
188
|
-
print("开始 Redis 分布式队列诊断...")
|
|
189
|
-
print("=" * 50)
|
|
190
|
-
|
|
191
|
-
try:
|
|
192
|
-
# 1. 测试连接
|
|
193
|
-
redis_url = await test_redis_connection()
|
|
194
|
-
|
|
195
|
-
# 2. 测试序列化
|
|
196
|
-
if not await test_serialization():
|
|
197
|
-
return
|
|
198
|
-
|
|
199
|
-
# 3. 测试基本操作
|
|
200
|
-
if not await test_queue_operations(redis_url):
|
|
201
|
-
return
|
|
202
|
-
|
|
203
|
-
# 4. 测试并发操作
|
|
204
|
-
if not await test_concurrent_operations(redis_url):
|
|
205
|
-
return
|
|
206
|
-
|
|
207
|
-
print("=" * 50)
|
|
208
|
-
print("所有测试通过!Redis 队列工作正常")
|
|
209
|
-
|
|
210
|
-
except Exception as e:
|
|
211
|
-
print("=" * 50)
|
|
212
|
-
print(f"诊断失败: {e}")
|
|
213
|
-
traceback.print_exc()
|
|
214
|
-
|
|
215
|
-
# 提供解决建议
|
|
216
|
-
print("\n🔧 可能的解决方案:")
|
|
217
|
-
print("1. 检查 Redis 服务是否启动: redis-server")
|
|
218
|
-
print("2. 检查 Redis 密码配置")
|
|
219
|
-
print("3. 检查防火墙和端口 6379")
|
|
220
|
-
print("4. 安装 Redis: pip install redis")
|
|
221
|
-
print("5. 检查 Redis 配置文件中的 bind 设置")
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
if __name__ == "__main__":
|
|
1
|
+
#!/usr/bin/env python3
|
|
2
|
+
# -*- coding: utf-8 -*-
|
|
3
|
+
"""
|
|
4
|
+
Redis 分布式队列测试脚本
|
|
5
|
+
用于诊断和修复分布式队列问题
|
|
6
|
+
"""
|
|
7
|
+
import asyncio
|
|
8
|
+
import sys
|
|
9
|
+
import traceback
|
|
10
|
+
import time
|
|
11
|
+
from crawlo.queue.redis_priority_queue import RedisPriorityQueue
|
|
12
|
+
from crawlo.network.request import Request
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
async def test_redis_connection():
|
|
16
|
+
"""测试 Redis 连接"""
|
|
17
|
+
print("🔍 1. 测试 Redis 连接...")
|
|
18
|
+
|
|
19
|
+
# 测试不同的 Redis URL 格式
|
|
20
|
+
test_urls = [
|
|
21
|
+
"redis://localhost:6379/0",
|
|
22
|
+
"redis://:oscar&0503@127.0.0.1:6379/0", # 带密码
|
|
23
|
+
"redis://127.0.0.1:6379/0", # 无密码
|
|
24
|
+
]
|
|
25
|
+
|
|
26
|
+
for redis_url in test_urls:
|
|
27
|
+
try:
|
|
28
|
+
print(f" 尝试连接: {redis_url}")
|
|
29
|
+
queue = RedisPriorityQueue(redis_url=redis_url)
|
|
30
|
+
await queue.connect()
|
|
31
|
+
print(f" 连接成功: {redis_url}")
|
|
32
|
+
await queue.close()
|
|
33
|
+
return redis_url
|
|
34
|
+
except Exception as e:
|
|
35
|
+
print(f" 连接失败: {redis_url} - {e}")
|
|
36
|
+
|
|
37
|
+
raise ConnectionError("所有 Redis URL 都连接失败")
|
|
38
|
+
|
|
39
|
+
|
|
40
|
+
async def test_queue_operations(redis_url):
|
|
41
|
+
"""测试队列基本操作"""
|
|
42
|
+
print("🔍 2. 测试队列基本操作...")
|
|
43
|
+
|
|
44
|
+
queue = RedisPriorityQueue(
|
|
45
|
+
redis_url=redis_url,
|
|
46
|
+
queue_name="test:crawlo:requests",
|
|
47
|
+
max_retries=2
|
|
48
|
+
)
|
|
49
|
+
|
|
50
|
+
try:
|
|
51
|
+
await queue.connect()
|
|
52
|
+
|
|
53
|
+
# 测试 put 操作
|
|
54
|
+
test_request = Request(url="https://example.com", priority=5)
|
|
55
|
+
print(f" 📤 插入请求: {test_request.url}")
|
|
56
|
+
|
|
57
|
+
success = await queue.put(test_request, priority=5)
|
|
58
|
+
if success:
|
|
59
|
+
print(" 插入成功")
|
|
60
|
+
else:
|
|
61
|
+
print(" 插入失败")
|
|
62
|
+
return False
|
|
63
|
+
|
|
64
|
+
# 测试队列大小
|
|
65
|
+
size = await queue.qsize()
|
|
66
|
+
print(f" 队列大小: {size}")
|
|
67
|
+
|
|
68
|
+
# 测试 get 操作
|
|
69
|
+
print(" 📥 获取请求...")
|
|
70
|
+
retrieved_request = await queue.get(timeout=2.0)
|
|
71
|
+
|
|
72
|
+
if retrieved_request:
|
|
73
|
+
print(f" 获取成功: {retrieved_request.url}")
|
|
74
|
+
# 测试 ack
|
|
75
|
+
await queue.ack(retrieved_request)
|
|
76
|
+
print(" ACK 成功")
|
|
77
|
+
else:
|
|
78
|
+
print(" 获取失败(超时)")
|
|
79
|
+
return False
|
|
80
|
+
|
|
81
|
+
return True
|
|
82
|
+
|
|
83
|
+
except Exception as e:
|
|
84
|
+
print(f" 队列操作失败: {e}")
|
|
85
|
+
traceback.print_exc()
|
|
86
|
+
return False
|
|
87
|
+
finally:
|
|
88
|
+
await queue.close()
|
|
89
|
+
|
|
90
|
+
|
|
91
|
+
async def test_serialization():
|
|
92
|
+
"""测试序列化问题"""
|
|
93
|
+
print("🔍 3. 测试 Request 序列化...")
|
|
94
|
+
|
|
95
|
+
try:
|
|
96
|
+
import pickle
|
|
97
|
+
from crawlo.network.request import Request
|
|
98
|
+
|
|
99
|
+
# 创建测试请求
|
|
100
|
+
request = Request(
|
|
101
|
+
url="https://example.com",
|
|
102
|
+
method="GET",
|
|
103
|
+
headers={"User-Agent": "Test"},
|
|
104
|
+
meta={"test": "data"},
|
|
105
|
+
priority=5
|
|
106
|
+
)
|
|
107
|
+
|
|
108
|
+
# 测试序列化
|
|
109
|
+
serialized = pickle.dumps(request)
|
|
110
|
+
print(f" 序列化成功,大小: {len(serialized)} bytes")
|
|
111
|
+
|
|
112
|
+
# 测试反序列化
|
|
113
|
+
deserialized = pickle.loads(serialized)
|
|
114
|
+
print(f" 反序列化成功: {deserialized.url}")
|
|
115
|
+
|
|
116
|
+
return True
|
|
117
|
+
|
|
118
|
+
except Exception as e:
|
|
119
|
+
print(f" 序列化失败: {e}")
|
|
120
|
+
traceback.print_exc()
|
|
121
|
+
return False
|
|
122
|
+
|
|
123
|
+
|
|
124
|
+
async def test_concurrent_operations(redis_url):
|
|
125
|
+
"""测试并发操作"""
|
|
126
|
+
print("🔍 4. 测试并发操作...")
|
|
127
|
+
|
|
128
|
+
async def producer(queue, start_id):
|
|
129
|
+
"""生产者"""
|
|
130
|
+
try:
|
|
131
|
+
for i in range(5):
|
|
132
|
+
request = Request(url=f"https://example{start_id + i}.com", priority=i)
|
|
133
|
+
await queue.put(request, priority=i)
|
|
134
|
+
await asyncio.sleep(0.1)
|
|
135
|
+
print(f" 生产者 {start_id} 完成")
|
|
136
|
+
except Exception as e:
|
|
137
|
+
print(f" 生产者 {start_id} 失败: {e}")
|
|
138
|
+
|
|
139
|
+
async def consumer(queue, consumer_id):
|
|
140
|
+
"""消费者"""
|
|
141
|
+
consumed = 0
|
|
142
|
+
try:
|
|
143
|
+
for _ in range(3): # 每个消费者处理3个请求
|
|
144
|
+
request = await queue.get(timeout=5.0)
|
|
145
|
+
if request:
|
|
146
|
+
await queue.ack(request)
|
|
147
|
+
consumed += 1
|
|
148
|
+
await asyncio.sleep(0.05)
|
|
149
|
+
else:
|
|
150
|
+
break
|
|
151
|
+
print(f" 消费者 {consumer_id} 处理了 {consumed} 个请求")
|
|
152
|
+
except Exception as e:
|
|
153
|
+
print(f" 消费者 {consumer_id} 失败: {e}")
|
|
154
|
+
|
|
155
|
+
queue = RedisPriorityQueue(
|
|
156
|
+
redis_url=redis_url,
|
|
157
|
+
queue_name="test:concurrent:requests"
|
|
158
|
+
)
|
|
159
|
+
|
|
160
|
+
try:
|
|
161
|
+
await queue.connect()
|
|
162
|
+
|
|
163
|
+
# 并发运行生产者和消费者
|
|
164
|
+
tasks = [
|
|
165
|
+
producer(queue, 0),
|
|
166
|
+
producer(queue, 10),
|
|
167
|
+
consumer(queue, 1),
|
|
168
|
+
consumer(queue, 2),
|
|
169
|
+
]
|
|
170
|
+
|
|
171
|
+
await asyncio.gather(*tasks, return_exceptions=True)
|
|
172
|
+
|
|
173
|
+
# 检查剩余队列大小
|
|
174
|
+
final_size = await queue.qsize()
|
|
175
|
+
print(f" 最终队列大小: {final_size}")
|
|
176
|
+
|
|
177
|
+
return True
|
|
178
|
+
|
|
179
|
+
except Exception as e:
|
|
180
|
+
print(f" 并发测试失败: {e}")
|
|
181
|
+
return False
|
|
182
|
+
finally:
|
|
183
|
+
await queue.close()
|
|
184
|
+
|
|
185
|
+
|
|
186
|
+
async def main():
|
|
187
|
+
"""主测试函数"""
|
|
188
|
+
print("开始 Redis 分布式队列诊断...")
|
|
189
|
+
print("=" * 50)
|
|
190
|
+
|
|
191
|
+
try:
|
|
192
|
+
# 1. 测试连接
|
|
193
|
+
redis_url = await test_redis_connection()
|
|
194
|
+
|
|
195
|
+
# 2. 测试序列化
|
|
196
|
+
if not await test_serialization():
|
|
197
|
+
return
|
|
198
|
+
|
|
199
|
+
# 3. 测试基本操作
|
|
200
|
+
if not await test_queue_operations(redis_url):
|
|
201
|
+
return
|
|
202
|
+
|
|
203
|
+
# 4. 测试并发操作
|
|
204
|
+
if not await test_concurrent_operations(redis_url):
|
|
205
|
+
return
|
|
206
|
+
|
|
207
|
+
print("=" * 50)
|
|
208
|
+
print("所有测试通过!Redis 队列工作正常")
|
|
209
|
+
|
|
210
|
+
except Exception as e:
|
|
211
|
+
print("=" * 50)
|
|
212
|
+
print(f"诊断失败: {e}")
|
|
213
|
+
traceback.print_exc()
|
|
214
|
+
|
|
215
|
+
# 提供解决建议
|
|
216
|
+
print("\n🔧 可能的解决方案:")
|
|
217
|
+
print("1. 检查 Redis 服务是否启动: redis-server")
|
|
218
|
+
print("2. 检查 Redis 密码配置")
|
|
219
|
+
print("3. 检查防火墙和端口 6379")
|
|
220
|
+
print("4. 安装 Redis: pip install redis")
|
|
221
|
+
print("5. 检查 Redis 配置文件中的 bind 设置")
|
|
222
|
+
|
|
223
|
+
|
|
224
|
+
if __name__ == "__main__":
|
|
225
225
|
asyncio.run(main())
|
|
@@ -0,0 +1,176 @@
|
|
|
1
|
+
#!/usr/bin/env python3
|
|
2
|
+
# -*- coding: utf-8 -*-
|
|
3
|
+
"""
|
|
4
|
+
Redis队列名称修复测试脚本
|
|
5
|
+
用于验证RedisPriorityQueue中队列名称处理的修复
|
|
6
|
+
"""
|
|
7
|
+
import sys
|
|
8
|
+
import os
|
|
9
|
+
|
|
10
|
+
# 添加项目根目录到路径
|
|
11
|
+
sys.path.insert(0, os.path.join(os.path.dirname(__file__), '..'))
|
|
12
|
+
|
|
13
|
+
# 导入相关模块
|
|
14
|
+
from crawlo.queue.redis_priority_queue import RedisPriorityQueue
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
def test_normalize_queue_name():
|
|
18
|
+
"""测试队列名称规范化函数"""
|
|
19
|
+
print("开始测试RedisPriorityQueue队列名称规范化...")
|
|
20
|
+
print("=" * 50)
|
|
21
|
+
|
|
22
|
+
# 创建一个RedisPriorityQueue实例用于测试
|
|
23
|
+
queue = RedisPriorityQueue(redis_url="redis://127.0.0.1:6379/15")
|
|
24
|
+
|
|
25
|
+
test_cases = [
|
|
26
|
+
{
|
|
27
|
+
"name": "已经规范化的名称",
|
|
28
|
+
"input": "crawlo:test_project:queue:requests",
|
|
29
|
+
"expected": "crawlo:test_project:queue:requests"
|
|
30
|
+
},
|
|
31
|
+
{
|
|
32
|
+
"name": "双重 crawlo 前缀",
|
|
33
|
+
"input": "crawlo:crawlo:queue:requests",
|
|
34
|
+
"expected": "crawlo:queue:requests"
|
|
35
|
+
},
|
|
36
|
+
{
|
|
37
|
+
"name": "三重 crawlo 前缀",
|
|
38
|
+
"input": "crawlo:crawlo:crawlo:queue:requests",
|
|
39
|
+
"expected": "crawlo:queue:requests"
|
|
40
|
+
},
|
|
41
|
+
{
|
|
42
|
+
"name": "无 crawlo 前缀",
|
|
43
|
+
"input": "test_project:queue:requests",
|
|
44
|
+
"expected": "crawlo:test_project:queue:requests"
|
|
45
|
+
},
|
|
46
|
+
{
|
|
47
|
+
"name": "空队列名称",
|
|
48
|
+
"input": "",
|
|
49
|
+
"expected": "crawlo:requests"
|
|
50
|
+
}
|
|
51
|
+
]
|
|
52
|
+
|
|
53
|
+
all_passed = True
|
|
54
|
+
|
|
55
|
+
for i, test_case in enumerate(test_cases, 1):
|
|
56
|
+
print(f"测试 {i}: {test_case['name']}")
|
|
57
|
+
print(f" 输入: {test_case['input']}")
|
|
58
|
+
|
|
59
|
+
# 测试规范化函数
|
|
60
|
+
result = queue._normalize_queue_name(test_case['input'])
|
|
61
|
+
print(f" 输出: {result}")
|
|
62
|
+
print(f" 期望: {test_case['expected']}")
|
|
63
|
+
|
|
64
|
+
# 验证结果
|
|
65
|
+
if result == test_case['expected']:
|
|
66
|
+
print(" ✓ 测试通过")
|
|
67
|
+
else:
|
|
68
|
+
print(" ✗ 测试失败")
|
|
69
|
+
all_passed = False
|
|
70
|
+
|
|
71
|
+
print()
|
|
72
|
+
|
|
73
|
+
print("=" * 50)
|
|
74
|
+
if all_passed:
|
|
75
|
+
print("所有测试通过!队列名称规范化修复成功")
|
|
76
|
+
return True
|
|
77
|
+
else:
|
|
78
|
+
print("部分测试失败,请检查实现")
|
|
79
|
+
return False
|
|
80
|
+
|
|
81
|
+
|
|
82
|
+
def test_queue_initialization():
|
|
83
|
+
"""测试队列初始化时的名称处理"""
|
|
84
|
+
print("开始测试RedisPriorityQueue初始化时的名称处理...")
|
|
85
|
+
print("=" * 50)
|
|
86
|
+
|
|
87
|
+
test_cases = [
|
|
88
|
+
{
|
|
89
|
+
"name": "正常命名",
|
|
90
|
+
"queue_name": "crawlo:test_project:queue:requests",
|
|
91
|
+
"expected_queue": "crawlo:test_project:queue:requests",
|
|
92
|
+
"expected_processing": "crawlo:test_project:queue:processing",
|
|
93
|
+
"expected_failed": "crawlo:test_project:queue:failed"
|
|
94
|
+
},
|
|
95
|
+
{
|
|
96
|
+
"name": "双重 crawlo 前缀",
|
|
97
|
+
"queue_name": "crawlo:crawlo:queue:requests",
|
|
98
|
+
"expected_queue": "crawlo:queue:requests",
|
|
99
|
+
"expected_processing": "crawlo:queue:processing",
|
|
100
|
+
"expected_failed": "crawlo:queue:failed"
|
|
101
|
+
}
|
|
102
|
+
]
|
|
103
|
+
|
|
104
|
+
all_passed = True
|
|
105
|
+
|
|
106
|
+
for i, test_case in enumerate(test_cases, 1):
|
|
107
|
+
print(f"测试 {i}: {test_case['name']}")
|
|
108
|
+
print(f" 输入队列名称: {test_case['queue_name']}")
|
|
109
|
+
|
|
110
|
+
try:
|
|
111
|
+
# 创建RedisPriorityQueue实例
|
|
112
|
+
queue = RedisPriorityQueue(
|
|
113
|
+
redis_url="redis://127.0.0.1:6379/15",
|
|
114
|
+
queue_name=test_case['queue_name'],
|
|
115
|
+
module_name="test_project"
|
|
116
|
+
)
|
|
117
|
+
|
|
118
|
+
print(f" 实际队列名称: {queue.queue_name}")
|
|
119
|
+
print(f" 实际处理队列: {queue.processing_queue}")
|
|
120
|
+
print(f" 实际失败队列: {queue.failed_queue}")
|
|
121
|
+
|
|
122
|
+
print(f" 期望队列名称: {test_case['expected_queue']}")
|
|
123
|
+
print(f" 期望处理队列: {test_case['expected_processing']}")
|
|
124
|
+
print(f" 期望失败队列: {test_case['expected_failed']}")
|
|
125
|
+
|
|
126
|
+
# 验证结果
|
|
127
|
+
queue_name_ok = queue.queue_name == test_case['expected_queue']
|
|
128
|
+
processing_queue_ok = queue.processing_queue == test_case['expected_processing']
|
|
129
|
+
failed_queue_ok = queue.failed_queue == test_case['expected_failed']
|
|
130
|
+
|
|
131
|
+
if queue_name_ok and processing_queue_ok and failed_queue_ok:
|
|
132
|
+
print(" ✓ 测试通过")
|
|
133
|
+
else:
|
|
134
|
+
print(" ✗ 测试失败")
|
|
135
|
+
all_passed = False
|
|
136
|
+
|
|
137
|
+
except Exception as e:
|
|
138
|
+
print(f" ✗ 测试异常: {e}")
|
|
139
|
+
all_passed = False
|
|
140
|
+
|
|
141
|
+
print()
|
|
142
|
+
|
|
143
|
+
print("=" * 50)
|
|
144
|
+
if all_passed:
|
|
145
|
+
print("队列初始化测试通过!")
|
|
146
|
+
return True
|
|
147
|
+
else:
|
|
148
|
+
print("队列初始化测试失败!")
|
|
149
|
+
return False
|
|
150
|
+
|
|
151
|
+
|
|
152
|
+
def main():
|
|
153
|
+
"""主测试函数"""
|
|
154
|
+
print("开始Redis队列名称修复测试...")
|
|
155
|
+
print("=" * 50)
|
|
156
|
+
|
|
157
|
+
# 测试队列名称规范化函数
|
|
158
|
+
normalize_test_passed = test_normalize_queue_name()
|
|
159
|
+
print()
|
|
160
|
+
|
|
161
|
+
# 测试队列初始化
|
|
162
|
+
init_test_passed = test_queue_initialization()
|
|
163
|
+
print()
|
|
164
|
+
|
|
165
|
+
print("=" * 50)
|
|
166
|
+
if normalize_test_passed and init_test_passed:
|
|
167
|
+
print("所有测试通过!Redis队列名称修复完成")
|
|
168
|
+
return 0
|
|
169
|
+
else:
|
|
170
|
+
print("部分测试失败,请检查实现")
|
|
171
|
+
return 1
|
|
172
|
+
|
|
173
|
+
|
|
174
|
+
if __name__ == "__main__":
|
|
175
|
+
exit_code = main()
|
|
176
|
+
sys.exit(exit_code)
|