def _get_service_names(self):
"""
Get a list of service names from Sentinel. Tries Sentinel hosts until one succeeds; if none succeed,
raises a ConnectionError.
:return: the list of service names from Sentinel.
"""
master_info = None
connection_errors = []
for sentinel in self._sentinel.sentinels:
# Unfortunately, redis.sentinel.Sentinel does not support sentinel_masters, so we have to step
# through all of its connections manually
try:
master_info = sentinel.sentinel_masters()
break
except (redis.ConnectionError, redis.TimeoutError) as e:
connection_errors.append('Failed to connect to {} due to error: "{}".'.format(sentinel, e))
continue
if master_info is None:
raise redis.ConnectionError(
'Could not get master info from Sentinel\n{}:'.format('\n'.join(connection_errors))
)
return list(master_info.keys())
python类sentinel()的实例源码
def _get_connection(self, index=None):
if index is None:
index = self._get_random_index()
if not 0 <= index < self._ring_size:
raise ValueError(
'There are only {count} hosts, but you asked for connection {index}.'.format(
count=self._ring_size,
index=index,
)
)
for i in range(self._sentinel_failover_retries + 1):
try:
return self._get_master_client_for(self._services[index])
except redis.sentinel.MasterNotFoundError:
self.reset_clients() # make sure we reach out to get master info again on next call
if i == self._sentinel_failover_retries:
raise CannotGetConnectionError('Master not found; gave up reloading master info after failover.')
self._get_counter('backend.sentinel.master_not_found_retry').increment()
time.sleep((2 ** i + random.random()) / 4.0)
def _get_service_names(self):
"""
Get a list of service names from Sentinel. Tries Sentinel hosts until one succeeds; if none succeed,
raises a ConnectionError.
"""
master_info = None
connection_errors = []
for sentinel in self._sentinel.sentinels:
# Unfortunately, redis.sentinel.Sentinel does not support sentinel_masters, so we have to step
# through all of its connections manually
try:
master_info = sentinel.sentinel_masters()
break
except (redis.ConnectionError, redis.TimeoutError) as e:
connection_errors.append("Failed to connect to {}: {}".format(sentinel, e))
continue
if master_info is None:
raise redis.ConnectionError(
"Could not get master info from sentinel\n{}.".format("\n".join(connection_errors)))
return list(master_info.keys())
### Connection handling ####
def __init__(
self,
hosts=None,
connection_kwargs=None,
sentinel_services=None,
sentinel_refresh_interval=0, # noqa TODO Unused; remove this after all settings have been changed
sentinel_failover_retries=0,
):
# Master client caching
self._master_clients = {}
# Master failover behavior
assert sentinel_failover_retries >= 0
self._sentinel_failover_retries = sentinel_failover_retries
self._sentinel = redis.sentinel.Sentinel(self._setup_hosts(hosts), **(connection_kwargs or {}))
if sentinel_services:
self._validate_service_names(sentinel_services)
self._services = sentinel_services
else:
self._services = self._get_service_names()
self._ring_size = len(self._services)
self._connection_index_generator = itertools.cycle(range(self._ring_size))
self.metrics_counter_getter = None
super(SentinelRedisClient, self).__init__(ring_size=len(self._services))
def _get_master_client_for(self, service_name):
if service_name not in self._master_clients:
self._get_counter('backend.sentinel.populate_master_client').increment()
self._master_clients[service_name] = self._sentinel.master_for(service_name)
return self._master_clients[service_name]
def sentinel_exists():
if not SENTINEL_HOSTS:
return False
sen = redis.sentinel.Sentinel(SENTINEL_HOSTS)
try:
sen.discover_master(SERVICE_NAMES[0])
except MasterNotFoundError:
return False
return True
# Default conformance tests
def _setup_hosts(self, hosts):
# Override to only accept tuples, since the redis.sentinel.Sentinel does not accept URLs
if not hosts:
hosts = [("localhost", 26379)]
final_hosts = list()
if isinstance(hosts, six.string_types):
# user accidentally used one host string instead of providing a list of hosts
raise ValueError("ASGI Redis hosts must be specified as an iterable list of hosts.")
for entry in hosts:
if isinstance(entry, six.string_types):
raise ValueError("Sentinel Redis host entries must be specified as tuples, not strings.")
else:
final_hosts.append(entry)
return final_hosts