luffy项目之django操作redis,celery使用
复习
# redis:非关系型数据库,存数据的地方,纯内存操作,6.x之前 单线程架构,5大数据类型
# 编译安装,win支持不好
# 启动服务:redis-server 配置文件 使用win的服务启动
# 停止服务: shutdown 使用win的服务停止
# 客户端链接:redis-cli -h 地址 -p 端口
# python链接redis,连接池链接(把池做成单例)单例:整个全局就这一个实例 实现了全局只用这一个实例
# 字符串操作:get set
# 字典操作:hget hset 5大数据类型,只支持一层,后面都是字符串
hash1 hobby ['篮球','足球'] # 把value转成json格式字符串存入
# 列表操作:lpush lpop blpop
# 通用操作
1 djagno 使用redis
1.1 通用操作(跟框架无关)
# 写一个py文件
import redis
POOL = redis.ConnectionPool(host='127.0.0.1', port=6379, max_connections=100)
# 导入,使用
from utils.redis_pool import POOL
import redis
conn=redis.Redis(connection_pool=POOL)
conn.set('name','lqz')
conn.close()
1.2 使用django-redis
# 安装 pip install django-redis
# redis的配置,django缓存的配置,以后django的缓存也用redis了
# 配置文件
CACHES = {
"default": {
"BACKEND": "django_redis.cache.RedisCache",
"LOCATION": "redis://127.0.0.1:6379/0",
"OPTIONS": {
"CLIENT_CLASS": "django_redis.client.DefaultClient",
"CONNECTION_POOL_KWARGS": {"max_connections": 100},
"DECODE_RESPONSES": True,
# "PASSWORD": "",
}
}
}
# 使用
from django_redis import get_redis_connection
conn=get_redis_connection('default') # 指定使用哪个配置
conn.set('age',19)
1.3 django 中使用,建议使用这种方法(常用)
# 也会存到redis中,最大的好处,cache.set的value值,可以是python的任意类型对象
from django.core.cache import cache
cache.set('hobby',['篮球','足球'])
res=cache.get('hobby')
# cache帮咱们做了封装,可以缓存任意的python对象
# django缓存的内部,拿到对象转成了二进制(pickle),存到了redis中
2 celery介绍和架构
# celery:(芹菜),分布式 异步任务 框架 ,对win支持不好
Celery is a project with minimal funding, so we don’t support Microsoft Windows. Please don’t open any issues related to that platform.
#Celery的架构由三部分组成,
消息中间件(message broker):需要使用第三方,reids,rabbitmq等 #提交任务的地方
任务执行单元(worker):celery提供的 #执行任务的地方
任务执行结果存储(task result store):使用第三方 redis等 #任务完成的结果的地方
pip install celery
# 使用场景
异步执行:解决耗时任务,将耗时操作任务提交给Celery去异步执行,比如发送短信/邮件、消息推送、音视频处理等等
延迟执行:解决延迟任务
定时执行:解决周期(周期)任务,比如每天数据统计
#cerely主要是提高并发量
3 celery快速使用
####### 第一步:写一个py文件,实例化得到app,编写任务
from celery import Celery
broker='redis://127.0.0.1:6379/1' #消息中间件
backend='redis://127.0.0.1:6379/2' #结果存储
app = Celery(__name__,backend=backend,broker=broker) #__name__等于app对象的名字
#写任务 也可以说叫函数,使用装饰器装饰一下
@app.task
def add(a,b):
import time
time.sleep(2)
return a+b
######## 第二步:在其他系统中,提交任务,(导入任务)
from main import add
#同步调用,不叫提交任务
# res=add(3,4)
# print(res)
#异步执行,先提交任务,并不执行
res=add.apply_async(args=[3,4])
#返回结果是任务的id号61bdfa12-95b1-46e4-b020-40db8113bc47
print(res)
#### 第三步:启动worker执行任务
# 使用命令启动worker
# win机器,需要安装 eventlet
# pip install eventlet
# 启动命令 -A启动那个文件 -l日志界别 -P win下才加
# 3.x 及以前:celery worker -A main -l info -P eventlet
# 4.x及以后:celery -A main worker -l info -P eventlet
# linux执行下面:区别在于linux不用加-P参数
celery -A main worker -l info
### 第四步:worker就会从任务中取出任务执行
### 第五步:查看任务执行结果
#查询结果 根据id查询 还有app名字
from main import app
from celery.result import AsyncResult
id = '61bdfa12-95b1-46e4-b020-40db8113bc47'
if __name__ == '__main__':
asy = AsyncResult(id=id, app=app)
if asy.successful(): # 顺利执行完了,可以取结果了
res = asy.get() # 获取任务执行的结果
print(res)
elif asy.failed():
print('任务失败')
elif asy.status == 'PENDING':
print('任务等待中被执行')
elif asy.status == 'RETRY':
print('任务异常后正在重试')
elif asy.status == 'STARTED':
print('任务已经开始被执行')
4 包管理形式
#上面的方式正式项目不那么用 都采用下面的方式 包管理形式,写好之后这个包可以直接拿到项目使用
#### 第一步:建立如下格式
celery_task # 包名
__init__.py
celery.py # 必须叫这个名字 存放的是app等配置文件的名字
course_task.py # 一堆任务
home_task.py # 一堆任务
user_task.py # 一堆任务
任务提交-正常不写在这.py #正常写在谁用写在谁里面
结果查看--正常也不写在这.py #正常写在谁用写在谁里面
### 第二步,在celery.py中写入
from celery import Celery
broker='redis://127.0.0.1:6379/1' # 消息中间件
backend='redis://127.0.0.1:6379/2' # 结果存储
# include 表示哪些py文件的任务被app管理
app = Celery(__name__,backend=backend,broker=broker,include=['celery_task.course_task','celery_task.home_task','celery_task.user_task'])
### 第三步:写好多任务
from .celery import app # 使用相对路径导入
import time
@app.task
def send_sms(phone,code):
print('短信发送成功','手机号为:%s,code为:%s'%(phone,code))
time.sleep(1)
return '手机号为:%s,code为:%s'%(phone,code)
### 第四步:启动worker,在包这一层路径 包在的文件夹路径比如scripts里面有一个包a 那么就需要在scprits文件夹下,而不是文件夹a下
#celery -A 包名 worker -l info -P eventlet
celery -A celery_task worker -l info -P eventlet
####第五步:提交任务(什么时候提交都可以)
from celery_task.user_task import send_sms
res=send_sms.apply_async(args=['3354325334',5555])
print(res) # eb89760d-73fe-41c9-a7a5-6ae91f811aa1
### 第六步:结果查看
from celery_task.celery import app
from celery.result import AsyncResult
id = 'eb89760d-73fe-41c9-a7a5-6ae91f811aa1'
if __name__ == '__main__':
asy = AsyncResult(id=id, app=app)
if asy.successful(): # 顺利执行完了,可以取结果了
res = asy.get() # 任务执行的结果
print(res)
elif asy.failed():
print('任务失败')
elif asy.status == 'PENDING':
print('任务等待中被执行')
elif asy.status == 'RETRY':
print('任务异常后正在重试')
elif asy.status == 'STARTED':
print('任务已经开始被执行')
5 执行异步任务和延迟任务
# 异步任务
res=任务.delay(参数1,参数2)
print(res)
# 延迟任务
from datetime import datetime, timedelta
#当前时间+一分钟之后
eta=datetime.utcnow() + timedelta(minutes=1)
# eta是一个时间对象,不能传数字
res=send_sms.apply_async(args=['18953675221',8888],eta=eta)
print(res)
6 执行定时任务
##############第一步: 在celery.py中加入
# 修改app的配置文件
# 时区
app.conf.timezone = 'Asia/Shanghai'
# 是否使用UTC
app.conf.enable_utc = False
# 任务的定时配置
from datetime import timedelta
from celery.schedules import crontab
app.conf.beat_schedule = {
'send_sms':{
'task': 'celery_task.user_task.send_sms',
'schedule': timedelta(seconds=3), # 每隔3s
# 'schedule': crontab(hour=8, day_of_week=1), # 每周一早八点
'args': ('12345667', 8888),
}
}
####第二步:启动worker和beat
# 启动两个任务(worker 和 beat)
# 启动beat自动提交
celery -A celery_task beat -l info #启动beat
celery -A celery_task worker -l info -P eventlet #启动worker 执行任务
7 路飞项目中使用
# 轮播图
# 定时更新轮播图缓存 不用每次都去数据库查询
# 在脚本中加入可执行djagno的命令
import os
os.environ.setdefault("DJANGO_SETTINGS_MODULE", "luffy.settings.dev")
# 加入缓存,会有问题,缓存不一致----》双写一致性
-先删缓存,再写mysql
-先写mysql,删除缓存
-先写mysql,更新缓存
-定时更新
#celery配置
app.conf.beat_schedule = {
'update_banner': {
'task': 'celery_task.home_task.banner_update',
'schedule': timedelta(seconds=3), # 每隔3s
# 'schedule': crontab(hour=8, day_of_week=1), # 每周一早八点
'args': (),
}
}
# 轮播图加缓存
def list(self, request, *args, **kwargs):
# 先去缓存中查数据,如果有,之间返回
res = cache.get('banner_list')
if res:
#走了缓存
print('走了缓存')
return Response(data=res)
else:
print('没走缓存')
# 如果没有,去数据库查,放入缓存
response = super().list(request, *args, **kwargs)
cache.set('banner_list', response.data)
return response
# 任务:
def banner_update():
from .celery import app
from django.core.cache import cache
from django.conf import settings
from home.models import Banner
from home.Serializer import BannerSerializer
@app.task
def banner_update():
# 往django缓存中写入轮播图数据
queryset = Banner.objects.all().filter(is_delete=False, is_show=True)[:settings.BANNER_COUNT]
ser = BannerSerializer(instance=queryset, many=True)
for item in ser.data:
item['image'] = 'http://127.0.0.1:8000' + item['image']
cache.set('banner_list', ser.data)
# 启动beat,和worker
celery -A celery_task beat -l info #beat 执行任务
celery -A celery_task worker -l info -P eventlet #存储任务