实验背景
为了缓解后台压力,在前端请求时添加一个缓冲队列用来缓存请求。但是,实验中发现flask框架会在修改代码逻辑内容后进行重启,导致缓存队列清空。为了解决这个问题,在队列清空时将队列中的请求持久化到本地存储中。由此,避免数据的丢失。
代码框架
代码结构:
tree
.
├── bin
│ └── run.py
├── demo.py
├── log
└── stor
├── demo.py
├── __init__.py
└── webapi.py
3 directories, 5 files
run.py
import sys
import os
app_root = os.path.abspath(os.path.join(os.path.dirname(__file__), '..'))
sys.path.append(app_root)
print(app_root)
from stor.webapi import app
from stor.demo import task_consumer
if __name__ == '__main__':
# 启动一个守护线程,用来监测队列中的任务
thr = task_consumer()
app.run(debug = True, threaded = True, host = '0.0.0.0', port = 10800, use_reloader = True)
thr.join()
webapi.py
from flask import Flask, request
app = Flask(__name__)
from demo import task_producer
@app.route('/demo', methods = ['GET'])
def put_task():
tasks = request.values.getlist('tasks', int)
print('tasks: ' + str(tasks))
# 将前端推放进来的数据存放到任务队列中
task_producer(tasks)
return str(tasks)
demo.py
import threading
import time
from Queue import Queue as queue
from functools import wraps
import sys
import os
import fcntl
q = queue()
def task_monitor():
# 监控队列中是否存在待执行任务,若存在则处理(打印),并从队列中取出。
while True:
L = []
if q.qsize() != 0:
L.append(q.get())
q.task_done()
for x in L:
print('x: ' + str(x))
time.sleep(5)
def task_consumer():
# 设置守护线程
thr = threading.Thread(target = task_monitor, args = (), kwargs = {})
thr.setDaemon(True)
thr.start()
print(str(thr))
return thr
def task_producer(tasks):
# 向队列中存放任务
for task in tasks:
q.put(task)
执行结果
这里是简易版的,重启后台进程将导致队列中内容丢失。
# 请求任务1,2
$ curl 0.0.0.0:10800/demo?tasks=1\&tasks=2
# 后台处理任务1,2
tasks: [1, 2]
127.0.0.1 - - [03/Aug/2020 16:04:45] "GET /demo?tasks=1&tasks=2 HTTP/1.1" 200 -
x: 1
x: 2
# 请求任务1,2
# 在处理1的过程,flask后台重启
$ tasks: [1, 2]
127.0.0.1 - - [03/Aug/2020 16:05:53] "GET /demo?tasks=1&tasks=2 HTTP/1.1" 200 -
x: 1
* Detected change in '/root/flask/myproject/stor/demo.py', reloading
* Restarting with stat
/root/flask/myproject
<Thread(Thread-1, started daemon 140206898276096)>
* Debugger is active!
* Debugger PIN: 374-140-162
# 任务2丢失
添加缓存任务持久化逻辑
缓存队列持久化,主要是对共享队列在初始化与析构时,存储缓存队列中的内容。具体的,flask后台重启的逻辑会启动垃圾回收机制,将代码逻辑中的数据清空。若在队列清空时也就是被垃圾回收时能够将数据持久化到文件中,那么数据将被保留下来而避免了丢失。
具体实现中,将缓存队列Queue封装到类中。在初始化对象时,查看是否拥有为加载的文件,在析构时将缓存队列中的数据持久化的文件中。
import threading
import time
from Queue import Queue as queue
from functools import wraps
import sys
import os
import fcntl
class Cache:
def __init__(self):
self.q = queue()
self.root_path = os.path.join(os.path.dirname(os.path.realpath(__file__)), '..')
self.cache_path = os.path.dirname(os.path.join(self.root_path, 'log/'))
self.cache_name = os.path.join(self.cache_path, 'taskid.json')
def Queue(self):
return self.q
def check(self):
if(os.path.exists(self.cache_name)):
with open(self.cache_name, 'r') as f:
for task in f.readlines():
print('get task: ' + str(task))
self.q.put(task.strip())
os.remove(self.cache_name)
def __del__(self):
with open(self.cache_name, 'a') as f:
while(0 < self.q.qsize()):
task = self.q.get()
print('put task: ' + str(task))
f.write(str(task) + '\n')
self.q.task_done()
task = Cache()
q = task.Queue()
#q = queue()
def task_monitor():
task.check()
while True:
L = []
if q.qsize() != 0:
L.append(q.get())
q.task_done()
for x in L:
print('x: ' + str(x))
time.sleep(5)
def task_consumer():
thr = threading.Thread(target = task_monitor, args = (), kwargs = {})
thr.setDaemon(True)
thr.start()
print(str(thr))
return thr
def task_producer(tasks):
for task in tasks:
q.put(task)
执行任务
# 发起请求
$ curl 0.0.0.0:10800/demo?tasks=1\&tasks=2
# 执行结果
tasks: [1, 2]
127.0.0.1 - - [03/Aug/2020 16:42:00] "GET /demo?tasks=1&tasks=2 HTTP/1.1" 200 -
tasks: [3, 4]
127.0.0.1 - - [03/Aug/2020 16:42:05] "GET /demo?tasks=3&tasks=4 HTTP/1.1" 200 -
* Detected change in '/root/flask/myproject/stor/demo.py', reloading
put task: 1
put task: 2
put task: 3
put task: 4
* Restarting with stat
/root/flask/myproject
get task: 1
get task: 2
get task: 3
get task: 4
x: 1
# 重启后台进程
<Thread(Thread-1, started daemon 140067621132032)>
* Debugger is active!
* Debugger PIN: 374-140-162
* Detected change in '/root/flask/myproject/stor/demo.py', reloading
put task: 2
put task: 3
put task: 4
* Restarting with stat
/root/flask/myproject
get task: 2
get task: 3
get task: 4
x: 2
<Thread(Thread-1, started daemon 139786904717056)>
* Debugger is active!
* Debugger PIN: 374-140-162
* Detected change in '/root/flask/myproject/stor/demo.py', reloading
put task: 3
put task: 4
* Restarting with stat
/root/flask/myproject
get task: 3
get task: 4
x: 3
<Thread(Thread-1, started daemon 140183869146880)>
* Debugger is active!
* Debugger PIN: 374-140-162
* Detected change in '/root/flask/myproject/stor/demo.py', reloading
put task: 4
* Restarting with stat
/root/flask/myproject
get task: 4
x: 4
<Thread(Thread-1, started daemon 140620198328064)>
* Debugger is active!
总结
仍旧有很多思路去解决这个问题,使用装饰器,使用进程。还待实践。