থ্রেডিং অন্য সম্ভাব্য সমাধান। যদিও সেলারি ভিত্তিক সমাধান মাপের অ্যাপ্লিকেশনগুলির জন্য ভাল, যদি আপনি প্রশ্নের শেষ প্রান্তে খুব বেশি ট্র্যাফিকের প্রত্যাশা না করেন তবে থ্রেডিং একটি কার্যকর বিকল্প।
এই দ্রবণটি মিগুয়েল গ্রিনবার্গের পাইকন 2016 ফ্লাস্ক এ স্কেল উপস্থাপনার উপর ভিত্তি করে তৈরি করা হয়েছে , বিশেষত তার স্লাইড ডেকে 41 স্লাইড। আসল উত্সে আগ্রহী ব্যক্তিদের জন্য তাঁর কোডটি গিথুবেও উপলব্ধ ।
একটি ব্যবহারকারীর দৃষ্টিকোণ থেকে কোড নিম্নলিখিত হিসাবে কাজ করে:
- আপনি শেষ পয়েন্টে কল করেন যা দীর্ঘদিনের কাজটি সম্পাদন করে।
- এই শেষ পয়েন্টটি 202 টাস্কের স্থিতি পরীক্ষা করার জন্য একটি লিঙ্ক সহ স্বীকৃত প্রদান করে।
- স্থিতি লিঙ্কে কলগুলি যখন টাকগুলি এখনও চলছে তখন 202 ফেরত দেয় এবং 200 টি (এবং ফলাফল) কাজ শেষ হয়ে গেলে ফিরে আসে।
একটি এপিআই কলকে একটি পটভূমির কাজে রূপান্তর করতে, কেবলমাত্র @async_api ডেকরেটার যুক্ত করুন।
এখানে একটি সম্পূর্ণরূপে উদাহরণ রয়েছে:
from flask import Flask, g, abort, current_app, request, url_for
from werkzeug.exceptions import HTTPException, InternalServerError
from flask_restful import Resource, Api
from datetime import datetime
from functools import wraps
import threading
import time
import uuid
tasks = {}
app = Flask(__name__)
api = Api(app)
@app.before_first_request
def before_first_request():
"""Start a background thread that cleans up old tasks."""
def clean_old_tasks():
"""
This function cleans up old tasks from our in-memory data structure.
"""
global tasks
while True:
five_min_ago = datetime.timestamp(datetime.utcnow()) - 5 * 60
tasks = {task_id: task for task_id, task in tasks.items()
if 'completion_timestamp' not in task or task['completion_timestamp'] > five_min_ago}
time.sleep(60)
if not current_app.config['TESTING']:
thread = threading.Thread(target=clean_old_tasks)
thread.start()
def async_api(wrapped_function):
@wraps(wrapped_function)
def new_function(*args, **kwargs):
def task_call(flask_app, environ):
with flask_app.request_context(environ):
try:
tasks[task_id]['return_value'] = wrapped_function(*args, **kwargs)
except HTTPException as e:
tasks[task_id]['return_value'] = current_app.handle_http_exception(e)
except Exception as e:
tasks[task_id]['return_value'] = InternalServerError()
if current_app.debug:
raise
finally:
tasks[task_id]['completion_timestamp'] = datetime.timestamp(datetime.utcnow())
task_id = uuid.uuid4().hex
tasks[task_id] = {'task_thread': threading.Thread(
target=task_call, args=(current_app._get_current_object(),
request.environ))}
tasks[task_id]['task_thread'].start()
print(url_for('gettaskstatus', task_id=task_id))
return 'accepted', 202, {'Location': url_for('gettaskstatus', task_id=task_id)}
return new_function
class GetTaskStatus(Resource):
def get(self, task_id):
"""
Return status about an asynchronous task. If this request returns a 202
status code, it means that task hasn't finished yet. Else, the response
from the task is returned.
"""
task = tasks.get(task_id)
if task is None:
abort(404)
if 'return_value' not in task:
return '', 202, {'Location': url_for('gettaskstatus', task_id=task_id)}
return task['return_value']
class CatchAll(Resource):
@async_api
def get(self, path=''):
print("starting processing task, path: '%s'" % path)
time.sleep(10)
print("completed processing task, path: '%s'" % path)
return f'The answer is: {path}'
api.add_resource(CatchAll, '/<path:path>', '/')
api.add_resource(GetTaskStatus, '/status/<task_id>')
if __name__ == '__main__':
app.run(debug=True)