You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Streamlit交互式仪表盘缓存优化及代码专业度提升技术咨询

Streamlit Pay & Ship 预测仪表盘优化方案

针对你当前的实现,以下是关于缓存刷新方案、异步适配及OOP重构的具体优化建议:


一、缓存刷新方案:线程 vs Asyncio 与全局单例优化

1. 当前线程实现的问题

现有线程方案存在多会话重复任务的隐患:Streamlit每个用户会话都会执行main函数,导致创建多个refresh_cache线程,重复触发缓存清除,造成资源浪费。此外,线程未设置守护属性,Streamlit重启时可能残留僵尸线程。

2. Asyncio的适配价值

Streamlit 1.22+版本原生支持异步函数,用asyncio实现定时任务可减少线程切换开销,但需注意:

  • 不能直接用while True + asyncio.sleep循环,需结合会话状态避免重复创建任务。
  • 用asyncio.create_task启动异步任务,并通过st.session_state标记任务运行状态。

3. 最优实现:全局单例任务

无论用线程还是异步,核心是确保全局仅一个刷新任务运行:

  • 用st.session_state存储任务运行标记,避免重复创建。
  • 线程模式下设置daemon=True,确保Streamlit退出时线程自动终止。

二、OOP重构:模块化与可维护性提升

将仪表盘核心逻辑封装为类,分离模型管理、缓存刷新、UI渲染等职责,代码结构更清晰:

核心封装思路

  • 模型管理:把MLFlow交互、模型加载逻辑封装为类方法,通过会话状态缓存模型实例。
  • 定时任务:将缓存刷新的时间计算、任务启停封装为独立方法,支持线程/异步切换。
  • 状态控制:用st.session_state统一管理模型实例、任务运行状态,避免全局变量污染。

三、完整优化代码示例

import logging
import threading
import asyncio
from datetime import datetime
from dateutil.relativedelta import relativedelta
import streamlit as st
import mlflow_manager

logging.getLogger().setLevel(logging.INFO)

class ForecastDashboard:
    def __init__(self, experiment_id, bucket_name, mlflow_url):
        self.experiment_id = experiment_id
        self.bucket_name = bucket_name
        self.mlflow_url = mlflow_url
        self._init_session_state()

    def _init_session_state(self):
        # 初始化会话状态,避免重复加载模型和创建任务
        if "model_atv" not in st.session_state:
            st.session_state.model_atv = None
        if "model_tx" not in st.session_state:
            st.session_state.model_tx = None
        if "refresh_task_active" not in st.session_state:
            st.session_state.refresh_task_active = False

    @st.cache_data(ttl=None)
    def _load_models_from_mlflow(self):
        """内部模型加载方法,用Streamlit缓存"""
        mlflow_client = mlflow_manager.MLFlowManager(
            self.experiment_id, self.bucket_name, self.mlflow_url
        )
        mlflow_client.download_artifacts(sub_experiment="atv", destination_folder="data")
        model_tx = mlflow_client.get_model("ps_monthly_forecast_num_txs")
        model_atv = mlflow_client.get_model("ps_monthly_forecast_atv")
        return model_atv, model_tx

    def get_models(self):
        """对外暴露的模型获取接口,自动从缓存或重新加载"""
        if st.session_state.model_atv is None or st.session_state.model_tx is None:
            st.session_state.model_atv, st.session_state.model_tx = self._load_models_from_mlflow()
        return st.session_state.model_atv, st.session_state.model_tx

    def _get_next_refresh_datetime(self):
        """计算下一次缓存刷新时间(每月10日7:00)"""
        now = datetime.now()
        # 判断当前是否已过当月10日7点
        if now.day > 10 or (now.day == 10 and now.hour >= 7):
            # 目标时间为下月10日7点
            target = (now.replace(day=1) + relativedelta(months=2)).replace(
                day=10, hour=7, minute=0, second=0, microsecond=0
            )
        else:
            # 目标时间为当月10日7点
            target = now.replace(day=10, hour=7, minute=0, second=0, microsecond=0)
        return target

    def _cache_refresh_thread(self):
        """线程版缓存刷新逻辑"""
        while st.session_state.refresh_task_active:
            target_time = self._get_next_refresh_datetime()
            time_diff = (target_time - datetime.now()).total_seconds()
            
            if time_diff <= 0:
                # 立即执行缓存清除
                st.cache_data.clear()
                st.session_state.model_atv = None
                st.session_state.model_tx = None
                logging.info("[缓存刷新] 已清除缓存,模型将在下一次请求时重新加载")
                # 避免短时间内重复循环
                threading.Event().wait(60)
                continue
            
            # 等待到目标时间
            threading.Event().wait(time_diff)
            # 执行缓存清除
            st.cache_data.clear()
            st.session_state.model_atv = None
            st.session_state.model_tx = None
            logging.info("[缓存刷新] 已清除缓存,模型将在下一次请求时重新加载")

    async def _cache_refresh_async(self):
        """异步版缓存刷新逻辑"""
        while st.session_state.refresh_task_active:
            target_time = self._get_next_refresh_datetime()
            time_diff = (target_time - datetime.now()).total_seconds()
            
            if time_diff <= 0:
                st.cache_data.clear()
                st.session_state.model_atv = None
                st.session_state.model_tx = None
                logging.info("[缓存刷新] 已清除缓存,模型将在下一次请求时重新加载")
                await asyncio.sleep(60)
                continue
            
            await asyncio.sleep(time_diff)
            st.cache_data.clear()
            st.session_state.model_atv = None
            st.session_state.model_tx = None
            logging.info("[缓存刷新] 已清除缓存,模型将在下一次请求时重新加载")

    def start_refresh_task(self, use_async=False):
        """启动缓存刷新任务,支持线程/异步模式"""
        if not st.session_state.refresh_task_active:
            st.session_state.refresh_task_active = True
            if use_async:
                asyncio.create_task(self._cache_refresh_async())
                logging.info("[任务启动] 异步缓存刷新任务已启动")
            else:
                thread = threading.Thread(target=self._cache_refresh_thread, daemon=True)
                thread.start()
                logging.info("[任务启动] 线程版缓存刷新任务已启动")

    def stop_refresh_task(self):
        """停止缓存刷新任务"""
        st.session_state.refresh_task_active = False
        logging.info("[任务停止] 缓存刷新任务已终止")

def main():
    # 配置参数建议提取到环境变量或配置文件
    EXPERIMENT_ID = "your_experiment_id"
    BUCKET_NAME = "your_bucket_name"
    MLFLOW_URL = "your_mlflow_url"

    # 初始化仪表盘
    dashboard = ForecastDashboard(EXPERIMENT_ID, BUCKET_NAME, MLFLOW_URL)
    # 启动缓存刷新任务(如需异步,设置use_async=True)
    dashboard.start_refresh_task(use_async=False)

    # 获取模型
    model_atv, model_tx = dashboard.get_models()

    # 后续添加UI与预测逻辑
    st.title("Pay & Ship 未来5个月营收与交易预测")
    # ... 此处添加你的仪表盘UI代码 ...

if __name__ == "__main__":
    main()

四、额外优化建议

  • 配置解耦:将EXPERIMENT_ID等参数从代码中分离,用环境变量(如os.getenv)或配置文件(如pydantic-settings)管理,便于部署切换环境。
  • 异常处理:在_load_models_from_mlflow方法中添加try-except块,捕获MLFlow连接失败、模型下载失败等异常,避免任务崩溃。
  • 内存优化:如果模型体积较大,可缓存MLFlow下载的模型文件路径,而非直接缓存模型实例,减少内存占用。
  • 监控增强:添加模型加载耗时、缓存刷新时间等日志指标,便于排查性能问题。

内容的提问来源于stack exchange,提问作者Jorge Gomes

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.02 14:24:55