在 Python 中命名当前 Redis master (运行 in Docker)

Naming current Redis master (run in Docker) in Python

如果主服务器宕机,如何Python故障转移到Redis从服务器?

在当前配置下,Sentinels 选出一个新的 master,但是 Python 写入停止。

我假设我不应该像 docker-compose.yml 文件中那样使用 redis-master;还有哪些选择?

在网络模式下,我无法让 Sentinels 相互识别。

docker-compose.yml

version: '3'

services:

  redis-master:
    container_name: redis-master
    image: redis:latest
    command: redis-server --port 6379
    ports:
      - "6379:6379"
    volumes:
      - .:/app

  redis-slave:
    container_name: redis-slave
    image: redis:latest
    command: redis-server --slaveof redis-master 6379 --protected-mode no
    volumes:
       - .:/app

  sentinel-1:
    container_name: sentinel-1
    build: sentinel
    environment:
      - SENTINEL_DOWN_AFTER=5000
      - SENTINEL_FAILOVER=5000

  sentinel-2:
    container_name: sentinel-2
    build: sentinel
    environment:
      - SENTINEL_DOWN_AFTER=5000
      - SENTINEL_FAILOVER=5000

  sentinel-3:
    container_name: sentinel-3
    build: sentinel
    environment:
      - SENTINEL_DOWN_AFTER=5000
      - SENTINEL_FAILOVER=5000

  app:
    container_name: python-app
    image: pyredis
    command: python app.py

Python 应用程序:

import redis
import random
import time

r = redis.StrictRedis(host="redis-master", port=6379, db=0)

for i in range(0, 1000):
    timestamp = time.strftime("%Y-%m-%d %H:%M:%S", time.gmtime())
    num = random.randint(1,101)
    r.set(timestamp, num)
    time.sleep(2)

谢谢,欢迎任何意见。

您需要一个函数来重新定义您的 r 值:

import redis
from redis.exceptions import ConnectionError

def get_connection(host):
    global r
    other_host = "redis-slave" if "master" in host else "redis-master"

    try:
        r = redis.StrictRedis(host=host, port=6379, db=0)
    except ConnectionError:
        # connection against host failed, try other_host
        host = other_host
        r = redis.StrictRedis(host=host, port=6379, db=0)
    return host


host = get_connection('redis-master')

for i in range(0, 1000):
    timestamp = time.strftime("%Y-%m-%d %H:%M:%S", time.gmtime())
    num = random.randint(1,101)
    try:
        r.set(timestamp, num)
    # handles explicitly redis.exceptions.ConnectionError
    except ConnectionError: 
        host = get_connection(host)
        r.set(timestamp, num)
    time.sleep(2)

在master宕机的情况下,会调用那个函数,将连接切换到slave,反之亦然。如果两个都down了,被外层except捕获的Exception会被raise,程序会崩溃

编辑

对于通常为 BIG NO-NO 的可变默认值,这可能是一个非常有趣的用例。 注意,我仍然不会在任何生产代码中实现它,只是在这里显示副作用

双端队列支持就地旋转,因此您可以使用它来切换 hostother_host,如下所示:

from collections import deque

d = deque(('redis-master', 'redis-slave'), maxlen=2)

d
deque(['redis-master', 'redis-slave'], maxlen=2)

d.rotate()
d
deque(['redis-slave', 'redis-master'], maxlen=2)

现在,您可以将其用作缓存并交换顺序

def get_connection(d=deque(('redis-master', 'redis-slave'), maxlen=2)):
    global r
    host, other_host = d # unpacks the two values

    try:
        r = redis.StrictRedis(host=host, port=6379, db=0)
    except ConnectionError:
        r = redis.StrictRedis(host=other_host, port=6379, db=0)
        d.rotate() # changes the order of the hosts

展示其工作原理:

def get_connection(d=deque(('redis-master', 'redis-slave'), maxlen=2)):
    print(d)
    try:
        raise ValueError # as a test
    except ValueError:
        print("Caught error")
        d.rotate()
        print(d)


get_connection()
deque(['redis-master', 'redis-slave'], maxlen=2)
Caught error
deque(['redis-slave', 'redis-master'], maxlen=2)

get_connection()
deque(['redis-slave', 'redis-master'], maxlen=2)
Caught error
deque(['redis-master', 'redis-slave'], maxlen=2)

现在您的外部程序不需要知道 host,它只需要根据需要调用重试函数:

for i in range(0, 1000):
    timestamp = time.strftime("%Y-%m-%d %H:%M:%S", time.gmtime())
    num = random.randint(1,101)
    try:
        r.set(timestamp, num)
    # handles explicitly redis.exceptions.ConnectionError
    except ConnectionError: 
        get_connection() # host doesn't need to be returned anymore
        r.set(timestamp, num)
    time.sleep(2)