阅读是全球性的吗收藏.deque从一个瓶子里请求保险箱?

2024-04-26 12:52:58 发布

您现在位置:Python中文网/ 问答频道 /正文

我有一个Flask应用程序,它应该在指定的路径上向用户显示长时间运行的函数的结果。结果大约每小时就会改变一次。为了避免用户等待结果,我希望将其缓存在应用程序中的某个位置,并在后台以特定的时间间隔(例如每小时)重新计算,这样用户请求就不必等待长时间运行的计算函数。你知道吗

我提出的解决这个问题的想法如下所示,但是,我不完全确定在使用多线程甚至多处理的web服务器(如waitresseventletgunicorn)的生产环境中这样做是否真的“安全”。你知道吗

为了在后台重新计算结果,我使用了来自APScheduler libraryBackgroundScheduler。你知道吗

然后将结果附加在collections.deque对象中,该对象被注册为模块范围的变量(因为据我所知,在Flask应用程序中保存应用程序范围的全局变量没有更好的可能性了?!)。由于deque的最大大小被设置为2,所以当新结果出现时,旧结果将在deque的右侧弹出。你知道吗

Flask视图现在将deque[0]返回给请求者,请求者应该始终是最新的结果。我决定使用deque而不是Queue,因为后者没有内在的可能性来读取第一项而不删除它。你知道吗

因此,可以保证没有用户需要等待结果,因为旧的结果只会在新的结果出现的那一刻从“缓存”中消失。你知道吗

下面是一个简单的例子。当运行脚本并点击http://localhost:5000时,可以看到缓存正在运行-“Job finished at”不应晚于10秒,再加上在“Current time”之后重新计算它的一些非常短的时间,仍然不应等待Job函数的time.sleep(5)秒,直到请求返回。你知道吗

对于给定的需求,这是一个有效的实现,它也可以在生产就绪的WSGI服务器设置中工作,还是应该以不同的方式实现?你知道吗

from flask import Flask
from apscheduler.schedulers.background import BackgroundScheduler
import time
import datetime
from collections import deque

# a global deque that is filled by APScheduler and read by a Flask view
deque = deque(maxlen=2)

# a function filling the deque that is executed in regular intervals by APScheduler
def some_long_running_job():
    print('complicated long running job started...')
    time.sleep(5)
    job_finished_at = datetime.datetime.now()
    deque.appendleft(job_finished_at)

# a function setting up the scheduler
def start_scheduler():
    scheduler = BackgroundScheduler()
    scheduler.add_job(some_long_running_job,
                      trigger='interval',
                      seconds=10,
                      next_run_time=datetime.datetime.utcnow(),
                      id='1',
                      name='Some Job name'
                      )
    scheduler.start()

# a flask application
app = Flask(__name__)

# a flask route returning an item from the global deque
@app.route('/')
def display_job_result():
    current_time = datetime.datetime.now()
    job_finished_at = deque[0]

    return '''
        Current time is: {0} <br>
        Job finished at: {1}
        '''.format(current_time, job_finished_at)

# start the scheduler and flask server
if __name__ == '__main__':
    start_scheduler()
    app.run()

Tags: the用户fromimport应用程序flaskdatetimetime
1条回答
网友
1楼 · 发布于 2024-04-26 12:52:58

如果运行多个进程,线程安全是不够的:

即使^{}是线程安全的:

Deques support thread-safe, memory efficient appends and pops from either side of the deque with approximately the same O(1) performance in either direction.

资料来源:https://docs.python.org/3/library/collections.html#collections.deque

根据您的配置,您的web服务器可能在多个进程中运行多个worker,因此每个进程都有自己的对象实例。你知道吗


即使只有一个工作线程,线程安全也可能不够:

您可能已经选择了异步工作程序类型。异步工作者不知道何时可以安全地屈服,因此必须保护您的代码不受以下情况的影响:

  1. 请求1的工作进程读取值a,并产生
  2. 请求2的工作进程还读取值a,写入a + 1,并生成
  3. 请求1的工作进程写入值a + 1,即使它应该是a + 1 + 1

可能的解决方案:

使用Flask应用程序之外的东西来存储数据。这可以是一个数据库,在这种情况下,最好是像Redis这样的内存数据库。或者,如果工作进程类型与multiprocessing模块兼容,则可以尝试使用multiprocessing.managers.BaseManager为所有工作进程提供Python对象。你知道吗

相关问题 更多 >