1. 背景
- django==3.2.15
- djangorestframework==3.13.1
- django-redis==5.2.0
- redis==4.5.1
当我们的Django需要使用redis sentinel模式时,会安装
pip install django-redis-sentinel
在实际使用时发现, django-redis-sentinel太久没更新,其与我们Django,django-redis之间不兼容;
2.解决方案
2.1. 手写django-redis-sentinel
2.1.1. redis_sentinel_client.py
# redis_sentinel_client.py
import logging
from typing import Any, Optional
from django_redis.client import DefaultClient
__LOG__ = logging.getLogger(__name__)
from django_redis.util import CacheKey
from redis.sentinel import Sentinel
class SentinelClient(DefaultClient):
def __init__(self, server, params, backend):
super(SentinelClient, self).__init__(server,params,backend)
self._connection_string = server
sentinel_kwargs = self._options.get("SENTINEL_KWARGS", None)
master_name, sentinel_hosts, db = self.parse_connection_string(self._connection_string)
sentinel = Sentinel(sentinels=sentinel_hosts, sentinel_kwargs=sentinel_kwargs)
host, port = sentinel.discover_master(master_name)
password = self._options.get("PASSWORD", None)
self._server = list()
self._server.append(self.ret_refis_connect_url(host, port, db, password))
slaves = sentinel.discover_slaves(master_name)
for host, port in slaves:
self._server.append(self.ret_refis_connect_url(host, port, db, password))
__LOG__.info("Finding {}".__format__(self._server))
def ret_refis_connect_url(self, host, port, db, password):
if password != None:
return "redis://:%s@%s:%s/%s"%(password, host, port, db)
return "redis://%s:%s/%s"%(host, port, db)
def parse_connection_string(self, connstring):
try:
connection_params = connstring.split("/")
# 我们约定将第一个redis host设为redis master
master_name = connection_params[0]
servers = [host_port.split(":") for host_port in connection_params[1].split(",")]
sentinel_hosts = [(host, int(port)) for host,port in servers]
db = connection_params[2]
except (ValueError, TypeError, IndexError):
raise Exception("Incorrect format '%s'"%(connstring))
return master_name, sentinel_hosts, db
def make_key(self, key: Any, version: Optional[Any] = None, prefix: Optional[str] = None):
if prefix is None:
prefix = self._backend.key_prefix
return CacheKey('%s:%s'%(prefix, key))
2.1.2. settings.py
# settings.py
# redis master: 192.168.31.175:6379
# redis sentinel_01: 192.168.31.174:6379
# redis_sentinel_02: 192.168.31.173:6379
...
CACHES = {
"default": {
"BACKEND": "django_redis.cache.RedisCache",
# 我们默认第一个host为redis master
"LOCATION": "mymaster/192.168.31.175:6379,192.168.31.174:6379,192.168.31.173:6379/0",
"OPTIONS": {
"PASSWORD": os.environ.get("REDIS_PASSWORD"),
"SENTINEL_KWARGS": {"password": os.environ.get("REDIS_SENTINEL_PASSWORD"), "socket_timeout": 3},
"CLIENT_CLASS": "redis_sentinel_client.SentinelClient",
"SERIALIZER": "django_redis.serializers.json.JSONSerializer"
},
"KEY_PREFIX": "liyuan"
}
}
...