main.py 5.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215
  1. """
  2. Tolan语音助手后端主应用
  3. """
  4. import time
  5. from fastapi import FastAPI, WebSocket, WebSocketDisconnect
  6. from fastapi.middleware.cors import CORSMiddleware
  7. from fastapi.responses import JSONResponse
  8. from loguru import logger
  9. import uvicorn
  10. from config import settings
  11. from connection_pool import connection_pool
  12. from websocket_handler import connection_manager
  13. from performance import performance_monitor
  14. # 创建FastAPI应用
  15. app = FastAPI(
  16. title=settings.APP_NAME,
  17. version=settings.APP_VERSION,
  18. description="实时语音交互API服务"
  19. )
  20. # 配置CORS
  21. app.add_middleware(
  22. CORSMiddleware,
  23. allow_origins=["*"] if settings.CORS_ALLOW_ALL else settings.CORS_ORIGINS,
  24. allow_credentials=True,
  25. allow_methods=["*"],
  26. allow_headers=["*"],
  27. )
  28. # 配置日志
  29. logger.add(
  30. f"{settings.LOG_DIR}/app_{{time}}.log",
  31. rotation=settings.LOG_ROTATION,
  32. retention=settings.LOG_RETENTION,
  33. level=settings.LOG_LEVEL,
  34. format="{time:YYYY-MM-DD HH:mm:ss} | {level} | {message}"
  35. )
  36. @app.on_event("startup")
  37. async def startup_event():
  38. """应用启动事件"""
  39. logger.info("=" * 60)
  40. logger.info(f"{settings.APP_NAME} v{settings.APP_VERSION} 正在启动...")
  41. logger.info("=" * 60)
  42. # 初始化连接池
  43. try:
  44. await connection_pool.initialize()
  45. logger.success("✓ 连接池初始化成功")
  46. except Exception as e:
  47. logger.error(f"✗ 连接池初始化失败: {e}")
  48. logger.info("=" * 60)
  49. logger.success(f"应用启动完成!")
  50. logger.info(f"服务地址: http://{settings.HOST}:{settings.PORT}")
  51. logger.info(f"API文档: http://{settings.HOST}:{settings.PORT}/docs")
  52. logger.info(f"WebSocket: ws://{settings.HOST}:{settings.PORT}/ws/realtime")
  53. logger.info("=" * 60)
  54. @app.on_event("shutdown")
  55. async def shutdown_event():
  56. """应用关闭事件"""
  57. logger.info("应用正在关闭...")
  58. # 关闭连接池
  59. try:
  60. await connection_pool.close_all()
  61. logger.success("✓ 连接池已关闭")
  62. except Exception as e:
  63. logger.error(f"✗ 关闭连接池失败: {e}")
  64. logger.info("应用已关闭")
  65. @app.get("/")
  66. async def root():
  67. """根路径"""
  68. return {
  69. "app": settings.APP_NAME,
  70. "version": settings.APP_VERSION,
  71. "status": "running",
  72. "endpoints": {
  73. "docs": "/docs",
  74. "websocket": "/ws/realtime",
  75. "health": "/health"
  76. }
  77. }
  78. @app.get("/health")
  79. async def health_check():
  80. """健康检查"""
  81. return {
  82. "status": "healthy",
  83. "timestamp": int(time.time() * 1000),
  84. "connection_pool": {
  85. "initialized": connection_pool.is_initialized,
  86. "pool_size": connection_pool.pool_size,
  87. "idle": connection_pool.idle_connections.qsize(),
  88. "active": len(connection_pool.active_connections)
  89. },
  90. "active_sessions": len(connection_manager.active_connections)
  91. }
  92. @app.get("/stats")
  93. async def get_stats():
  94. """获取性能统计"""
  95. return {
  96. "status": "ok",
  97. "timestamp": int(time.time() * 1000),
  98. "performance": performance_monitor.get_stats(),
  99. "connection_pool": {
  100. "pool_size": connection_pool.pool_size,
  101. "idle": connection_pool.idle_connections.qsize(),
  102. "active": len(connection_pool.active_connections)
  103. },
  104. "sessions": {
  105. "active": len(connection_manager.active_connections),
  106. "total": performance_monitor.session_count
  107. }
  108. }
  109. @app.websocket("/ws/realtime")
  110. async def websocket_endpoint(websocket: WebSocket):
  111. """WebSocket实时通信端点"""
  112. session_id = f"session_{int(time.time() * 1000)}"
  113. session = None
  114. try:
  115. # 建立连接
  116. session = await connection_manager.connect(websocket, session_id)
  117. logger.info(f"WebSocket连接建立: {session_id}")
  118. # 启动会话
  119. if not await session.start():
  120. logger.error(f"会话启动失败: {session_id}")
  121. return
  122. # 处理流式数据
  123. await session.process_stream()
  124. except WebSocketDisconnect:
  125. logger.info(f"客户端主动断开: {session_id}")
  126. except Exception as e:
  127. logger.error(f"WebSocket错误 [{session_id}]: {e}")
  128. # 发送错误消息
  129. if session:
  130. try:
  131. await session.send_error(str(e))
  132. except:
  133. pass
  134. finally:
  135. # 清理连接
  136. await connection_manager.disconnect(session_id)
  137. @app.exception_handler(Exception)
  138. async def global_exception_handler(request, exc):
  139. """全局异常处理"""
  140. logger.error(f"全局异常: {exc}")
  141. return JSONResponse(
  142. status_code=500,
  143. content={
  144. "error": "Internal Server Error",
  145. "message": str(exc)
  146. }
  147. )
  148. def start_server():
  149. """启动服务器"""
  150. # 检查是否使用SSL
  151. ssl_keyfile = settings.SSL_KEYFILE
  152. ssl_certfile = settings.SSL_CERTFILE
  153. import os
  154. use_ssl = (
  155. os.path.exists(ssl_keyfile) and
  156. os.path.exists(ssl_certfile)
  157. )
  158. if use_ssl:
  159. logger.info(f"使用SSL证书启动")
  160. uvicorn.run(
  161. "main:app",
  162. host=settings.HOST,
  163. port=settings.PORT,
  164. reload=settings.DEBUG,
  165. ssl_keyfile=ssl_keyfile,
  166. ssl_certfile=ssl_certfile,
  167. log_level=settings.LOG_LEVEL.lower()
  168. )
  169. else:
  170. logger.warning("未找到SSL证书,使用HTTP启动")
  171. uvicorn.run(
  172. "main:app",
  173. host=settings.HOST,
  174. port=settings.PORT,
  175. reload=settings.DEBUG,
  176. log_level=settings.LOG_LEVEL.lower()
  177. )
  178. if __name__ == "__main__":
  179. start_server()