DAY 83 redis01
1 redis的连接池
-在创建连接池的时候,就要指定连接地址
-以后每次使用都是从连接池中拿一个连接
2 编码问题,实例化得到redis连接对象(Redis类),可以指定编码
3 http,应用层协议,基于tcp封装
-ssh,http,ftp,websocket,DNS---》应用层协议
-tcp/ip:socket,抽象层
-UDP:DNS
4 mysql ,redis:cs架构的软件
-客户端和服务端通信----》通过网络----》基于tcp自封装协议
-客户端和服务端通信,必须遵循这个协议
-es,docker:http协议,符合resful规范
5 手机号登录接口
6 手机号注册功能
-前端把手机号,密码,验证码传入---》创建用户
7 前端的手机号登录,发送验证码,手机号注册
8 redis:非关系型数据库,缓存数据库,c,性能很高,5大数据类型
-cs架构的,
-启动服务端 redis-server 配置文件
-客户端连接 redis-cli -h -p
-windows的RedisDesktopManager客户端连接
9 python连接redis:普通连接,连接池
10 redis的字符串操作
11 python代码 是解释型语言,必须运行在解释器之上
pyinstaller---可以把这个py文件打包成exe
打成的exe特别大,不需要解释器了
-go 编译型,写的代码---》编译成exe
-java:是编译型---》字节码文件
-jdk:java开发工具包,包含了jre,jvm
-jre:java运行环境
-jvm:java虚拟机,至少占200m内存
-阿里有自己的jvm
1 redis的hash操作
from redis import Redis
conn = Redis()
# 1 hset(name, key, value)
# conn.hset('hash_test','name','lqz')
# conn.hset('hash_test','age','19')
# name对应的hash中设置一个键值对(不存在,则创建;否则,修改)
# 2 hmset(name, mapping)
# conn.hmset('hash_test1',{'name':'egon','age':18})
# 3 hget(name ,key)
# print(conn.hget('hash_test1','name'))
# 4 hmget(name, keys, *args)
# print(conn.hmget('hash_test1',['name','age']))
# 5 hgetall(name)
# print(conn.hgetall('hash_test1')) # 尽量少执行,慢长命令,可能会撑爆应用程序的内容
# 6 hlen(name)
# print(conn.hlen('hash_test1'))
# 获取name对应的hash中键值对的个数
# 6 hkeys(name)
# print(conn.hkeys('hash_test1'))
# 获取name对应的hash中所有的key的值
# 7 hvals(name)
# print(conn.hvals('hash_test1'))
# 获取name对应的hash中所有的value的值
# 8 hexists(name, key)
# print(conn.hexists('hash_test1','hobby'))
# print(conn.hexists('hash_test1','name'))
# 检查name对应的hash是否存在当前传入的key
# 9 hdel(name ,*keys)
# conn.hdel('hash_test1','name','wife')
# 将name对应的hash中指定key的键值对删除
# print(re.hdel('xxx' ,'sex' ,'name'))
# 10 hincrby(name, key, amount=1)
# conn.hincrby('hash_test1','age',amount=10)
# 自增name对应的hash中的指定key的值,不存在则创建key=amount
# 参数:
# name,redis中的name
# key, hash对应的key
# amount,自增数(整数)
# 11 hincrbyfloat(name, key, amount=1.0)
# conn.hincrbyfloat('hash_test1','age',1.2) # 存在精度损失问题
# 12 hscan(name, cursor=0, match=None, count=None)
# for i in range(1000):
# conn.hset('test','%s-%s'%('key',i),i)
# res=conn.hscan('test',cursor=0,count=100)
# print(res)
# print(len(res[1]))
#
# res=conn.hscan('test',cursor=res[0],count=100)
# print(res)
# print(len(res[1]))
# 13hscan_iter(name, match=None, count=None)
# hgetall---》一次性全取出来 ----》生成器用的位置
for item in conn.hscan_iter('test',count=10): # 取出全部的value值,但是每次取10个
print(item)
conn.close()
2 redis的列表操作
from redis import Redis
conn = Redis()
# 1 lpush(name ,values)
# conn.lpush('list_test','egon')
# conn.lpush('list_test','lqz')
# conn.rpush('list_test','ylf')
# 2 lpushx(name ,value)
# 在name对应的list中添加元素,只有name已经存在时,值添加到列表的最左边
# conn.lpushx('yyyyy' ,'egon')
# conn.lpushx('list_test' ,'egon')
# 3 llen(name)
# print(conn.llen('list_test'))
# name对应的list元素的个数
# 4 linsert(name, where, refvalue, value))
# conn.linsert('list_test','before','lqz','dlrb')
# conn.linsert('list_test','after','lqz','jason')
# 在name对应的列表的某一个值前或后插入一个新值
# 参数:
# name,redis的name
# where,BEFORE或AFTER(小写也可以)
# refvalue,标杆值,即:在它前后插入数据(如果存在多个标杆值,以找到的第一个为准)
# value,要插入的数据
# 5 r.lset(name, index, value)
# conn.lset('list_test',0,'xxxx')
# 对name对应的list中的某一个索引位置重新赋值
# 参数:
# name,redis的name
# index,list的索引位置
# value,要设置的值
# 5 r.lrem(name, value, num)
# conn.lrem('list_test',value='egon',count=1)
# 在name对应的list中删除指定的值
# 参数:
# name,redis的name
# value,要删除的值
# num, num=0,删除列表中所有的指定值;
# num=2,从前到后,删除2个;
# num=-2,从后向前,删除2个
# 6 lpop(name)
# print(conn.lpop('list_test'))
# print(conn.rpop('list_test'))
# 在name对应的列表的左侧获取第一个元素并在列表中移除,返回值则是第一个元素
# 更多:
# rpop(name) 表示从右向左操作
# 6 lindex(name, index)
# print(conn.lindex('list_test',3))
# 在name对应的列表中根据索引获取列表元素
# 7 lrange(name, start, end)
# print(conn.lrange('list_test', 0, 2))
# 在name对应的列表分片获取数据
# 参数:
# name,redis的name
# start,索引的起始位置
# end,索引结束位置 print(re.lrange('aa',0,re.llen('aa')))
# 8 ltrim(name, start, end)
# conn.ltrim('list_test',0,1) #保留0-1之间的
# 在name对应的列表中移除没有在start-end索引之间的值
# 参数:
# name,redis的name
# start,索引的起始位置
# end,索引结束位置(大于列表长度,则代表不移除任何)
# 9 rpoplpush(src, dst)
# 从一个列表取出最右边的元素,同时将其添加至另一个列表的最左边
# 参数:
# src,要取数据的列表的name
# dst,要添加数据的列表的name
###记住
# 10 blpop(keys, timeout)
print(conn.blpop('list_test'))
# 11 r.brpop(keys, timeout),从右向左获取数据
3 其他操作
# 公共操作,跟类型无关
from redis import Redis
conn=Redis()
# 1 delete(*names)
# conn.delete('name','age','test')
# 2 exists(name)
# res=conn.exists('hash_test','hash_test1')
# print(res)
# 检测redis的name是否存在
# 3 keys(pattern='*')
# res=conn.keys('h?sh_test') # 慎用
# res=conn.keys('hash_*') # 慎用
# print(res)
# 根据模型获取redis的name
# 更多:
# KEYS * 匹配数据库中所有 key 。
# KEYS h?llo 匹配 hello , hallo 和 hxllo 等。
# KEYS h*llo 匹配 hllo 和 heeeeello 等。
# KEYS h[ae]llo 匹配 hello 和 hallo ,但不匹配 hillo
# 4 expire(name, time)
# conn.expire('hash_test',5)
# 为某个redis的某个name设置超时时间
# 5 rename(src, dst)
# conn.rename('hash_test1','xxx')
# 对redis的name重命名为
# 6 move(name, db))
# conn.move('xxx',1)
# 将redis的某个值移动到指定的db下
# 7 randomkey()
# print(conn.randomkey())
# 随机获取一个redis的name(不删除)
# 8 type(name)
# print(conn.type('name'))
# print(conn.type('list'))
4 管道
'''
1 非关系型数据库,本身不支持事务
2 redis中的管道可以实现事务的支持(要么都成功,要么都失败)
-实现的原理:多条命令放到一个管道中,一次性执行
3 具体代码
4 如果是集群环境,不支持管道
'''
import redis
pool = redis.ConnectionPool()
r = redis.Redis(connection_pool=pool)
pipe = r.pipeline(transaction=True) # 开启事务
pipe.multi() #管道等待放入多条命令
pipe.set('name', 'egon')
pipe.set('role', 'admin')
# 到此,命令都没有执行
pipe.execute() #执行管道中的所有命令
5 django中使用redis
方式一(通用方式,任何框架都可以用)
# 写一个pool
from redis import ConnectionPool
POOL = ConnectionPool(max_connections=5, host='127.0.0.1', port=6379)
# 在使用的位置,获取连接,获取数据
from luffyapi.libs.redis_pool import POOL
from redis import Redis
conn = Redis(connection_pool=POOL)
name = conn.get('name')
第二种:django框架专用
1 pip install django-redis
2 在配置文件中配置
CACHES = {
"default": {
"BACKEND": "django_redis.cache.RedisCache",
"LOCATION": "redis://127.0.0.1:6379",
"OPTIONS": {
"CLIENT_CLASS": "django_redis.client.DefaultClient", #这句指的是django中的缓存也缓存到redis中了
"CONNECTION_POOL_KWARGS": {"max_connections": 100} #连接池的大小
# "PASSWORD": "123",
}
}
}
3 在使用的位置
conn=get_redis_connection() # 从连接池中取一个连接
name = conn.get('name')
# 这个好,他可以缓存任意对象
from django.core.cache import cache
cache.set('user',user)
cache.get()
4 以后django中都用这种,好处是django中的缓存也会放到redis中
6 接口缓存
# 以首页轮播图为例,加入缓存,后期所有接口都可使用缓存
# 用户来了,先去缓存中看,如果有直接返回,如果没有,去mysql中查,查完以后在缓存中放一份
# 双写一致性:处理方案有三种
-只要插入数据,就删除缓存
class BannerView(ViewSetMixin, ListAPIView):
queryset = Bannder.objects.all().filter(is_show=True, is_delete=False).order_by('-orders')[0:settings.BANNER_SIZE]
serializer_class = BannerSerializer
def list(self, request, *args, **kwargs):
banner_list = cache.get('banner_cache_list')
if not banner_list: # 没有值,走它,去数据库查
response = super().list(request, *args, **kwargs)
# 在缓存中放一份
cache.set('banner_cache_list', response.data)
else:
# 走了缓存,速度很快
print('走了缓存')
response = Response(data=banner_list)
return response
7 celery介绍
1 celery:芹菜,分布式的异步任务框架
2 可以做的事,我们用它来解决什么问题
- 异步任务 (异步的执行这个任务函数)
- 延迟任务(延迟5s钟,执行一个任务(函数))
- 定时任务(定时的执行任务(函数))如果单纯执行定时任务,没必要用 celery,可以使用别的
3 平台问题
Celery is a project with minimal funding, so we don’t support Microsoft Windows. Please don’t open any issues related to that platform
4 读一下
"""
1)可以不依赖任何服务器,通过自身命令,启动服务(内部支持socket)
2)celery服务为为其他项目服务提供异步解决任务需求的
注:会有两个服务同时运行,一个是项目服务,一个是celery服务,项目服务将需要异步处理的任务交给celery服务,celery就会在需要时异步完成项目的需求
人是一个独立运行的服务 | 医院也是一个独立运行的服务
正常情况下,人可以完成所有健康情况的动作,不需要医院的参与;但当人生病时,就会被医院接收,解决人生病问题
人生病的处理方案交给医院来解决,所有人不生病时,医院独立运行,人生病时,医院就来解决人生病的需求
"""
5 Celery的架构由三部分组成,
-消息中间件(message broker)
-Celery本身不提供消息服务,但是可以方便的和第三方提供的消息中间件集成。包括,RabbitMQ, Redis等等
-任务执行单元(worker)
-Worker是Celery提供的任务执行的单元,worker并发的运行在分布式的系统节点中
-任务执行结果存储(task result store)
-Task result store用来存储Worker执行的任务的结果,Celery支持以不同方式存储任务的结果,包括AMQP, redis等
8 简单使用
1 pip install celery
2 两种目录组织结构
-普通的
-包管理的(推荐的)
3 普通的(就一个py文件)
celery -A proj worker -l INFO
celery -A celery_task worker -l info -P eventlet
4 基本使用
8.1 基本使用
# 第一步:定义一个py文件(名字随意,我们叫celery_tase)
from celery import Celery
backend = 'redis://127.0.0.1:6379/1'
broker = 'redis://127.0.0.1:6379/2'
app = Celery(__name__,broker=broker,backend=backend)
# 被它修饰,就变成了celery的任务
@app.task
def add(a,b):
return a+b
#第二步:提交任务(新建一个py文件:submit_task)
from celery_task import add
# 异步调用
# 只是把任务提交到了redis中,但是没有执行,返回一个唯一标识,后期使用唯一标识去看任务执行结果
res=add.delay(33,41)
print(res)
#第三步:启动worker
#celery_task py文件的名字
#-l info日志输出级别是info
# -P eventlet 在win平台需要加上
celery -A celery_task worker -l info -P eventlet
#如果队列里有任务,就会执行,如果没有任务,worker就等在这
# 第四步:查询结果是否执行完成
from celery_task import app
from celery.result import AsyncResult
id = 'ed85b97a-a231-4d71-ba11-e5f6450d0b47'
if __name__ == '__main__':
a = AsyncResult(id=id, app=app)
if a.successful():
result = a.get()
print(result)
elif a.failed():
print('任务失败')
elif a.status == 'PENDING':
print('任务等待中被执行')
elif a.status == 'RETRY':
print('任务异常后正在重试')
elif a.status == 'STARTED':
print('任务已经开始被执行')
8.2 包管理结构
1 包结构
celery_task
__init__.py
celery.py
course_task.py
home_task.py
user_task.py
2 celery.py
from celery import Celery
backend = 'redis://127.0.0.1:6379/1'
broker = 'redis://127.0.0.1:6379/2'
app = Celery(__name__, broker=broker, backend=backend,
include=['celery_task.course_task', 'celery_task.user_task', 'celery_task.home_task'])
3 home_task.py
from .celery import app
@app.task
def add(a,b):
import time
time.sleep(10)
return a+b
4 在任意项目中使用
-导入,之间使用,例如
def test(request):
# 提交一个计算 90+80的任务
from celery_task.home_task import add
res=add.delay(90,80)
return HttpResponse('任务已经提,任务id为:%s'%str(res))