【Django】Redis的使用【原创】

0. 参考

django-redis的中文文档



在Django中使用Redis有两种方式:

  • 自定义模式
  • 使用第三方组件【推荐】

1. 自定义模式

这种方式的话,不止Django可以使用,其他的框架或者是原生Python也可以使用


A. 安装依赖

pip install redis

B. Redis配置

封装成模块,可以全局使用

import redis

# 抽取封装成模块,全局使用(单例模式,redis_pool.py)
POOL = redis.ConnectionPool(host='xx.xx.xx.xx', port=6379, password='xxx', max_connections=1000)

C. 使用

# 引用全局连接池
from redis_pool import POOL

conn = redis.Redis(connection_pool=POOL)
conn.set(key, value)

2. 使用第三方组件

如果是Django的话,建议使用该方式


A. 安装依赖

pip install django-redis

B. 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, 'decode_responses': True},
            # "PASSWORD": "密码",
        },
        # 前缀
        "KEY_PREFIX": "test"
    }
}

注意:max_connections是连接池,如不需要可去掉
注意:前缀看个人需要,如不需要统一加前缀可以去掉
注意:如果decode_responses不设置为True的话,使用get_redis_connection读取的数据是bytes,需要decode为utf-8,加上该设置的话那么读取出去就不需要自己decode了,然而如果decode_responses设置为True的话,那使用下面的第二种方法cache来读取是会报错的
比如:

>>> from django_redis import get_redis_connection
>>> conn = get_redis_connection()
>>> conn.set('redis_test3', 22)
True
>>> conn.get('redis_test3')
b'22'

注意:如有多个Redis的话,可以在CACHE里面添加一个新的,区别于default即可


C. 使用

有两种方式:

  • get_redis_connection
  • cache

第一种方法:get_redis_connection
推荐使用这种方法

from django_redis import get_redis_connection
 
# 这里可以传使用哪个redis,不传默认是default
redis = get_redis_connection()
redis.get(key)
redis.set(key, value)

注意:如果decode_responses不设置为True的话,使用get_redis_connection读取的数据是bytes,需要decode为utf-8,加上该设置的话那么读取出去就不需要自己decode了
比如:

>>> from django_redis import get_redis_connection
>>> conn = get_redis_connection()
>>> conn.set('redis_test3', 22)
True
>>> conn.get('redis_test3')
b'22'

注意:使用这种方法的话,caches里面的前缀是不生效的,即设置到redis里面去,keys是什么就是什么,不会自动加上前缀

扫描二维码关注公众号,回复: 13730185 查看本文章

第二种方法:cache

from django.core.cache import cache
 
cache.get(key)
cache.set(key, value)

注意:如果caches里面的decode_responses设置为True的话,那读取是会报错的

>>> cache.get('redis_test3')
Traceback (most recent call last):
  File "c:\users\jiand\appdata\local\programs\python\python37\Lib\code.py", line 90, in runcode
    exec(code, self.locals)
  File "<console>", line 1, in <module>
  File "D:\Python\Envs\Django2Demo\lib\site-packages\django_redis\cache.py", line 87, in get
    value = self._get(key, default, version, client)
  File "D:\Python\Envs\Django2Demo\lib\site-packages\django_redis\cache.py", line 27, in _decorator
    return method(self, *args, **kwargs)
  File "D:\Python\Envs\Django2Demo\lib\site-packages\django_redis\cache.py", line 94, in _get
    return self.client.get(key, default=default, version=version, client=client)
  File "D:\Python\Envs\Django2Demo\lib\site-packages\django_redis\client\default.py", line 220, in get
    value = client.get(key)
  File "D:\Python\Envs\Django2Demo\lib\site-packages\redis\client.py", line 1579, in get
    return self.execute_command('GET', name)
  File "D:\Python\Envs\Django2Demo\lib\site-packages\redis\client.py", line 878, in execute_command
    return self.parse_response(conn, command_name, **options)
  File "D:\Python\Envs\Django2Demo\lib\site-packages\redis\client.py", line 892, in parse_response
    response = connection.read_response()
  File "D:\Python\Envs\Django2Demo\lib\site-packages\redis\connection.py", line 734, in read_response
    response = self._parser.read_response()
  File "D:\Python\Envs\Django2Demo\lib\site-packages\redis\connection.py", line 358, in read_response
    response = self.encoder.decode(response)
  File "D:\Python\Envs\Django2Demo\lib\site-packages\redis\connection.py", line 129, in decode
    value = value.decode(self.encoding, self.encoding_errors)
UnicodeDecodeError: 'utf-8' codec can't decode byte 0x80 in position 0: invalid start byte

总结:
这两种方法只能二选一,最好是统一使用一种,建议使用get_redis_connection并且cache的decode_responses设置为True
如果需要两种共用的话,那么cache里面去掉decode_responses的配置,然后使用get_redis_connection读取的时候把字节转字符串:

redis_value = self.conn.get(lock_name)

# 注意:如果是get_redis_connection的话,从redis里面读取的是bytes字符的
redis_value = redis_value.decode('utf-8') if isinstance(redis_value, bytes) else redis_value

示例代码

from django_redis import get_redis_connection

def get_private_ips(self, subnet_id, is_ignore_cache=False):
    """
    查询私有IP列表
    使用Redis缓存,保存1小时
    :param subnet_id:
    :param is_ignore_cache: 是否忽略缓存 默认是否
    :return:
    """
    key = 'HW_private_ips_area_%s_subnetId_%s' % (self.area, subnet_id)
    cache = get_redis_connection()

    # 保存1小时
    expired_time = 60 * 60 * 1
    res = cache.get(key, None)

    if not res or is_ignore_cache:
        sig = self.sig
        uri = "/v1/{0}/subnets/{1}/privateips".format(sig.ProjectId, subnet_id)
        res = self._hw_request(uri)

        if res and not res.get('code') and res.get('privateips'):
            cache.set(key, res, expired_time)

    return res

简单的Redis锁的实现:

from django_redis import get_redis_connection


class LockService:
    def __init__(self, conn=None):
        """
        如果不传连接池的话,默认读取配置的Redis作为连接池
        :param conn:
        """
        self.conn = conn if conn else get_redis_connection()

    def acquire_lock(self, lock_name, value, expire_time=60):
        """
        加锁
        如果不存在lock_name,则创建,并且设置过期时间,避免死锁
        如果存在lock_name,则刷新过期时间

        插入成功:返回True
        已存在:返回False并且刷新过期时间
        :param lock_name:
        :param value:
        :param expire_time:
        :return:
        """
        if self.conn.setnx(lock_name, value):
            # 注意:Todo 这里可能会有问题,如果程序在写入redis之后但未设置有效期之前突然崩溃,则无法设置过期时间,将发生死锁
            self.conn.expire(lock_name, expire_time)
            return True
        elif self.conn.ttl(lock_name):
            self.conn.expire(lock_name, expire_time)
        return False

    def release_lock(self, lock_name, value):
        """
        释放锁
        注意:只有value值一致才会删除,避免在并发下删除了其他进程/线程设置的锁
        :param lock_name:
        :param value:
        :return:
        """
        redis_value = self.conn.get(lock_name)

        # 注意:如果是get_redis_connection的话,从redis里面读取的是bytes字符的
        redis_value = redis_value.decode('utf-8') if isinstance(redis_value, bytes) else redis_value
        if str(redis_value) == str(value):
            self.conn.delete(lock_name)

猜你喜欢

转载自blog.csdn.net/jiandanokok/article/details/109426427