# +====================================================================== # | 模块: 启动引导层 # | 说明: 解析命令行参数、创建应用并启动 HTTP 服务, # | 注册信号处理实现优雅关闭 # +====================================================================== """启动引导程序。 - 解析命令行参数(--port) - 创建应用并启动 HTTP 服务 - 注册信号处理实现优雅关闭 """ # ---------------------------------------------------------------------- # 导入依赖 # 涵盖:信号处理、Tornado HTTP 服务与事件循环、命令行参数工具、 # 应用/数据库/邮件/Redis 配置、日志器 # ---------------------------------------------------------------------- import signal import tornado.httpserver import tornado.ioloop from tornado.options import define, options, parse_command_line from config.app import TORNADO_ENV, TORNADO_HOST, TORNADO_NAME, TORNADO_PORT from config.database import DB_PASSWORD from config.email import MAIL_PASSWORD from config.redis import REDIS_PASSWORD from core.logger import logger # ============================================================ # 命令行参数定义 # ============================================================ define('port', default=TORNADO_PORT, help='run on the given port', type=int) # ====================================================================== # 启动 Banner # ====================================================================== _BANNER = r""" ____ _ _ | _ \ _ _ __ _ _ __ ___ (_) _ __ __ _ _ __ __| | | | | || | | | / _` || '__| / __|| || '_ \ / _` || '__| / _` | | |_| || |_| || (_| || | | (__ | || |_) || (_| || | | (_| | |____/ \__,_| \__,_||_| \___||_|| .__/ \__,_||_| \__,_| |_| Tornado + EleVue 企业级管理框架 """ # ====================================================================== # 启动服务 # ====================================================================== def main(): """启动服务。""" # 延迟导入应用工厂,避免与根目录入口模块同名冲突 from app import create_app # 打印启动 Banner print(_BANNER) parse_command_line() app = create_app() # 预热 ip2region 离线库,避免首个日志落库时在事件循环上加载阻塞 try: from common.utils.ip2region import preload preload() except Exception: logger.warning("ip2region 预热失败(将延迟到首次查询时加载)") # 生产环境安全提醒(签名密钥已在 config.auth 启动校验中 fail-close;此处对其它 # 敏感凭据做软告警,提示确认来源,不阻断启动) if TORNADO_ENV.strip().lower() == "production": for name, value in (("DB_PASSWORD", DB_PASSWORD), ("REDIS_PASSWORD", REDIS_PASSWORD), ("MAIL_PASSWORD", MAIL_PASSWORD)): if not value: logger.warning("生产环境检测到 %s 为空,请确认凭据来源(环境变量 / 密钥管理服务)", name) http_server = tornado.httpserver.HTTPServer(app) http_server.listen(options.port) # 启动任务调度器并加载运行中任务 from modules.job.scheduler import init_scheduler init_scheduler() logger.info("服务启动成功 → http://%s:%s", TORNADO_HOST, options.port) ioloop = tornado.ioloop.IOLoop.current() # ============================================================ # 优雅关闭回调 # ============================================================ def _shutdown(): """收到退出信号时关闭调度器并停止事件循环。""" logger.info("收到退出信号,正在优雅关闭...") from modules.job.scheduler import shutdown_scheduler shutdown_scheduler() ioloop.stop() # ============================================================ # 注册信号处理(优雅关闭) # ============================================================ for sig in (signal.SIGINT, signal.SIGTERM): try: signal.signal(sig, lambda s, f: ioloop.add_callback_from_signal(_shutdown)) except (ValueError, AttributeError, OSError): pass ioloop.start() # ====================================================================== # 入口 # ====================================================================== if __name__ == '__main__': main()