intrabot/gunicorn.conf.py
2026-03-15 21:36:03 +03:00

71 lines
3.4 KiB
Python

"""
Конфигурация Gunicorn для продакшена.
Архитектура:
- 1 воркер (workers=1) — обязательно, т.к.:
* SQLite не поддерживает несколько независимых процессов без WAL
* SSE event bus (events.py) хранит состояние в памяти одного процесса
* Notifier (poster.py) инициализируется один раз в памяти воркера
- gthread: несколько потоков внутри воркера — позволяет обслуживать
одновременно SSE-поток дашборда + вебхуки + API-запросы
- post_fork: стартует Telegram-бот и шедулер внутри воркера (не в мастере)
Локальная разработка: python intradbot.py (Flask dev server)
Продакшен (Docker): gunicorn -c gunicorn.conf.py "web.server:create_app()"
"""
import os
import threading
import time
# ── Сервер ────────────────────────────────────────────────────────────────────
bind = f"0.0.0.0:{os.environ.get('PORT', '8080')}"
workers = 1 # строго 1 — см. комментарий выше
worker_class = "gthread" # многопоточный воркер для SSE + вебхуков одновременно
threads = 4 # потоков: SSE-клиенты + API-запросы + запас
timeout = 120 # достаточно для долгих SSE-соединений
keepalive = 5
# ── Логирование ───────────────────────────────────────────────────────────────
accesslog = "-" # stdout
errorlog = "-" # stderr
loglevel = "info"
access_log_format = '%(h)s "%(r)s" %(s)s %(b)s %(D)sµs'
# ── Хуки жизненного цикла ────────────────────────────────────────────────────
def on_starting(server):
"""Инициализация до форка воркеров (выполняется в мастер-процессе)."""
import config
import database
from intradesk import reference
os.makedirs(os.path.dirname(config.DB_FILE), exist_ok=True)
database.init_db()
reference.seed_static()
reference.load_all()
server.log.info("Database and reference data initialized.")
def post_fork(server, worker):
"""
Выполняется в каждом воркер-процессе после fork.
Запускает Telegram-бот и шедулер как daemon-потоки внутри воркера.
"""
import schedule
from intradesk import reference
from bot.telegram import start_bot
threading.Thread(target=start_bot, daemon=True, name="telegram-bot").start()
schedule.every().monday.at("10:00").do(reference.load_all)
def _run_scheduler():
while True:
schedule.run_pending()
time.sleep(1)
threading.Thread(target=_run_scheduler, daemon=True, name="scheduler").start()
worker.log.info("Telegram bot and scheduler started in worker.")