day10

内容概要

  • redis之字符串
  • redis之列表
  • redis之hash
  • redis其他操作
  • redis管道
  • django中使用redis
  • celery介绍和安装
  • celery快速使用
  • celery包结构

redis之字符串

from redis import Redis

conn = Redis(host='127.0.0.1', port=6379)

# conn.set("age", 18, ex=)
"""
set(name, value, ex=None, px=None, nx=False, xx=False)
ex, 过期时间(秒)
px,过期时间(毫秒)
nx,如果设置为True,则只有name不存在时,当前set操作才执行,值存在就修改不了,执行没有效果
xx,如果设置为True,则只有name存在时,当前set操作才执行,值不存在,执行没有效果
"""
# conn.set("age", 18, ex=5)
# conn.set("age", 18, px=3000)
# conn.set("age", 15, nx=True)
# conn.set("age", 19, nx=True)
# conn.set("age", 19, xx=True)
# conn.set("age", 19, xx=True)

339772b8790a86f43c616aaaf00dc3a

84ca951acbf22468f5b3bfee3b3a94c

d777d7caa2266e0fe29d74b0ce2041b

"""
 setnx(name, value)
 设置值,只有name不存在时,执行设置操作(添加)如果存在,不会修改
 相当于 set(nx=True) 默认nx=True
"""

"""
setex(name, time, value)
设置值
参数
time,过期时间(数字秒, 或 timedelta对象)
"""
# conn.setex("age", 3, 18)

"""
psetex(name, time_ms, value)
设置值
参数
time_ms, 过期时间(数字毫秒 或 timedelta对象)
"""

# conn.psetex("<", 30000, 19)
# print(conn.get(":"))

"""
mset(*args, **kwargs)
 mset(k1='v1', k2='v2')
"""
# conn.mset({'name': "呵呵", "age": "18"})

"""
get(name)
获取值

"""
# print(conn.get("name"))
# print(str(conn.get("name"), encoding="utf8"))

3dd65a914a3c5c688e8c7466672ba8f

"""
mget(keys, *args)
批量获取
如 mget("k1", "k2)
或 mget(["k1","k2")
"""
# print(conn.mget(["name", "age"]))  # [b'\xe5\x91\xb5\xe5\x91\xb5', b'18']

"""
getset(name, value)
设置新值并获取原来的值
"""
# res = conn.getset("age", 200)
# print(res)

5d6e9b9848f3adc4268af087243ca90

"""
getrange(key, start, end)
获取子序列
参数:
name, Redis 的name
start, 起始位置(字节)
end,  结束位置(字节)
"""
# print(conn.get("name"))
# res = conn.getrange("name", 0, 0)
# print(res)

c1d193f539253f3470ade7f366f5b7e

"""
setrange(name, offset, value)
修改字符串内容,从指定字符串索引开始向后替换(新值太长时,则向后添加)
参数:
    offset,字符串索引,字节(一个汉字三个字节)
    value,要设置的值
"""
# conn.setrange("name",0, "李")
# print(str(conn.get("name"), encoding="utf8"))


"""
setbit(name, offset, value)
# 对name对应值的二进制表示的位进行操作
 
# 参数:
    # name,redis的name
    # offset,位的索引(将值变换成二进制后再进行索引)
    # value,值只能是 1 或 0
 
# 注:如果在Redis中有一个对应: n1 = "foo",
        那么字符串foo的二进制表示为:01100110 01101111 01101111
    所以,如果执行 setbit('n1', 7, 1),则就会将第7位设置为1,
        那么最终二进制则变成 01100111 01101111 01101111,即:"goo"

"""

"""
getbit(name, offset)
# 获取name对应的值的二进制表示中的某位的值 (0或1)
"""

"""
bitcount(key, start=None, end=None)

# 获取name对应的值的二进制表示中 1 的个数
# 参数:
    # key,Redis的name
    # start,位起始位置
    # end,位结束位置
"""

"""
bitop(operation, dest, *keys)
# 获取多个值,并将值做位运算,将最后的结果保存至新的name对应的值
 
# 参数:
    # operation,AND(并) 、 OR(或) 、 NOT(非) 、 XOR(异或)
    # dest, 新的Redis的name
    # *keys,要查找的Redis的name
 
# 如:
    bitop("AND", 'new_name', 'n1', 'n2', 'n3')
    # 获取Redis中n1,n2,n3对应的值,然后讲所有的值做位运算(求并集),然后将结果保存 new_name 对应的值中

"""

"""
strlen(name)
返回name对应值的字节长度(一个汉字3个字节)

"""
# print(conn.strlen("name"))  # 6

"""
incr(self, name, amount=1)

自增 name对应的值, 当name不存在时, 则创建name=amount,否则,
则自增
参数:
name.Redis的name
amount.自增数(必须是整数)
"""
# conn.incr("age")  # 不传amount默认自增 1

"""
incrbyfloat(self, name, amount=1.0)
# 自增 name对应的值,当name不存在时,则创建name=amount,否则,则自增。
 
# 参数:
    # name,Redis的name
    # amount,自增数(浮点型)
"""

"""
decr(self, name, amount=1)
# 自减 name对应的值,当name不存在时,则创建name=amount,否则,则自减。
 
# 参数:
    # name,Redis的name
    # amount,自减数(整数)

但是 float 自减1报错 , 
"""
# conn.incrbyfloat("age", amount=1.2)

# conn.decr("age")  # value is not an integer or out of range

"""
append(key, value)
在redis name 对应的值后面追加内容

参数:
    key, redis 的name
    value, 要追加的字符串
"""

conn.append("name", "handsome")
print(conn.get("name"))  # 李呵handsome

redis之List操作

list操作,redis中的list在内存中按照一个name对应一个list来存储

from redis import Redis
from pool import POOL

conn = Redis(connection_pool=POOL)

'''
1 lpush(name, values)
2 rpush(name, values) 表示从右向左操作
3 lpushx(name, value)
4 rpushx(name, value) 表示从右向左操作
5 llen(name)
6 linsert(name, where, refvalue, value))
7 r.lset(name, index, value)
8 r.lrem(name, value, num)
9 lpop(name)
10 rpop(name) 表示从右向左操作
11 lindex(name, index)
12 lrange(name, start, end)
13 ltrim(name, start, end)
14 rpoplpush(src, dst)
15 blpop(keys, timeout)
16 r.brpop(keys, timeout),从右向左获取数据
17 brpoplpush(src, dst, timeout=0)

'''

"""
lpush(name,values)
在name对应的list中添加元素,每个新的元素都添加到列表的最左边

如:
    r.lpush("oo", 11,22,33)
rpush(name, values) 表示从右向左操作
"""

conn.lpush("hobby", "篮球", "足球")

image-20230308172943953

"""
lpushx(name, value)
在name对应的list中添加元素,只有name已经存在时,值添加到列表的

相当于 set的 xx 只有存在才会修改
"""
conn.lpushx("hobby", "呵呵呵")  #
conn.lock("userinfo", "撒旦卡")
"""
llen(name)
name对应的list元素个数

"""
print(conn.llen("hobby"))  # 4
"""
linsert(name, where, refvalue, value)
在name对应的列表的某一个前或后插入一个新值
参数:
    name, redis的name
    where, BEFORE或AFTER(小写也可以)
    refvalue, 标杆值,即:在它前后插入数据(如果存在多个标杆值,以找到的第一个为准)
    value,要插入的数据
"""
# conn.linsert("hobby", "before", "呵呵呵", "李金科")

"""
lset(name, index, value)
对name对应的list中的某一个搜索引位置重新赋值
参数:
    name, redis的name
    index, list的索引位置
    value, 要设置的值
"""
# conn.lset("hobby", 1, "芜湖起飞")
# 会把 hobby 的索引1 的值 修改为 芜湖起飞


"""
lrem(name, count, value, )
对name对应的list中删除指定的值
参数:
name, redis的name
value, 要删除的值
num, num=0, 删除列表中所有指定的值
    num=2,从前到后, 删除2个
    num=-2, 从后往前删除2个
"""
# conn.lrem("hobby", 1, "李金科")
# 从左往右把李金科删除掉


"""
lpop(name)
在name对应的列表的左侧获取第一个元素并在列表中移除,返回值则是第一个元素

"""
# print(conn.lpop("hobby"))

"""
lindex(name, index)
在name对应的列表中根据索引获取列表元素
"""
# print(conn.lindex("hobby", 0))

"""
lrange(name, start, end)
在name, redis的name
start,索引的起始位置,
end, 索引的结束位置
"""
# print(conn.lrange("hobby", 0, 0))  # 这是一个闭区间就是区第一个值, 取出来是一个列表

"""
ltrim(name, start, end)
在name对应的列表中留下在start-end索引之间的值
name, redis的name
start, 索引的起始位置
end, 索引结束位置(大于列表长度,则代表不移除任何)
"""
# conn.ltrim("hobby", 0, 2)

4dc9e777a22434a64fd3b7a54349592 f9da80697005dcb3be7a31f95e1dca5

"""
rpoplpush(src, dst)
从一个列表中取出最右边的元素,同时将其添加到另一个列表的最左边
参数:
src,要取数据的列表的name
dst,要添加数据的列表的name
"""
# conn.lpush("hobby1", "足球")
# conn.rpoplpush("hobby1", "hobby")

4756cacf3ce48f1f84dfe273da3c5ed

3c6f28114e10dc30f267571fe08b34f

"""
blpop(keys, timeout)
将多个列表排列,按照从左到右去pop对应得元素
参数:
keys, redis的name的集合
timeout, 超时时间, 当元素搜友列表的元素获取完之后,阻塞等待
列表内有数据的时间(秒), 0表示永远阻塞
# 更多:
    # r.brpop(keys, timeout),从右向左获取数据
爬虫实现简单分布式:多个url放到列表里,往里不停放URL,程序循环取值,但是只能一台机器运行取值,可以把url放到redis中,多台机器从redis中取值,爬取数据,实现简单分布式

"""


"""
# 从一个列表的右侧移除一个元素并将其添加到另一个列表的左侧
 
# 参数:
    # src,取出并要移除元素的列表对应的name
    # dst,要插入元素的列表对应的name
    # timeout,当src对应的列表中没有数据时,阻塞等待其有数据的超时时间(秒),0 表示永远阻塞

"""

redis之hash

'''
1 hset(name, key, value)
2 hmset(name, mapping)
3 hget(name,key)
4 hmget(name, keys, *args)
5 hgetall(name)
6 hlen(name)
7 hkeys(name)
8 hvals(name)
9 hexists(name, key)
10 hdel(name,*keys)
11 hincrby(name, key, amount=1)
12 hincrbyfloat(name, key, amount=1.0)
13 hscan(name, cursor=0, match=None, count=None)
14 hscan_iter(name, match=None, count=None)

'''
import redis

conn = redis.Redis()

"""
hset(name,key, value)
name对应的hash中设置一个键值对(不存在则创建,否则,修改)

参数
    # name,redis的name
    # key,name对应的hash中的key
    # value,name对应的hash中的value
 
# 注:
    # hsetnx(name, key, value),当name对应的hash中不存在当前key时则创建(相当于添加)   
"""
# conn.hset("userinfo", "name", "lqz")


"""
hmset(name, mapping)
在name,redis的name
mapping,字典,如:{"k1":'v1',"k2":'v2'}
"""
# conn.hmset("userinfo", {"age": '18', "芜湖": "起飞"})   # 虽然报错但还是添加进去了

"""
hget(name,key)
# 在name对应的hash中获取根据key获取value
"""
# res = conn.hget("userinfo", "age")
# print(res)


"""
hmget(name, keys, *args)
在name对应的hash中获取多个key的值 
参数:
    name, redis对应的name
    keys,要获取key集合,如:['k1', 'k2', 'k3']
    # *args,要获取的key,如:k1,k2,k3
"""

# res = conn.hmget("userinfo", ["name", 'age'])
#
# res1 = conn.hmget("userinfo", "name", "age")
# print(res)
# print(res1)

"""
hgetall(name)
获取name对应hash的索引


"""
# res = conn.hgetall("userinfo")
# print(res)

"""
hlen(name)
获取name对应hash中键值对的个数
"""
# res = conn.hlen("userinfo")  # 1
# print(res)

"""
hkeys(name)
获取name对应的hash中所有的key的值
"""
# res = conn.hkeys("userinfo")
# print(res)  # [b'name']

"""
hvals(name)
获取name对应的hash中所有的values值
"""
# res = conn.hvals("userinfo")
# print(res)  # [b'lqz']


"""
hexists(name, key)
检查name对应的hash是否存在当前传入的key
"""
# res = conn.hexists("userinfo", "age")  # False
# res1 = conn.hexists("userinfo", "name")  # True
#
# print(res)
# print(res1)

"""
hdel(name, *key)
将name对应的hash中指定key的键值对删除
"""
# res = conn.hdel("userinfo", "name")  # 1
# res1 = conn.hdel("userinfo", "123")  # 0
# print(res1)


"""
hincrby(name, key, amout=1)
自增name对应的hash中指定key的值,不存在则创建key=amout
参数:
    name, redis中的name
    key, hash对应的key
    amount, 自增数(整数)
"""
# res = conn.hincrby("userinfo", "age")


"""
hincrbyfloat(name, key, amount=1.0)
自增name对应的hash中的指定key的值,不存在则创建key=amout
参数:
    name, redis 中的name
    key, hash对应的key
    amount, 自增数(浮点数)
    自增name对应的hash中指定key
"""
# res = conn.hincrbyfloat("userinfo", "age")
# print(res)


"""
hscan(name, cursor=0, match=None, cout=None)
增量式迭代获取,对于数据大的数据非常有用,hscan可以实现分片的
获取数据,并非一次性将数据全部获取完,从而放置内存被撑爆
参数:
    name redis的name
    cursor,游标(基于游标分批获取数据)
    match,匹配指定key,默认None,表示所有的key
    count,每次分片最少获取个数,默认None表示采用Redis的默认分片个数
如:
    # 第一次:cursor1, data1 = r.hscan('xx', cursor=0, match=None, count=None)
    # 第二次:cursor2, data1 = r.hscan('xx', cursor=cursor1, match=None, count=None)
    # ...
    # 直到返回值cursor的值为0时,表示数据已经通过分片获取完毕

"""

# res = conn.hscan("userinfo", count=10)
# print(res)

"""
hscan_iter(name, match=None, count=None)
# 利用yield封装hscan创建生成器,实现分批去redis中获取数据
 
# 参数:
    # match,匹配指定key,默认None 表示所有的key
    # count,每次分片最少获取个数,默认None表示采用Redis的默认分片个数
 
# 如:
    # for item in r.hscan_iter('xx'):
    #     print item

"""

redis其他操作

''' 通用操作,不指定类型,所有类型都支持
delete(*names)

# 根据删除redis中的任意数据类型
exists(name)

# 检测redis的name是否存在

keys(pattern='*')

# 根据模型获取redis的name

# 更多:
    # KEYS * 匹配数据库中所有 key 。
    # KEYS h?llo 匹配 hello , hallo 和 hxllo 等。
    # KEYS h*llo 匹配 hllo 和 heeeeello 等。
    # KEYS h[ae]llo 匹配 hello 和 hallo ,但不匹配 hillo

expire(name ,time)

# 为某个redis的某个name设置超时时间
rename(src, dst)

# 对redis的name重命名为
move(name, db))

# 将redis的某个值移动到指定的db下
randomkey()

# 随机获取一个redis的name(不删除)
type(name)

# 获取name对应值的类型
scan(cursor=0, match=None, count=None)
scan_iter(match=None, count=None)

# 同字符串操作,用于增量迭代获取key
'''

import redis

conn = redis.Redis()

# conn.set("name", "lqz")
# conn.set("age", "19")
#
# conn.lpush("hobby", "篮球", "足球")

# conn.delete("age")  # 删除
# res = conn.exists("age")  # 0
# print(res)
# # *是匹配所有 ?是匹配单个
# res1 = conn.keys("*")  # [b'hobby', b'name', b'userinfo']
# print(res1)
# res2 = conn.keys("?ame")  # [b'name']
# print(res2)

# conn.expire("name", 2)  # 给 name设置一个过期时间 2秒
#
# conn.rename("userinfo", "userinfo1")  # 重命名
#
# conn.move('name', 1)  # 移动到 1数据库

# res = conn.randomkey()  # 随机一个key  b'hobby'
# print(res)

# res = conn.type("hobby")  # b'list'
# res1 = conn.type("userinfo1")  # b'hash'
# print(res)
# print(res1)

redis管道

事务----》四大特性:

  • 原子性
  • 一致性
  • 隔离性
  • 持久性

redis支持事务吗?

单实例才支持所谓的事务,支持事务是基于管道的

  • 执行命令 一条一条执行

    import redis
    
    conn = redis.Redis()
    p = conn.pipeline(transaction=True)
    p.multi()
    # conn.set("p1", 500)
    # conn.set("p2", 500)
    """
    如果在他们转账的时候报错了
    
    """
    conn.decr("p1", 100)
    raise Exception()
    conn.incr("p2", 100)
    
    conn.close()
    

    image-20230308210539804

    p1 减少了

    但是 p2没有增加

    image-20230308210611409

    用了管道

    import redis
    
    conn = redis.Redis()
    p = conn.pipeline(transaction=True)  # 产生一条管道
    p.multi()
    p.decr("p1", 100)  # 放到管道里面
    p.incr("p2", 100)  # 放到管道里面
    # raise Exception
    p.execute()  # 只有执行这一条代码才会执行 那两条代码
    conn.close()
    

django中使用redis

方式一:自定义包方案(通用的, 不针对框架,所有框架都可以使用)

​ 第一步 : 写一个pool.py

​ 产生一个连接池

​ 第二步,以后在别的地方,直接导入使用即可

在utils文件里面创建一个py文件 里面写

import redis

POOL = redis.ConnectionPool(max_connections=10)

在视图函数中写

from redis import Redis
from utils import POOL
from django.http import JsonResponse


def redis_test(reqeust):
    conn = Redis(connection_pool=POOL)
    conn.incr("p2", 100)
    res = conn.get("p2")
    return JsonResponse({"name": str(res)})

方式二

  • 方案一:django的缓存使用redis

    -settings.py中配置

    需要先下载 pip install django-redis

    settings.py

    CACHES = {
        "default": {
            "BACKEND": "django_redis.cache.RedisCache",
            "LOCATION": "redis://127.0.0.1:6379",
            "OPTIONS": {
                "CLIENT_CLASS": "django_redis.client.DefaultClient",
                "CONNECTION_POOL_KWARGS": {"max_connections": 100}
                # "PASSWORD": "123",
            }
        }
    }
    

    view层

    from django.core.cache import cache
    
    
    def redis_test(reqeust):
        cache.set("p3", 100)
        res = cache.get("p3")
        return JsonResponse({"name": res})
    

    image-20230308214432428

       -在使用redis的地方:cache.set('count',  res+1)
       -pickle序列化后,存入的
    
  • 方案二:使用第三方 模块django-redis模块

    from django_redis import get_redis_connection
    
    
    def redis_test(reqeust):
        conn = get_redis_connection()
        res = conn.incr("age")
        return JsonResponse({"name": str(res)})
    

    image-20230308214825576

celery介绍和按照

Celery是什么

翻译过来是,芹菜 的意思,跟芹菜没有关系

框架:服务,python的框架,跟django无关

能用来做什么:

  1. 异步任务
  2. 定时任务
  3. 延时任务

理解celery的运行原理

"""
1. 可以不依赖任何服务器,通过自身命令,启动服务
2.celery服务为其他项目服务提供异步解决任务需求的

注: 会有两个服务同时运行,一个是项目服务,一个是celery服务,项目服务将需要异步处理的任务交给celery服务,celery就会在需要时异步完成项目的需求

人是一个独立运行的服务  | 医院也是一个独立运行的服务
正常情况下,人可以完成所有健康情况的动作,不需要医院的参与;但当人生病时,就会被医院接收,解决人生病问题
人生病的处理方案交给医院来解决,所有人不生病时,医院独立运行,人一旦生病,一眼就来解决人生病需要治病的需求
"""

celery架构(Broker, backend 都使用redis)

  1. 任务中间件Broker(中间件),其他服务提交的异步任务,放在里面排队

    需要借助于第三方 redis rabbitmq

  2. 任务执行单元worker 真正执行异步任务的进程

    celery提供

  3. 结束存储 backend 结果存储, 函数的返回结果,函数的返回结果存到 backend中

    需要借助于第三方:redis,msyql

使用场景

  1. 异步执行:解决耗时任务
  2. 延时执行:解决延时任务
  3. 定时执行:解决周期(周期)任务

celery 不支持win,通过evnetlet支持在win上运行

celery快速使用

安装 --》安装完成,会有一个可执行文件 celery

pip install celery

win:pip install eventlet

快速使用

第一步:新建 main.py

from celery import Celery

# 提交的异步任务,放在里面
broker = 'redis://127.0.0.1:6379/1'

# 执行完的结果,放在这里
backend = 'redis://127.0.0.1:6379/2'

app = Celery('test', broker=broker, backend=backend)


@app.task
def add(a, b):
    import time
    time.sleep(3)
    print('------',a + b)
    return a + b

第二步提交任务

在外部创建一个s1 提交任务

# 提交任务

from main import add

# 同步调用
# res = add(5, 6)
# print(res)

# 异步调用
res = add.delay(5,6)   #原来add的参数,直接放在delay中传入即可
print(res)  # 704b5a2a-cf16-4870-a4d4-a84dca099b8e

第三步:启动worker

win:
   -4.x之前版本
	celery worker -A main -l info -P eventlet
   -4.x之后
	celery  -A main  worker -l info -P eventlet
mac:
   celery  -A main  worker -l info

image-20230308230800514

第四步:worker会执行消息中间件中的任务,把结果存起来

第五步:咱们要看执行结果,拿到执行的结果

# 查看结果

from main import app

from celery.result import AsyncResult

id = '704b5a2a-cf16-4870-a4d4-a84dca099b8e'
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('任务已经开始被执行')

image-20230308231017307

celery包结构

project
    ├── celery_task  	# celery包
    │   ├── __init__.py # 包文件
    │   ├── celery.py   # celery连接和配置相关文件,且名字必须交celery.py
    │   └── tasks.py    # 所有任务函数
    ├── add_task.py  	# 添加任务
    └── get_result.py   # 获取结果

第一步:新建包celery_task

在包下新建【必须叫celery】的py文件,celery.py写代码

第二步:在包内部,写task,任务异步任务

task.py文件中

from .celery import app

@app.task
def add(a, b):
    print("我执行了, 我是add")
    return a + b

task1.py

from .celery import app

@app.task
def send_sms():
    print("发送短信")
    return True

第三步:启动worker ,包所在目录下

在这个包的上级目录

​ celery -A celery_task worker -l info -P eventlet

image-20230308233148676

第四步:其他程序 提交任务,被提交到中间件中,等待worker执行,因为worker启动了,就会被worker执行

在外部创建一个 main1.py

image-20230308233231263

from celery_t1.task import add
from celery_t1.task1 import send_sms

res1 = add.delay(1, 2)  # a93bc96a-de51-447e-ac66-a0334595bbe2
res2 = send_sms.delay()  # fe28b0af-3d4c-44d5-9899-9938c9e9f89c
print(res1)
print(res2)

提交任务

第五步 worker会自动执行,结果存到backend中

第六步:我们查看结构

from celery_t1.celery import app

from celery.result import AsyncResult

# id = 'fe28b0af-3d4c-44d5-9899-9938c9e9f89c'
id = 'b0b5cfb0-aa5d-42a8-8aba-907894c2e520'
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('任务已经开始被执行')
posted @ 2023-03-08 23:40  可否  阅读(17)  评论(0)    收藏  举报