一.頻率限制短信接口html
settings/dev.py:前端
# drf配置 REST_FRAMEWORK = { # Throttling 'DEFAULT_THROTTLE_RATES': { 'user': None, 'anon': None, 'sms': '1/min' }, }
apps/user/throttles.py:vue
from rest_framework.throttling import SimpleRateThrottle class SMSRateThrottle(SimpleRateThrottle): scope = 'sms' def get_cache_key(self, request, view): # 針對手機號進行限制 mobile = request.data.get('mobile') or request.query_params.get('mobile') if not mobile: return None return self.cache_format % { 'scope': self.scope, 'ident': mobile }
user/views.py:ios
from libs.txm import get_code, send_sms from django.core.cache import cache from .throttles import SMSRateThrottle from . import models, serializers from settings.const import SMS_CODE_EXC class SMSAPIView(APIView): throttle_classes = [SMSRateThrottle] def post(self, request, *args, **kwargs): # 拿到前端的手機號 mobile = request.data.get('mobile') # 完成手機號的校驗(是否合法) if not mobile or not re.match(r'^1[3-9][0-9]{9}$', mobile): return APIResponse(1, '手機號有誤') # 產生驗證碼 code = get_code() # 通知第三方發送短信 result = send_sms(mobile, code, SMS_CODE_EXC // 60) # 失敗:響應前臺發送失敗 if not result: return APIResponse(1, '短信發送失敗') # 成功:緩存驗證碼(校驗備用),響應前臺發送成功 cache.set('%s_code' % mobile, code, SMS_CODE_EXC) # print(cache.get('%s_code' % mobile)) return APIResponse(0, '短信發送成功')
二.手機號驗證接口redis
前臺手機號校驗register.vue:vue-router
methods: { checkMobile() { if (this.mobile.length < 1) { return false; } // 手機號碼格式是否正確 if (!/^1[3-9]\d{9}$/.test(this.mobile)) { this.$message({ message: "對不起!手機號碼格式有誤!" }); return false; } // 驗證手機號碼是否已經註冊了 this.$axios({ url: this.$settings.base_url + '/user/mobile/', method: 'get', params: { mobile: this.mobile } }).then(response => { let data = response.data; // window.console.log(data); if (data.status != 0) { this.$message({ message: "對不起!手機號碼已經被註冊!", duration: 1000 }); return false; } else { this.$message({ message: "期待您加入咱們!", duration: 1000, // onClose: () => { // console.log('信息框關閉了') // } }); } }).catch(error => { let data = error.response.data; this.$message({ message: data.message }) }) }, ...
後臺手機號校驗:數據庫
user/urlsdjango
from django.urls import path from . import views urlpatterns = [ path('sms/', views.SMSAPIView.as_view()), path('mobile/', views.MobileAPIView.as_view()), ]
user/views.pyaxios
class MobileAPIView(APIView): def get(self, request, *args, **kwargs): # 獲取手機號 mobile = request.query_params.get('mobile') if not mobile: return APIResponse(1, '手機號必須提供') # 校驗手機號是否合法 if not re.match(r'^1[3-9][0-9]{9}$', mobile): return APIResponse(1, '手機號有誤') # 校驗手機號是否存在 try: models.User.objects.get(mobile=mobile) return APIResponse(1, '手機號已註冊') except: return APIResponse(0, '手機號未註冊')
三.註冊接口實現(用戶名是手機號)windows
settings/const.py:
# 驗證碼過時時間(s),替換過時時間 SMS_CODE_EXC = 300000
前臺發送短信修訂register.vue:
send_sms() { // 發送短信 if (!/^1[3-9]\d{9}$/.test(this.mobile)) { this.$message({ message: "對不起!手機號碼格式有誤!" }); return false; } // 判斷是否在60s內發送太短信 if (this.is_send) { this.$message({ message: "對不起,不能頻繁發送短信驗證!" }); return false; } // 請求發送短信 this.$axios({ url: this.$settings.base_url + '/user/sms/', method: 'post', data: { mobile: this.mobile } }).then(response => { this.$message({ message: response.data.msg, }); // 修改短信的發送狀態 this.is_send = true; // 設置間隔時間60s let sms_interval_time = 60; // 設置短信發送間隔倒計時,.60s後把is_send改爲false let timer = setInterval(() => { if (sms_interval_time <= 1) { clearInterval(timer); this.sms_interval_tips = "獲取驗證碼"; this.is_send = false; // 從新回覆點擊發送功能的條件 } else { sms_interval_time -= 1; this.sms_interval_tips = `${sms_interval_time}秒後再次獲取`; } }, 1000); }).catch(error => { this.$message({ message: error.response.data.result, }) }); },
後臺註冊接口:
user/ url.py
from django.urls import path from . import views urlpatterns = [ path('sms/', views.SMSAPIView.as_view()), path('mobile/', views.MobileAPIView.as_view()), path('register/', views.RegisterAPIView.as_view()), path('login/', views.LoginAPIView.as_view()), path('login/mobile/', views.LoginMobileAPIView.as_view()), ]
views.py
class RegisterAPIView(APIView): def post(self, request, *args, **kwargs): # 將請求數據 手機密碼驗證碼 => 帳號手機密碼驗證碼 request_data = request.data request_data['username'] = request_data.get('mobile') # 校驗 user_ser = serializers.UserModelSerializer(data=request_data) if user_ser.is_valid(): user_ser.save() return APIResponse(0, '註冊成功', results=user_ser.data) else: return APIResponse(1, '註冊失敗', results=user_ser.errors)
序列化類user/serializers.py
from rest_framework import serializers from . import models from django.core.cache import cache import re class UserModelSerializer(serializers.ModelSerializer): code = serializers.CharField(write_only=True, min_length=4, max_length=4) class Meta: model = models.User fields = ('username', 'password', 'mobile', 'code') extra_kwargs = { 'password': { 'write_only': True } } def validate_mobile(self, value): if not re.match(r'^1[3-9][0-9]{9}$', value): raise serializers.ValidationError('手機號有誤') return value def validate(self, attrs): code = attrs.pop('code') mobile = attrs.get('mobile') old_code = cache.get('%s_code' % mobile) if code != old_code: raise serializers.ValidationError({'驗證碼': '驗證碼有誤'}) return attrs def create(self, validated_data): return models.User.objects.create_user(**validated_data)
#重寫create方法,密碼爲密文
注.手機號註冊不能相同,models.py中將mobile字段加一個unique=true
註冊前臺register.vue:
registerMobile() { // 註冊信息提交 if (!/^1[3-9]\d{9}$/.test(this.mobile)) { this.$message({ message: "對不起!手機號碼格式有誤!" }); return false; } if (this.sms.length < 1) { this.$message({ message: "短信驗證碼不能爲空!" }); return false; } if (this.password.length < 6 || this.password.length > 16) { this.$message({ message: "對不起,密碼長度必須在6-16個字符之間!" }); return false; } this.$axios({ url: this.$settings.base_url + '/user/register/', method: 'post', data: { mobile: this.mobile, password: this.password, code: this.sms } }).then(response => { let status = response.data.status; let msg = response.data.msg; if (status == 0) { this.$message({ message: msg, duration: 1000, onClose: () => { this.$router.push('/login') } }); } else { // 實際根據錯誤信息,提示錯誤的具體輸入框 this.mobile = ''; this.password = ''; this.sms = ''; this.$message({ message: msg, duration: 1000, }); } }).catch(error => { this.$message({ message: error.response.data.result }); }) } },
四.登陸接口的實現
兩種登陸路由:
from django.urls import path from . import views urlpatterns = [ path('sms/', views.SMSAPIView.as_view()), path('mobile/', views.MobileAPIView.as_view()), path('register/', views.RegisterAPIView.as_view()), path('login/', views.LoginAPIView.as_view()), path('login/mobile/', views.LoginMobileAPIView.as_view()), ]
user/views
首先安裝jwt: pip install djangorestframework-jwt
from rest_framework_jwt.serializers import jwt_encode_handler, jwt_payload_handler class LoginAPIView(APIView): authentication_classes = () permission_classes = () def post(self, request, *args, **kwargs): # username可能攜帶的不止是用戶名,可能仍是用戶的其它惟一標識 手機號 郵箱 username = request.data.get('username') password = request.data.get('password') if not (username and password): return APIResponse(1, '帳號密碼必須') # 若是username匹配上手機號正則 => 多是手機登陸 if re.match(r'^1[3-9][0-9]{9}$', username): try: # 手動經過 user 簽發 jwt-token user = models.User.objects.get(mobile=username) except: return APIResponse(1, '該手機未註冊') # 郵箱登陸 等 # 帳號登陸 else: try: # 手動經過 user 簽發 jwt-token user = models.User.objects.get(username=username) except: return APIResponse(1, '該帳號未註冊') # 得到用戶後,校驗密碼並簽發token if not user.check_password(password): return APIResponse(1, '密碼錯誤') payload = jwt_payload_handler(user) token = jwt_encode_handler(payload) return APIResponse(0, 'ok', results={ 'username': user.username, 'mobile': user.mobile, 'token': token }) class LoginMobileAPIView(APIView): authentication_classes = () permission_classes = () def post(self, request, *args, **kwargs): # 手機號獲取並校驗 mobile = request.data.get('mobile') code = request.data.get('code') if not (mobile and code): return APIResponse(1, '手機號與驗證碼必須') # 驗證碼獲取並校驗 old_code = cache.get('%s_code' % mobile) # 驗證碼實際開發只能夠使用一次 # cache.set('%s_code' % mobile, '0000', 1) if code != old_code: return APIResponse(1, '驗證碼錯誤') # 經過手機號獲取用戶 try: user = models.User.objects.get(mobile=mobile) except: return APIResponse(1, '該帳號未註冊') # 簽發token payload = jwt_payload_handler(user) token = jwt_encode_handler(payload) return APIResponse(0, 'ok', results={ 'username': user.username, 'mobile': user.mobile, 'token': token })
登陸前臺login.vue:
<template> <div class="login box"> <img src="@/assets/img/Loginbg.jpg" alt=""> <div class="login"> <div class="login-title"> <img src="@/assets/img/Logotitle.png" alt=""> <p>幫助有志向的年輕人經過努力學習得到體面的工做和生活!</p> </div> <div class="login_box"> <div class="title"> <span :class="{active: a0}" @click="changeLogin(0)">密碼登陸</span> <span :class="{active: a1}" @click="changeLogin(1)">短信登陸</span> </div> <div class="inp" v-if="login_type==0"> <input v-model="username" type="text" placeholder="用戶名 / 手機號碼" class="user"> <input v-model="password" type="password" name="" class="pwd" placeholder="密碼"> <div id="geetest1"></div> <div class="rember"> <p> <input id="checkbox" type="checkbox" class="no" v-model="remember"/> <span>記住密碼</span> </p> <p>忘記密碼</p> </div> <button class="login_btn" @click="loginAction">登陸</button> <p class="go_login">沒有帳號 <router-link to="/register">當即註冊</router-link> </p> </div> <div class="inp" v-show="login_type==1"> <input v-model="mobile" type="text" placeholder="手機號碼" class="user"> <div class="sms"> <input v-model="sms" type="text" placeholder="輸入驗證碼" class="user"> <span class="sms_btn" @click="send_sms">{{sms_interval_tips}}</span> </div> <button class="login_btn" @click="loginMobile">登陸</button> <p class="go_login">沒有帳號 <router-link to="/register">當即註冊</router-link> </p> </div> </div> </div> </div> </template> <script> export default { name: 'Login', data() { return { a0: 1, a1: 0, login_type: 0, username: "", password: "", remember: false, mobile: "", sms: "", is_send: false, // 是否在60s內發送了短信 sms_interval_tips: "獲取驗證碼", } }, created() { // 攔截已登陸狀況 let token = this.$cookies.get('token'); if (token) { this.$message({ message: '無需重複登陸', duration: 1000, onClose: () => { this.$router.go(-1) } }) } }, methods: { changeLogin(i) { this.login_type = i; if (i) { this.a0 = 0; this.a1 = 1; } else { this.a0 = 1; this.a1 = 0; } }, loginAction() { if (!this.username || !this.password) { return } this.$axios({ url: this.$settings.base_url + '/user/login/', method: 'post', data: { 'username': this.username, 'password': this.password } }).then((response) => { // 判斷用戶是否要記住密碼 if (this.remember) { // 記住密碼,永存 sessionStorage.clear(); localStorage.token = response.data.results.token; localStorage.username = response.data.results.username; localStorage.mobile = response.data.results.mobile; } else { /// 沒記住密碼,暫存 localStorage.clear(); sessionStorage.token = response.data.results.token; sessionStorage.username = response.data.results.username; sessionStorage.mobile = response.data.results.mobile; } if (response.data.status == 0) { let token = response.data.results.token; this.$cookies.set('token', token, 24 * 60 * 60); let username = response.data.results.username; this.$cookies.set('username', username, 24 * 60 * 60); this.$alert("歡迎回來!", "登陸成功!", { confirmButtonText: '肯定', callback: () => { // 登陸成功去主頁 this.$router.push("/"); } }) } else { this.username = ''; this.password = ''; } }).catch(() => { this.$alert("檢查帳號密碼!", "登陸失敗!", { confirmButtonText: '肯定', callback: () => { this.username = ''; this.password = ''; } }); }) }, send_sms() { // 發送短信 if (!/^1[3-9]\d{9}$/.test(this.mobile)) { this.$message({ message: "對不起!手機號碼格式有誤!" }); return false; } // 判斷是否在60s內發送太短信 if (this.is_send) { this.$message({ message: "對不起,不能頻繁發送短信驗證!" }); return false; } // 請求發送短信 this.$axios({ url: this.$settings.base_url + '/user/sms/', method: 'post', data: { mobile: this.mobile } }).then(response => { let msg = response.data.result; this.$message({ message: msg, }); if (msg === '短信發送失敗') return; // 修改短信的發送狀態 this.is_send = true; // 設置間隔時間60s let sms_interval_time = 60; // 設置短信發送間隔倒計時,.60s後把is_send改爲false let timer = setInterval(() => { if (sms_interval_time <= 1) { clearInterval(timer); this.sms_interval_tips = "獲取驗證碼"; this.is_send = false; // 從新回覆點擊發送功能的條件 } else { sms_interval_time -= 1; this.sms_interval_tips = `${sms_interval_time}秒後再次獲取`; } }, 1000); }).catch(error => { this.$message({ message: error.response.data.result, }) }); }, loginMobile() { // 註冊信息提交 if (!/^1[3-9]\d{9}$/.test(this.mobile)) { this.$message({ message: "對不起!手機號碼格式有誤!" }); return false; } if (this.sms.length < 1) { this.$message({ message: "短信驗證碼不能爲空!" }); return false; } this.$axios({ url: this.$settings.base_url + '/user/login/mobile/', method: 'post', data: { mobile: this.mobile, code: this.sms } }).then(response => { let _this = this; let status = response.data.status; let msg = response.data.msg; if (status == 0) { // cookie存儲token,保存登陸狀態 let token = response.data.results.token; _this.$cookies.set('token', token, 24 * 60 * 60); let username = response.data.results.username; _this.$cookies.set('username', username, 24 * 60 * 60); _this.$message({ message: '登陸成功', duration: 1000, onClose() { // 保存登陸狀態 sessionStorage.token = token; sessionStorage.username = response.data.results.username; sessionStorage.mobile = response.data.results.mobile; // 跳轉到主頁 _this.$router.push('/'); } }); } else { _this.mobile = ''; _this.sms = ''; _this.$message({ message: msg, }); } }).catch(error => { this.mobile = ''; this.sms = ''; this.$message({ message: error.response.data.result }); }) }, }, }; </script> <style scoped> .box { width: 100%; height: 100%; position: relative; overflow: hidden; } .box img { width: 100%; min-height: 100%; } .box .login { position: absolute; width: 500px; height: 400px; left: 0; margin: auto; right: 0; bottom: 0; top: -338px; } .login .login-title { width: 100%; text-align: center; padding-top: 20px; } .login-title img { width: 190px; height: auto; } .login-title p { font-family: PingFangSC-Regular; font-size: 18px; color: #fff; letter-spacing: .29px; padding-top: 10px; padding-bottom: 50px; } .login_box { width: 400px; height: auto; background: #fff; box-shadow: 0 2px 4px 0 rgba(0, 0, 0, .5); border-radius: 4px; margin: 0 auto; padding-bottom: 40px; } .login_box .title { font-size: 20px; color: #9b9b9b; letter-spacing: .32px; border-bottom: 1px solid #e6e6e6; display: flex; justify-content: space-around; padding: 50px 60px 0 60px; margin-bottom: 20px; cursor: pointer; } .login_box .title span.active { color: #4a4a4a; border-bottom: 2px solid #84cc39; } .inp { width: 350px; margin: 0 auto; } .inp input { outline: 0; width: 100%; height: 45px; border-radius: 4px; border: 1px solid #d9d9d9; text-indent: 20px; font-size: 14px; background: #fff !important; } .inp input.user { margin-bottom: 16px; } .inp .rember { display: flex; justify-content: space-between; align-items: center; position: relative; margin-top: 10px; } .inp .rember p:first-of-type { font-size: 12px; color: #4a4a4a; letter-spacing: .19px; margin-left: 22px; display: -ms-flexbox; display: flex; -ms-flex-align: center; align-items: center; /*position: relative;*/ } .inp .rember p:nth-of-type(2) { font-size: 14px; color: #9b9b9b; letter-spacing: .19px; cursor: pointer; } .inp .rember input { outline: 0; width: 30px; height: 45px; border-radius: 4px; border: 1px solid #d9d9d9; text-indent: 20px; font-size: 14px; background: #fff !important; } .inp .rember p span { display: inline-block; font-size: 12px; width: 100px; /*position: absolute;*/ /*left: 20px;*/ } #geetest { margin-top: 20px; } .login_btn { width: 100%; height: 45px; background: #84cc39; border-radius: 5px; font-size: 16px; color: #fff; letter-spacing: .26px; margin-top: 30px; } .inp .go_login { text-align: center; font-size: 14px; color: #9b9b9b; letter-spacing: .26px; padding-top: 20px; } .inp .go_login a { color: #84cc39; cursor: pointer; } #get_code { border: 0; width: 120px; height: 30px; background-color: antiquewhite; outline: none; } #get_code:active { color: white; } #checkbox { width: 20px; height: 20px; } .sms { position: relative; } .sms .sms_btn { position: absolute; top: -12px; right: 0; bottom: 0; margin: auto; width: 130px; text-align: center; height: 24px; color: #ff7000; cursor: pointer; border-left: 1px solid #999; } </style>
五.前臺註銷
後臺token過時時間配置dev.py:
# jwt配置 import datetime JWT_AUTH = { 'JWT_EXPIRATION_DELTA': datetime.timedelta(days=1), }
cookie在登陸成功後已保存
header.vue:
<template> <div class="header-box"> <div class="header"> <div class="content"> <div class="logo full-left"> <router-link to="/"><img @click="jump('/')" src="@/assets/img/logo.svg" alt=""></router-link> </div> <ul class="nav full-left"> <li><span @click="jump('/course')" :class="this_nav=='/course'?'this':''">免費課</span></li> <li><span @click="jump('/light-course')" :class="this_nav=='/light-course'?'this':''">輕課</span></li> <li><span>學位課</span></li> <li><span>題庫</span></li> <li><span>老男孩教育</span></li> </ul> <div class="login-bar full-right"> <div class="shop-cart full-left"> <img src="@/assets/img/cart.svg" alt=""> <span><router-link to="/cart">購物車</router-link></span> </div> <div class="login-box full-left"> <div v-if="!is_login"> <router-link to="/login">登陸</router-link> | <router-link to="/register">註冊</router-link> </div> <div v-else> <router-link to="/user">{{ username }}</router-link> | <span @click="logoutAction">註銷</span> </div> </div> </div> </div> </div> </div> </template> <script> export default { name: "Header", data() { return { this_nav: "", is_login: false, username: '', } }, created() { this.this_nav = localStorage.this_nav; this.is_login = this.$cookies.get('token'); this.username = this.$cookies.get('username'); }, methods: { jump(location) { localStorage.this_nav = location; // vue-router除了提供router-link標籤跳轉頁面之外,還提供了 js跳轉的方式 this.$router.push(location); }, logoutAction() { this.is_login = false; this.username = ''; this.$cookies.remove('token'); this.$cookies.remove('username'); } } } </script>
六.接口緩存
home/views.py
from rest_framework.generics import ListAPIView from rest_framework.response import Response from . import models, serializers """ class BannerListAPIView(ListAPIView): queryset = models.Banner.objects.filter(is_show=True, is_delete=False).order_by('-orders') serializer_class = serializers.BannerModelSerializer """ # 接口緩存 from django.core.cache import cache class BannerListAPIView(ListAPIView): def get(self, request, *args, **kwargs): banner_list_data = cache.get('api_banner_list_data') if not banner_list_data: print('查詢了數據庫') banner_query = models.Banner.objects.filter(is_show=True, is_delete=False).order_by('-orders') banner_list_data = serializers.BannerModelSerializer(banner_query, many=True).data # 創建接口緩存 cache.set('api_banner_list_data', banner_list_data) return Response(banner_list_data)
不走ListAPIView的Response,返回給前臺的數據不帶前綴,因此banner.vue需加前綴才能訪問:
<template> <div class="banner"> <el-carousel height="520px" :interval="3000" arrow="always"> <!--<el-carousel-item>--> <!--<img src="@/assets/img/banner1.png" alt="">--> <!--</el-carousel-item>--> <!--<el-carousel-item>--> <!--<img src="@/assets/img/banner2.png" alt="">--> <!--</el-carousel-item>--> <!--<el-carousel-item>--> <!--<img src="@/assets/img/banner3.png" alt="">--> <!--</el-carousel-item>--> <el-carousel-item v-for="banner in banner_list" :key="banner.image"> <!--<router-link :to="banner.link">--> <!--<img :src="banner.image" alt="">--> <!--</router-link>--> <a :href="banner.link"> <img :src="$settings.base_url + banner.image" alt=""> </a> </el-carousel-item> </el-carousel> </div> </template>
訪問量過多,緩存也沒法減緩數據庫的訪問壓力,須要藉助celery
Celery 官網:http://www.celeryproject.org/
Celery 官方文檔英文版:http://docs.celeryproject.org/en/latest/index.html
Celery 官方文檔中文版:http://docs.jinkan.org/docs/celery/
Celery的架構由三部分組成,消息中間件(message broker)、任務執行單元(worker)和 任務執行結果存儲(task result store)組成。
Celery自己不提供消息服務,可是能夠方便的和第三方提供的消息中間件集成。包括,RabbitMQ, Redis等等
Worker是Celery提供的任務執行的單元,worker併發的運行在分佈式的系統節點中。
Task result store用來存儲Worker執行的任務的結果,Celery支持以不一樣方式存儲任務的結果,包括AMQP, redis等
異步任務:將耗時操做任務提交給Celery去異步執行,好比發送短信/郵件、消息推送、音視頻處理等等
定時任務:定時執行某件事情,好比天天數據統計
pip install celery
消息中間件:RabbitMQ/Redis
app=Celery('任務名', broker='xxx', backend='xxx')
project
├── celery_task # celery包
│ ├── __init__.py # 包文件
│ ├── celery.py # celery鏈接和配置相關文件,且名字必須叫celery.py
│ └── tasks.py # 全部任務函數
├── add_task.py # 添加任務
└── get_result.py # 獲取結果
安裝: pip install celery
celery.py:
# 1)建立app + 任務 # 2)啓動celery(app)服務: # 非windows # 命令:celery worker -A celery_task -l info # windows: # pip3 install eventlet # celery worker -A celery_task -l info -P eventlet # 3)添加任務:手動添加,要自定義添加任務的腳本,右鍵執行腳本 # 4)獲取結果:手動獲取,要自定義獲取任務的腳本,右鍵執行腳本
代碼:
from celery import Celery """ broker=存儲tasks的倉庫 backend=存儲results的倉庫 include=[任務文件們] """ broker = 'redis://127.0.0.1:6379/11' backend = 'redis://127.0.0.1:6379/12' include = ['celery_task_1.task1', 'celery_task_1.task2'] app = Celery(broker=broker, backend=backend, include=include) # 啓動celery服務的命令: # 前提:必定要進入celery_task所屬的文件夾(上一層) # celery worker -A celery_task_1 -l info -P eventlet
基本使用(可寫多個task文件,也可把多個函數寫在一個task.py文件中):
tasks.py
from .celery import app import time, random # 任務就是一個個的功能函數,功能函數的返回值就是任務的執行結果 @app.task def add(n, m): res = n + m # 模擬不固定耗時操做 time.sleep(random.randint(1, 5)) print(res) return res
add_task.py:
# 手動添加任務的文件,該文件不是必須的(能夠自動添加任務)
# 該文件也不屬於 celery task 包的一部分,就是用來實現手動添加任務的
# 將add添加到broker倉庫,celery會去異步執行
from celery_task import tasks # 添加當即執行任務 t1 = tasks.add.delay(10, 20) t2 = tasks.low.delay(100, 50) print(t1.id) # 添加延遲任務 from datetime import datetime, timedelta def eta_second(second): ctime = datetime.now() utc_ctime = datetime.utcfromtimestamp(ctime.timestamp()) time_delay = timedelta(seconds=second) return utc_ctime + time_delay tasks.low.apply_async(args=(200, 50), eta=eta_second(10))
get_result.py:
from celery_task_1.celery import app from celery.result import AsyncResult id = '4e249f2d-559a-4a3e-8b43-d498b3d6355e' if __name__ == '__main__': async = AsyncResult(id=id, app=app) if async.successful(): result = async.get() print(result) elif async.failed(): print('任務失敗') elif async.status == 'PENDING': print('任務等待中被執行') elif async.status == 'RETRY': print('任務異常後正在重試') elif async.status == 'STARTED': print('任務已經開始被執行')
自動添加(沒有add_task.py文件):
celery.py
from celery import Celery broker = 'redis://127.0.0.1:6379/11' backend = 'redis://127.0.0.1:6379/12' include = ['celery_task.tasks',] app = Celery(broker=broker, backend=backend, include=include) # 啓動celery服務的命令: # 前提:必定要進入celery_task所屬的文件夾 # celery worker -A celery_task -l info -P eventlet # 自動添加任務 # 時區 app.conf.timezone = 'Asia/Shanghai' # 是否使用UTC app.conf.enable_utc = False # 任務的定時配置 from datetime import timedelta from celery.schedules import crontab app.conf.beat_schedule = { 'low-task': { 'task': 'celery_task.tasks.low', 'schedule': timedelta(seconds=3), # 'schedule': crontab(hour=8, day_of_week=1), # 每週一早八點 'args': (300, 150), }, 'my-add-task': { 'task': 'celery_task.tasks.add', 'schedule': timedelta(seconds=6), 'args': (300, 150), } } # 啓動 添加任務 服務的命令,也要進入celery_task所屬文件夾 # celery beat -A celery_task -l info
tasks.py:
from .celery import app @app.task def add(n, m): res = n + m print(res) return res @app.task def low(n, m): res = n - m print(res) return res
get_result.py同上
celery異步處理django任務(把celery_task放在大luffy下):
celery.py
# 啓動django依賴 # 將celery服務框架放在項目根目錄下 # import sys # sys.path.append(r'C:\Users\oldboy\Desktop\luffy\luffyapi') import os, django os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'luffyapi.settings.dev') django.setup() from celery import Celery broker = 'redis://127.0.0.1:6379/11' backend = 'redis://127.0.0.1:6379/12' include = ['celery_task.tasks',] app = Celery(broker=broker, backend=backend, include=include) # 啓動celery服務的命令: # 前提:必定要進入celery_task所屬的文件夾 # celery worker -A celery_task -l info -P eventlet # 自動添加任務 # 時區 app.conf.timezone = 'Asia/Shanghai' # 是否使用UTC app.conf.enable_utc = False # 任務的定時配置 from datetime import timedelta from celery.schedules import crontab app.conf.beat_schedule = { 'django-task': { 'task': 'celery_task.tasks.django_task', 'schedule': timedelta(seconds=10), # 'schedule': crontab(hour=8, day_of_week=1), # 每週一早八點 'args': (), }, } # 啓動 添加任務 服務的命令 # celery beat -A celery_task -l info
tasks.py:
from .celery import app from django.core.cache import cache from apps.home import models, serializers @app.task def django_task(): banner_query = models.Banner.objects.filter(is_show=True, is_delete=False).order_by('-orders') banner_list_data = serializers.BannerModelSerializer(banner_query, many=True).data # 創建接口緩存 cache.set('api_banner_list_data', banner_list_data) return '輪播圖緩存更新完畢'