-
Notifications
You must be signed in to change notification settings - Fork 8.8k
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
feat: support redis sentinel mode (#7756)
- Loading branch information
Showing
6 changed files
with
162 additions
and
21 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,26 +1,83 @@ | ||
import redis | ||
from redis.connection import Connection, SSLConnection | ||
from redis.sentinel import Sentinel | ||
|
||
redis_client = redis.Redis() | ||
|
||
class RedisClientWrapper(redis.Redis): | ||
""" | ||
A wrapper class for the Redis client that addresses the issue where the global | ||
`redis_client` variable cannot be updated when a new Redis instance is returned | ||
by Sentinel. | ||
This class allows for deferred initialization of the Redis client, enabling the | ||
client to be re-initialized with a new instance when necessary. This is particularly | ||
useful in scenarios where the Redis instance may change dynamically, such as during | ||
a failover in a Sentinel-managed Redis setup. | ||
Attributes: | ||
_client (redis.Redis): The actual Redis client instance. It remains None until | ||
initialized with the `initialize` method. | ||
Methods: | ||
initialize(client): Initializes the Redis client if it hasn't been initialized already. | ||
__getattr__(item): Delegates attribute access to the Redis client, raising an error | ||
if the client is not initialized. | ||
""" | ||
|
||
def __init__(self): | ||
self._client = None | ||
|
||
def initialize(self, client): | ||
if self._client is None: | ||
self._client = client | ||
|
||
def __getattr__(self, item): | ||
if self._client is None: | ||
raise RuntimeError("Redis client is not initialized. Call init_app first.") | ||
return getattr(self._client, item) | ||
|
||
|
||
redis_client = RedisClientWrapper() | ||
|
||
|
||
def init_app(app): | ||
global redis_client | ||
connection_class = Connection | ||
if app.config.get("REDIS_USE_SSL"): | ||
connection_class = SSLConnection | ||
|
||
redis_client.connection_pool = redis.ConnectionPool( | ||
**{ | ||
"host": app.config.get("REDIS_HOST"), | ||
"port": app.config.get("REDIS_PORT"), | ||
"username": app.config.get("REDIS_USERNAME"), | ||
"password": app.config.get("REDIS_PASSWORD"), | ||
"db": app.config.get("REDIS_DB"), | ||
"encoding": "utf-8", | ||
"encoding_errors": "strict", | ||
"decode_responses": False, | ||
}, | ||
connection_class=connection_class, | ||
) | ||
redis_params = { | ||
"username": app.config.get("REDIS_USERNAME"), | ||
"password": app.config.get("REDIS_PASSWORD"), | ||
"db": app.config.get("REDIS_DB"), | ||
"encoding": "utf-8", | ||
"encoding_errors": "strict", | ||
"decode_responses": False, | ||
} | ||
|
||
if app.config.get("REDIS_USE_SENTINEL"): | ||
sentinel_hosts = [ | ||
(node.split(":")[0], int(node.split(":")[1])) for node in app.config.get("REDIS_SENTINELS").split(",") | ||
] | ||
sentinel = Sentinel( | ||
sentinel_hosts, | ||
sentinel_kwargs={ | ||
"socket_timeout": app.config.get("REDIS_SENTINEL_SOCKET_TIMEOUT", 0.1), | ||
"username": app.config.get("REDIS_SENTINEL_USERNAME"), | ||
"password": app.config.get("REDIS_SENTINEL_PASSWORD"), | ||
}, | ||
) | ||
master = sentinel.master_for(app.config.get("REDIS_SENTINEL_SERVICE_NAME"), **redis_params) | ||
redis_client.initialize(master) | ||
else: | ||
redis_params.update( | ||
{ | ||
"host": app.config.get("REDIS_HOST"), | ||
"port": app.config.get("REDIS_PORT"), | ||
"connection_class": connection_class, | ||
} | ||
) | ||
pool = redis.ConnectionPool(**redis_params) | ||
redis_client.initialize(redis.Redis(connection_pool=pool)) | ||
|
||
app.extensions["redis"] = redis_client |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters