TCPDashboard/examples/collector_demo.py

309 lines
10 KiB
Python
Raw Normal View History

"""
Demonstration of the enhanced data collector system with health monitoring and auto-restart.
This example shows how to:
1. Create data collectors with health monitoring
2. Use the collector manager for coordinated management
3. Monitor collector health and handle failures
4. Enable/disable collectors dynamically
"""
import asyncio
from datetime import datetime, timezone
from typing import Any, Optional
from data import (
BaseDataCollector, DataType, CollectorStatus, MarketDataPoint,
CollectorManager, CollectorConfig
)
class DemoDataCollector(BaseDataCollector):
"""
Demo implementation of a data collector for demonstration purposes.
This collector simulates receiving market data and can be configured
to fail periodically to demonstrate auto-restart functionality.
"""
def __init__(self,
exchange_name: str,
symbols: list,
fail_every_n_messages: int = 0,
connection_delay: float = 0.1):
"""
Initialize demo collector.
Args:
exchange_name: Name of the exchange
symbols: Trading symbols to collect
fail_every_n_messages: Simulate failure every N messages (0 = no failures)
connection_delay: Simulated connection delay
"""
super().__init__(exchange_name, symbols, [DataType.TICKER])
self.fail_every_n_messages = fail_every_n_messages
self.connection_delay = connection_delay
self.message_count = 0
self.connected = False
self.subscribed = False
async def connect(self) -> bool:
"""Simulate connection to exchange."""
print(f"[{self.exchange_name}] Connecting...")
await asyncio.sleep(self.connection_delay)
self.connected = True
print(f"[{self.exchange_name}] Connected successfully")
return True
async def disconnect(self) -> None:
"""Simulate disconnection from exchange."""
print(f"[{self.exchange_name}] Disconnecting...")
await asyncio.sleep(self.connection_delay / 2)
self.connected = False
self.subscribed = False
print(f"[{self.exchange_name}] Disconnected")
async def subscribe_to_data(self, symbols: list, data_types: list) -> bool:
"""Simulate subscription to data streams."""
if not self.connected:
return False
print(f"[{self.exchange_name}] Subscribing to {len(symbols)} symbols: {', '.join(symbols)}")
await asyncio.sleep(0.05)
self.subscribed = True
return True
async def unsubscribe_from_data(self, symbols: list, data_types: list) -> bool:
"""Simulate unsubscription from data streams."""
print(f"[{self.exchange_name}] Unsubscribing from data streams")
self.subscribed = False
return True
async def _process_message(self, message: Any) -> Optional[MarketDataPoint]:
"""Process simulated market data message."""
self.message_count += 1
# Simulate periodic failures if configured
if (self.fail_every_n_messages > 0 and
self.message_count % self.fail_every_n_messages == 0):
raise Exception(f"Simulated failure after {self.message_count} messages")
# Create mock market data
data_point = MarketDataPoint(
exchange=self.exchange_name,
symbol=message['symbol'],
timestamp=datetime.now(timezone.utc),
data_type=DataType.TICKER,
data={
'price': message['price'],
'volume': message.get('volume', 100),
'timestamp': datetime.now(timezone.utc).isoformat()
}
)
return data_point
async def _handle_messages(self) -> None:
"""Simulate receiving and processing messages."""
if not self.connected or not self.subscribed:
await asyncio.sleep(0.1)
return
# Simulate receiving data for each symbol
for symbol in self.symbols:
try:
# Create simulated message
simulated_message = {
'symbol': symbol,
'price': 50000 + (self.message_count % 1000), # Fake price that changes
'volume': 1.5
}
# Process the message
data_point = await self._process_message(simulated_message)
if data_point:
self._stats['messages_processed'] += 1
await self._notify_callbacks(data_point)
except Exception as e:
# This will trigger reconnection logic
raise e
# Simulate processing delay
await asyncio.sleep(1.0)
async def data_callback(data_point: MarketDataPoint):
"""Callback function to handle received data."""
print(f"📊 Data received: {data_point.exchange} - {data_point.symbol} - "
f"Price: {data_point.data.get('price')} at {data_point.timestamp.strftime('%H:%M:%S')}")
async def monitor_collectors(manager: CollectorManager, duration: int = 30):
"""Monitor collector status and print updates."""
print(f"\n🔍 Starting monitoring for {duration} seconds...")
for i in range(duration):
await asyncio.sleep(1)
status = manager.get_status()
running = len(manager.get_running_collectors())
failed = len(manager.get_failed_collectors())
if i % 5 == 0: # Print status every 5 seconds
print(f"⏰ Status at {i+1}s: {running} running, {failed} failed, "
f"{status['statistics']['restarts_performed']} restarts")
print("🏁 Monitoring complete")
async def demo_basic_usage():
"""Demonstrate basic collector usage."""
print("=" * 60)
print("🚀 Demo 1: Basic Data Collector Usage")
print("=" * 60)
# Create a stable collector
collector = DemoDataCollector("demo_exchange", ["BTC-USDT", "ETH-USDT"])
# Add data callback
collector.add_data_callback(DataType.TICKER, data_callback)
# Start the collector
print("Starting collector...")
success = await collector.start()
if success:
print("✅ Collector started successfully")
# Let it run for a few seconds
await asyncio.sleep(5)
# Show status
status = collector.get_status()
print(f"📈 Messages processed: {status['statistics']['messages_processed']}")
print(f"⏱️ Uptime: {status['statistics']['uptime_seconds']:.1f}s")
# Stop the collector
await collector.stop()
print("✅ Collector stopped")
else:
print("❌ Failed to start collector")
async def demo_manager_usage():
"""Demonstrate collector manager usage."""
print("\n" + "=" * 60)
print("🎛️ Demo 2: Collector Manager Usage")
print("=" * 60)
# Create manager
manager = CollectorManager("demo_manager", global_health_check_interval=3.0)
# Create multiple collectors
stable_collector = DemoDataCollector("stable_exchange", ["BTC-USDT"])
failing_collector = DemoDataCollector("failing_exchange", ["ETH-USDT"],
fail_every_n_messages=5) # Fails every 5 messages
# Add data callbacks
stable_collector.add_data_callback(DataType.TICKER, data_callback)
failing_collector.add_data_callback(DataType.TICKER, data_callback)
# Add collectors to manager
manager.add_collector(stable_collector)
manager.add_collector(failing_collector)
print(f"📝 Added {len(manager.list_collectors())} collectors to manager")
# Start manager
success = await manager.start()
if success:
print("✅ Manager started successfully")
# Monitor for a while
await monitor_collectors(manager, duration=15)
# Show final status
status = manager.get_status()
print(f"\n📊 Final Statistics:")
print(f" - Total restarts: {status['statistics']['restarts_performed']}")
print(f" - Running collectors: {len(manager.get_running_collectors())}")
print(f" - Failed collectors: {len(manager.get_failed_collectors())}")
# Stop manager
await manager.stop()
print("✅ Manager stopped")
else:
print("❌ Failed to start manager")
async def demo_dynamic_management():
"""Demonstrate dynamic collector management."""
print("\n" + "=" * 60)
print("🔄 Demo 3: Dynamic Collector Management")
print("=" * 60)
# Create manager
manager = CollectorManager("dynamic_manager", global_health_check_interval=2.0)
# Start with one collector
collector1 = DemoDataCollector("exchange_1", ["BTC-USDT"])
collector1.add_data_callback(DataType.TICKER, data_callback)
manager.add_collector(collector1)
await manager.start()
print("✅ Started with 1 collector")
await asyncio.sleep(3)
# Add second collector
collector2 = DemoDataCollector("exchange_2", ["ETH-USDT"])
collector2.add_data_callback(DataType.TICKER, data_callback)
manager.add_collector(collector2)
print(" Added second collector")
await asyncio.sleep(3)
# Disable first collector
collector_names = manager.list_collectors()
manager.disable_collector(collector_names[0])
print("⏸️ Disabled first collector")
await asyncio.sleep(3)
# Re-enable first collector
manager.enable_collector(collector_names[0])
print("▶️ Re-enabled first collector")
await asyncio.sleep(3)
# Show final status
status = manager.get_status()
print(f"📊 Final state: {len(manager.get_running_collectors())} running collectors")
await manager.stop()
print("✅ Dynamic demo complete")
async def main():
"""Run all demonstrations."""
print("🎯 Data Collector System Demonstration")
print("This demo shows health monitoring and auto-restart capabilities\n")
try:
# Run demonstrations
await demo_basic_usage()
await demo_manager_usage()
await demo_dynamic_management()
print("\n" + "=" * 60)
print("🎉 All demonstrations completed successfully!")
print("=" * 60)
except Exception as e:
print(f"❌ Demo failed with error: {e}")
import traceback
traceback.print_exc()
if __name__ == "__main__":
asyncio.run(main())