出自 | hackpython(ID:hackpython)
Flask 是 Python 中有名的輕量級同步 web 框架,在一些開發中,可能會遇到需要長時間處理的任務,此時就需要使用異步的方式來實現,讓長時間任務在后臺運行,先將本次請求的響應狀態返回給前端,不讓前端界面「卡頓」,當異步任務處理好后,如果需要返回狀態,再將狀態返回。
怎么實現呢?
使用線程的方式
當要執行耗時任務時,直接開啟一個新的線程來執行任務,這種方式最為簡單快速。
通過 ThreadPoolExecutor 來實現
from ?time? import ?sleep
from ?concurrent.futures? import ?ThreadPoolExecutor
#?DOCS?https://docs.python.org/3/library/concurrent.futures.html#concurrent.futures.ThreadPoolExecutor
#?創建線程池執行器
executor?=?ThreadPoolExecutor( 2 )
app?=?Flask(__name__)
def ? run_jobs () :
???? #?交由線程去執行耗時任務
????executor.submit(long_task,? 'hello' ,? 123 )
???? return ? 'long?task?running.'
#?耗時任務
def ? long_task (arg1,?arg2) :
????print( "args:?%s?%s!" ?%?(arg1,?arg2))
????sleep( 5 )
????print( "Task?is?done!" )
if ?__name__?==? '__main__' :
????app.run()
pip? install ?redis
from ?celery? import ?Celery
app?=?Flask(__name__)
#?配置
#?配置消息代理的路徑,如果是在遠程服務器上,則配置遠程服務器中redis的URL
app.config[ 'CELERY_BROKER_URL' ]?=? 'redis://localhost:6379/0'
#?要存儲?Celery?任務的狀態或運行結果時就必須要配置
app.config[ 'CELERY_RESULT_BACKEND' ]?=? 'redis://localhost:6379/0'
#?初始化Celery
celery?=?Celery(app.name,?broker=app.config[ 'CELERY_BROKER_URL' ])
#?將Flask中的配置直接傳遞給Celery
celery.conf.update(app.config)
def ? long_task (arg1,?arg2) :
???? #?耗時任務的邏輯
???? return ?result
def ? index () :
????task?=?long_task.delay( 1 ,? 2 )
delay?()?方法是?applyasync?()?方法的快捷方式,applyasync?()?參數更多,可以更加細致的控制耗時任務,比如想要?long_task?()?在一分鐘后再執行
@app.route( '/' ,?methods=[ 'GET' ,? 'POST' ])
def ? index () :
????task?=?long_task.apply_async(args=[ 1 ,? 2 ],?countdown= 60 )
delay?()?與?apply_async?()?會返回一個任務對象,該對象可以獲取任務的狀態與各種相關信息。
@celery.task(bind=True)
def ? long_task ( self ) :
????verb?=?[ 'Starting?up' ,? 'Booting' ,? 'Repairing' ,? 'Loading' ,? 'Checking' ]
????adjective?=?[ 'master' ,? 'radiant' ,? 'silent' ,? 'harmonic' ,? 'fast' ]
????noun?=?[ 'solar?array' ,? 'particle?reshaper' ,? 'cosmic?ray' ,? 'orbiter' ,? 'bit' ]
????message?=? ''
????total?=?random.randint( 10 ,? 50 )
???? for ?i? in ?range(total):
???????? if ? not ?message? or ?random.random()?<? 0 . 25 :
???????????? #?隨機的獲取一些信息
????????????message?=? '{0}?{1}?{2}...' .format(random.choice(verb),
??????????????????????????????????????????????random.choice(adjective),
??????????????????????????????????????????????random.choice(noun))
???????? #?更新Celery任務狀態
???????? self .update_state(state= 'PROGRESS' ,
??????????????????????????meta={ 'current' :?i,? 'total' :?total,
???????????????????????????????? 'status' :?message})
????????time.sleep( 1 )
???? #?返回字典
???? return ?{ 'current' :? 100 ,? 'total' :? 100 ,? 'status' :? 'Task?completed!' ,
???????????? 'result' :? 42 }
def ? longtask () :
???? #?異步調用
????task?=?long_task.apply_async()
???? #?返回?202,與Location頭
???? return ?jsonify({}),? 202 ,?{ 'Location' :?url_for( 'taskstatus' ,
??????????????????????????????????????????????????task_id=task.id)}
def? taskstatus ( task_id ):
????task? =?long_task.AsyncResult(task_id)
???? if ?task.state?==? 'PENDING' :?
????????response?=?{
???????????? 'state' :?task.state,
???????????? 'current' :? 0 ,
???????????? 'total' :? 1 ,
???????????? 'status' :? 'Pending...'
????????}
????elif?task.state?!=? 'FAILURE' :?
????????response?=?{
???????????? 'state' :?task.state,?
????????????
???????????? 'current' :?task.info. get ( 'current' ,? 0 ),?
???????????? 'total' :?task.info. get ( 'total' ,? 1 ),?
???????????? 'status' :?task.info. get ( 'status' ,? '' )
????????}
???????? if ? 'result' ? in ?task.info:
????????????response[ 'result' ]?=?task.info[ 'result' ]
???? else :
????????
????????response?=?{
???????????? 'state' :?task.state,
???????????? 'current' :? 1 ,
???????????? 'total' :? 1 ,
???????????? 'status' :?str(task.info),??
????????}
???? return ?jsonify(response)
????classname:? 'my-class' ,
??id:? 'my-id' ,
?? //?進度條要出現的位置
????target:?document.getElementById( 'myDivId' )
};
//?初始化進度條對象
var ?nanobar?=? new ?Nanobar(?options?);
nanobar. go (? 30 ?);? //?30%?進度條
nanobar. go (? 76 ?);? //?76%?進度條
//?100%?進度條,進度條結束
nanobar. go ( 100 );
< button ? id = "start-bg-job" > Start?Long?Calculation button >< br >< br >
< div ? id = "progress" >div >
$( function ( )? {
???????$( '#start-bg-job' ).click(start_long_task);
???});
//?請求?longtask?接口
function ? start_long_task ( )? {
???????????? //?添加元素在html中
????????????div?=?$( '
' );
????????????$( '#progress' ).append(div);
???????????? //?創建進度條對象
???????????? var ?nanobar?=? new ?Nanobar({
???????????????? bg :? '#44f' ,
???????????????? target :?div[ 0 ].childNodes[ 0 ]
????????????});
???????????? //?ajax請求longtask
????????????$.ajax({
???????????????? type :? 'POST' ,
???????????????? url :? '/longtask' ,
???????????????? //?獲得數據,從響應頭中獲取Location
????????????????success:? function ( data,?status,?request )? {
????????????????????status_url?=?request.getResponseHeader( 'Location' );
???????????????????? //?調用?update_progress()?方法更新進度條
????????????????????update_progress(status_url,?nanobar,?div[ 0 ]);
????????????????},
???????????????? error :? function ( )? {
????????????????????alert( 'Unexpected?error' );
????????????????}
????????????});
????????}
//?更新進度條
function ? update_progress ( status_url,?nanobar,?status_div )? {
???????????? //?getJSON()方法是JQuery內置方法,這里向Location中對應的url發起請求,即請求「/status/
????????????$.getJSON(status_url,? function ( data )? {
???????????????? //?計算進度
????????????????percent?=? parseInt (data[ 'current' ]?*? 100 ?/?data[ 'total' ]);
???????????????? //?更新進度條
????????????????nanobar.go(percent);
???????????????? //?更新文字
????????????????$(status_div.childNodes[ 1 ]).text(percent?+? '%' );
????????????????$(status_div.childNodes[ 2 ]).text(data[ 'status' ]);
???????????????? if ?(data[ 'state' ]?!=? 'PENDING' ?&&?data[ 'state' ]?!=? 'PROGRESS' )?{
???????????????????? if ?( 'result' ? in ?data)?{
???????????????????????? //?展示結果
????????????????????????$(status_div.childNodes[ 3 ]).text( 'Result:?' ?+?data[ 'result' ]);
????????????????????}
???????????????????? else ?{
???????????????????????? //?意料之外的事情發生
????????????????????????$(status_div.childNodes[ 3 ]).text( 'Result:?' ?+?data[ 'state' ]);
????????????????????}
????????????????}
???????????????? else ?{
???????????????????? //?2秒后再次運行
????????????????????setTimeout( function ( )? {
????????????????????????update_progress(status_url,?nanobar,?status_div);
????????????????????},? 2000 );
????????????????}
????????????});?
????????}???????
◆
精彩推薦
◆
-
-
5大必知的圖算法,附Python代碼實現
-
-
如何用爬蟲技術幫助孩子秒到心儀的幼兒園(基礎篇)
-
-
2019年最新華為、BAT、美團、頭條、滴滴面試題目及答案匯總
-
阿里巴巴楊群:高并發場景下Python的性能挑戰
更多文章、技術交流、商務合作、聯系博主
微信掃碼或搜索:z360901061

微信掃一掃加我為好友
QQ號聯系: 360901061
您的支持是博主寫作最大的動力,如果您喜歡我的文章,感覺我的文章對您有幫助,請用微信掃描下面二維碼支持博主2元、5元、10元、20元等您想捐的金額吧,狠狠點擊下面給點支持吧,站長非常感激您!手機微信長按不能支付解決辦法:請將微信支付二維碼保存到相冊,切換到微信,然后點擊微信右上角掃一掃功能,選擇支付二維碼完成支付。
【本文對您有幫助就好】元
