來源商業新知網,原標題:十面九問的逆天異步神器-celery,你了解多少?
celery是什么,有什么用?
對於celery,各種文檔解釋一堆,可以自己看下,根據日常互動,公眾號用戶小白居多,官方用到的很多名詞可能你理解起來不是那么容易,我這里就不照本宣科了,就以我這邊的理解簡單以下方圖示解釋下,如果有誤,歡迎指正:
首先理解一個概念: 阻塞
理解阻塞,先從耗時操作講起,常見的場景比如用戶輸入、等待(sleep)這些都屬於,具體到實際項目中比如說發郵件、發短信、機器/深度學習訓練模型、自動化測試中的各種操作流程等待都會產生耗時,耗時操作通常我們也會稱之為阻塞,意思就是程序一行一行代碼執行,你這些耗時操作沒執行完畢,后面的代碼就不會執行,阻塞了后面的代碼執行
理解了阻塞以后,就很容易引出一個問題?我如果想提高程序執行效率不讓程序阻塞,那該怎么辦?有沒有一種方式,能在程序阻塞的時候,不影響我后面代碼的執行?
一般來說,我們可以用線程、進程、協程,都是可以實現的,只不過在實際項目中,有成熟開源並且廣泛應用的的東西,我們會用框架,而不去手寫多線程、進程、協程,也不是說這些不用去學,這是編程的基本知識,也是必須掌握的,很能體現編程基本功底
celery基本原理:
1、客戶端也就是python(django/flask等)發布任務
2、發布的任務存到任務隊列里面,可以以redis、rabbitMQ、MessageQueue、MySQL存儲,一般在django/flask程序里redis居多
3、任務處理者會不斷從任務隊列里面獲取任務執行
1、安裝django/celery庫
django==2.0.6
celery==3.1.26
django-celery==3.3.0
redis==2.10.6
都用pip安裝,我這邊這幾個版本測試沒問題,版本有些時候不兼容,如果報錯,網上找下解決
2、redis數據庫安裝
redis安裝:http://www.python88.cn/book/redis10/
注意以下幾點:
1、redis.conf改bind127.0.0.1為bind 真實ip,這樣可以遠程訪問
2、如果是阿里雲服務器,需要在控制台將6379端口開放
3、redis啟動服務端:redis-server,啟動客戶端:redis-cli
3、演示代碼demo
整體流程是在前面頁面點擊表格里面的執行,會用selenium打開瀏覽器,執行耗時操作,相當於每個執行都是一個單獨耗時任務
4、demo前端代碼:
給按鈕綁定run_case2方法,獲取當前表格行號(映射要用selenium打開的網站鏈接),作為參數傳到后端
function run_case2(obj){
row_tr = obj.parentNode.parentNode.rowIndex;//獲取當前行數
console.log(row_tr);
param = {"url":row_tr};
$.post('/web/yzm/', param, function (data) {
console.log(data.result)
})
}
5、demo后端代碼:(阻塞寫法)
看下面代碼,前端傳的行號,我這只是為了測試傳參數,隨便構造的,分別跟3個網址進行映射,對於下面的這種寫法,就是典型的阻塞線程,webdriver在執行的時候,只有當把quit()關閉瀏覽器執行完畢時候,才會return返回結果並在控制台打印success,如下圖演示,我點擊的是第三個執行按鈕
from selenium import webdriver
import time
@csrf_exempt
@login_required
def yzm(request):
url_num = request.POST.get("url")
if int(url_num)==1:
url_str = "https://www.baidu.com"
elif int(url_num==2:
url_str = "http://www.python66.cn"
elif int(url_num)==3:
url_str = "http://www.python88.cn"
print(url_num,url_str)
# 下面webdriver打開網站,並休眠5秒鍾都是耗時任務
driver = webdriver.Chrome(executable_path="C:chromedriver_win32chromedriver.exe")
driver.get(url_str)
time.sleep(5)
driver.quit()
# 耗時任務執行完畢開始return
return JsonResponse({"result": "success"})
6、demo后端代碼:(celery異步)
異步: 名字雖然為異步,你可以理解為同步,就是一邊做耗時操作,一般執行后面代碼,兩者同時執行
文件目錄,我在項目目錄下面建立了celery_task包,創建tasks.py文件
1、創建app,代表一個celery對象,broker代表隊列,用的redis 0號數據庫
2、然后將上一步我們selenium打開瀏覽器的方法封裝成open_url方法
3、@app.task代表定義任務,指明這個open_url方法是一個任務,可以在視圖里面調用發布
from celery import Celery
from selenium import webdriver
import time
# 創建celery的應用
app = Celery("celery_task", broker="redis://47.101.203.45:6379/0")
@app.task
def open_url(url):
driver = webdriver.Chrome(executable_path="C:chromedriver_win32chromedriver.exe")
driver.get(url)
time.sleep(5)
driver.quit()
views.py視圖里面這樣寫
1、from celery_task.tasks import open_url為導入任務
2、open_url.delay(url_str)為發布任務,其中delay里面可以傳參數,你前端傳過來的參數views.py視圖函數接收,可以再傳到celery任務中去
效果圖如下:當點擊執行時候,return的success很快就返回並打印了,跟我操控瀏覽器的過程沒一點牽涉,無需等待selenium,這樣就實現了非阻塞異步,完美解決了耗時問題
from celery_task.tasks import open_url
@csrf_exempt
@login_required
def yzm2(request):
url_num = request.POST.get("url")
if int(url_num)==1:
url_str = "https://www.baidu.com"
elif int(url_num)==2:
url_str = "http://www.python66.cn"
elif int(url_num)==3:
url_str = "http://www.python88.cn"
print(url_num,url_str)
open_url.delay(url_str)
return JsonResponse({"result":"success"})
7、多個任務同時執行
對於多個任務同時執行,我這里連續點擊三個執行按鈕,立馬先打印了3個success,說明3個任務都被celery異步處理了,對於多任務的參數問題,上面也說了,我們在delay里面傳入參數即可,傳到定義任務的tasks.py文件里面的對於方法中去,本例中我演示的是傳了一個url_str參數
8、啟動程序
打開兩個終端,都切換到項目目錄下面
1、先啟動celery:
celery -A celery_task.tasks worker -l info
2、再啟動django:
python manage.py runserver
本文重要的是理解原理,celery的牛逼遠不止於此,有興趣可以看看在爬蟲、機器學習、深度學習領域的使用,會對celery的使用場景有更清晰的認識