liurenchaxin/examples/demo_jixia_load_balancing.py

283 lines
11 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

#!/usr/bin/env python3
# -*- coding: utf-8 -*-
"""
稷下学宫负载均衡演示脚本
展示八仙论道的API负载分担策略
"""
import os
import sys
import time
import json
from datetime import datetime
# 添加项目路径
sys.path.append('/home/ben/liurenchaxin/src')
from jixia.engines.jixia_load_balancer import JixiaLoadBalancer, APIResult
def print_banner():
"""打印横幅"""
print("\n" + "="*80)
print("🏛️ 稷下学宫八仙论道 - API负载均衡演示系统")
print("📊 RapidAPI多源数据整合与负载分担策略")
print("="*80)
print(f"⏰ 演示时间: {datetime.now().strftime('%Y-%m-%d %H:%M:%S')}")
print()
def print_immortal_intro():
"""介绍八仙角色"""
immortals_info = {
'吕洞宾': '主力剑仙 - 综合分析与决策 (Alpha Vantage)',
'何仙姑': '风控专家 - 风险管理与合规 (Yahoo Finance)',
'张果老': '技术分析师 - 技术指标与图表分析 (Webull)',
'韩湘子': '基本面研究员 - 财务分析与估值 (Seeking Alpha)',
'汉钟离': '量化专家 - 数据挖掘与算法交易 (Yahoo Finance)',
'蓝采和': '情绪分析师 - 市场情绪与舆情监控 (Webull)',
'曹国舅': '宏观分析师 - 宏观经济与政策分析 (Seeking Alpha)',
'铁拐李': '逆向投资专家 - 价值发现与逆向思维 (Alpha Vantage)'
}
print("👥 八仙角色与API分配:")
print("-" * 60)
for immortal, description in immortals_info.items():
print(f" {immortal}: {description}")
print()
def demonstrate_single_immortal(load_balancer, immortal_name, symbol):
"""演示单个仙人的数据获取"""
print(f"\n🎭 {immortal_name} 单独获取数据演示:")
print("-" * 40)
# 获取股票报价
result = load_balancer.get_data_for_immortal(immortal_name, 'stock_quote', symbol)
if result.success:
data = result.data
print(f" ✅ 成功获取 {symbol} 数据:")
print(f" 💰 价格: ${data.get('price', 'N/A')}")
print(f" 📈 涨跌: {data.get('change_percent', 'N/A')}")
volume = data.get('volume', 'N/A')
if isinstance(volume, (int, float)):
print(f" 📊 成交量: {volume:,}")
else:
print(f" 📊 成交量: {volume}")
print(f" 🔗 数据源: {result.api_used}")
print(f" ⏱️ 响应时间: {result.response_time:.2f}")
print(f" 💾 缓存状态: {'' if result.cached else ''}")
else:
print(f" ❌ 获取失败: {result.error}")
def demonstrate_load_distribution(load_balancer):
"""演示负载分布"""
print("\n📊 API负载分布统计:")
print("-" * 40)
distribution = load_balancer.get_load_distribution()
if not distribution:
print(" 📝 暂无API调用记录")
return
total_calls = sum(stats['calls'] for stats in distribution.values())
for api_name, stats in distribution.items():
status_icon = "🟢" if stats['healthy'] else "🔴"
print(f" {status_icon} {api_name}:")
print(f" 📞 调用次数: {stats['calls']}")
print(f" 📊 负载占比: {stats['percentage']:.1f}%")
print(f" ❌ 连续失败: {stats['consecutive_failures']}")
print()
def demonstrate_api_comparison(load_balancer, symbol):
"""演示不同API的数据对比"""
print(f"\n🔍 {symbol} 多API数据对比:")
print("-" * 50)
apis = ['alpha_vantage', 'yahoo_finance_15', 'webull']
results = {}
for api in apis:
# 临时修改API分配来测试不同数据源
original_mapping = load_balancer.immortal_api_mapping['stock_quote']['吕洞宾']
load_balancer.immortal_api_mapping['stock_quote']['吕洞宾'] = api
result = load_balancer.get_data_for_immortal('吕洞宾', 'stock_quote', symbol)
results[api] = result
# 恢复原始配置
load_balancer.immortal_api_mapping['stock_quote']['吕洞宾'] = original_mapping
time.sleep(0.5) # 避免过快请求
# 显示对比结果
print(" API数据源对比:")
for api, result in results.items():
if result.success:
data = result.data
print(f" 📡 {api}:")
print(f" 💰 ${data.get('price', 'N/A')} ({data.get('change_percent', 'N/A')})")
print(f" ⏱️ {result.response_time:.2f}s")
else:
print(f" 📡 {api}: ❌ {result.error}")
print()
def demonstrate_cache_effectiveness(load_balancer, symbol):
"""演示缓存效果"""
print(f"\n💾 缓存效果演示 - {symbol}:")
print("-" * 40)
# 第一次调用(无缓存)
print(" 🔄 第一次调用(无缓存):")
start_time = time.time()
result1 = load_balancer.get_data_for_immortal('吕洞宾', 'stock_quote', symbol)
first_call_time = time.time() - start_time
if result1.success:
print(f" ⏱️ 响应时间: {result1.response_time:.2f}")
print(f" 💾 缓存状态: {'命中' if result1.cached else '未命中'}")
time.sleep(1)
# 第二次调用(有缓存)
print(" 🔄 第二次调用(有缓存):")
start_time = time.time()
result2 = load_balancer.get_data_for_immortal('吕洞宾', 'stock_quote', symbol)
second_call_time = time.time() - start_time
if result2.success:
print(f" ⏱️ 响应时间: {result2.response_time:.2f}")
print(f" 💾 缓存状态: {'命中' if result2.cached else '未命中'}")
if result2.cached:
speedup = (first_call_time / second_call_time) if second_call_time > 0 else float('inf')
print(f" 🚀 性能提升: {speedup:.1f}")
def demonstrate_failover(load_balancer, symbol):
"""演示故障转移"""
print(f"\n🔄 故障转移演示 - {symbol}:")
print("-" * 40)
# 模拟API故障
print(" ⚠️ 模拟主API故障...")
# 临时标记API为不健康
original_health = load_balancer.health_checker.health_status['alpha_vantage']['healthy']
load_balancer.health_checker.health_status['alpha_vantage']['healthy'] = False
load_balancer.health_checker.health_status['alpha_vantage']['consecutive_failures'] = 5
# 尝试获取数据(应该自动故障转移)
result = load_balancer.get_data_for_immortal('吕洞宾', 'stock_quote', symbol)
if result.success:
print(f" ✅ 故障转移成功使用备用API: {result.api_used}")
print(f" 💰 获取到价格: ${result.data.get('price', 'N/A')}")
else:
print(f" ❌ 故障转移失败: {result.error}")
# 恢复API健康状态
load_balancer.health_checker.health_status['alpha_vantage']['healthy'] = original_health
load_balancer.health_checker.health_status['alpha_vantage']['consecutive_failures'] = 0
print(" 🔧 API健康状态已恢复")
def save_demo_results(results, filename='demo_results.json'):
"""保存演示结果"""
demo_data = {
'timestamp': datetime.now().isoformat(),
'results': {},
'summary': {
'total_immortals': len(results),
'successful_calls': sum(1 for r in results.values() if r.success),
'failed_calls': sum(1 for r in results.values() if not r.success)
}
}
for immortal, result in results.items():
demo_data['results'][immortal] = {
'success': result.success,
'api_used': result.api_used,
'response_time': result.response_time,
'cached': result.cached,
'error': result.error,
'data_summary': {
'symbol': result.data.get('symbol') if result.success else None,
'price': result.data.get('price') if result.success else None,
'change_percent': result.data.get('change_percent') if result.success else None
}
}
with open(filename, 'w', encoding='utf-8') as f:
json.dump(demo_data, f, indent=2, ensure_ascii=False)
print(f"\n💾 演示结果已保存到: {filename}")
def main():
"""主演示函数"""
# 检查API密钥
rapidapi_key = os.getenv('RAPIDAPI_KEY')
if not rapidapi_key:
print("❌ 错误: 请设置RAPIDAPI_KEY环境变量")
print(" 提示: 使用 'doppler run python demo_jixia_load_balancing.py' 运行")
return
print_banner()
print_immortal_intro()
# 创建负载均衡器
print("🔧 初始化稷下学宫负载均衡器...")
load_balancer = JixiaLoadBalancer(rapidapi_key)
print("✅ 负载均衡器初始化完成\n")
# 演示股票代码
demo_symbols = ['AAPL', 'TSLA', 'MSFT']
for i, symbol in enumerate(demo_symbols, 1):
print(f"\n{'='*20} 演示 {i}: {symbol} {'='*20}")
# 1. 单个仙人演示
demonstrate_single_immortal(load_balancer, '吕洞宾', symbol)
# 2. 八仙论道演示
print(f"\n🏛️ 八仙论道完整演示 - {symbol}:")
debate_results = load_balancer.conduct_immortal_debate(symbol)
# 3. 负载分布演示
demonstrate_load_distribution(load_balancer)
# 只在第一个股票上演示高级功能
if i == 1:
# 4. API对比演示
demonstrate_api_comparison(load_balancer, symbol)
# 5. 缓存效果演示
demonstrate_cache_effectiveness(load_balancer, symbol)
# 6. 故障转移演示
demonstrate_failover(load_balancer, symbol)
# 保存结果
save_demo_results(debate_results, f'demo_results_{symbol.lower()}.json')
if i < len(demo_symbols):
print("\n⏳ 等待3秒后继续下一个演示...")
time.sleep(3)
# 最终统计
print("\n" + "="*80)
print("📈 演示完成 - 最终负载分布统计:")
demonstrate_load_distribution(load_balancer)
print("\n🎉 稷下学宫API负载均衡演示完成")
print("\n💡 关键特性:")
print(" ✅ 智能负载分担 - 八仙各司其职分散API压力")
print(" ✅ 自动故障转移 - API异常时自动切换备用源")
print(" ✅ 数据标准化 - 统一不同API的数据格式")
print(" ✅ 智能缓存 - 减少重复调用,提升响应速度")
print(" ✅ 实时监控 - 跟踪API健康状态和负载分布")
print("\n📚 查看详细配置: /home/ben/liurenchaxin/src/jixia/config/immortal_api_config.json")
print("🔧 核心引擎: /home/ben/liurenchaxin/src/jixia/engines/jixia_load_balancer.py")
print("="*80)
if __name__ == "__main__":
main()