-
Notifications
You must be signed in to change notification settings - Fork 0
/
multi_host.py
126 lines (99 loc) · 3.72 KB
/
multi_host.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
# -*- coding: utf8 -*-
from __future__ import print_function, absolute_import, unicode_literals
import six
if six.PY3:
from urllib.parse import urlparse
else:
from urlparse import urlparse
import requests
import logging
from singleton import Singleton
logger = logging.getLogger(__name__)
class MultiHostError(Exception):
def __init__(self, hostname):
super(MultiHostError, self).__init__()
self.host = hostname
def __str__(self):
return "Unable to open MultiHost %s" % self.host
def __unicode__(self):
return "Unable to open MultiHost %s" % self.host
class MultiHostHandlerException(Exception):
def __init__(self, hostname):
super(MultiHostHandlerException, self).__init__()
self.host = hostname
def __str__(self):
return "Host %s not registered" % self.host
def __unicode__(self):
return "Host %s not registered" % self.host
@Singleton
class MultiHostHandler(object):
def __init__(self):
self.hosts = {}
def open_url(self, url, timeout):
parsed_url = urlparse(url)
if not parsed_url.scheme:
parsed_url.scheme = "http"
hosts = self.hosts.get(parsed_url.hostname)
if hosts:
return hosts.open_path(parsed_url.path, parsed_url.scheme, timeout)
else:
logger.info("No MultiHost registered for url %s." % (url, ))
return requests.get(url).text
@classmethod
def register_multi_host(cls, hostname, extra_hosts):
"""
:param basestring hostname:
:param list extra_hosts:
"""
self = MultiHostHandler.Instance()
if not self.hosts.get(hostname):
self.hosts[hostname] = MultiHost(hostname, extra_hosts)
class Host(object):
def __init__(self, host, timeout=10, retries=1):
self.host = host
self._last_access_time = 0
self.is_accessible = True
self.timeout = timeout
self.retries = retries
def last_access_time(self):
if self.is_accessible:
return self._last_access_time
return 999999
def open_path(self, path, scheme="http"):
for i in range(self.retries):
try:
url = scheme + "://" + self.host + path
resp = requests.get(url=url, timeout=(2, self.timeout - 2))
if resp.status_code in (200, 201):
data = resp.text
return data
except requests.exceptions.Timeout as e:
logging.warning("request timed out, retry=%d/%d" % (i + 1, self.retries))
except requests.exceptions.RequestException as e:
logger.exception("error with %s: %s", self.host, e)
break
# raise e
self._last_access_time = 0
self.is_accessible = False
return None
class MultiHost(object):
def __init__(self, original_host, extra_hosts=[]):
self.original = original_host
self.hosts = [Host(original_host)]
for extra_host in extra_hosts:
if not isinstance(extra_host, Host):
extra_host = Host(extra_host)
self.hosts.append(extra_host)
def open_path(self, path, scheme="http", timeout=None):
do_sort = False
for host in self.hosts:
data = host.open_path(path, scheme)
if data:
logger.debug("%s used as host for %s", host.host, self.original)
if do_sort:
self.hosts.sort(key=lambda h: h.last_access_time())
return data
else:
logger.debug("%s unusable as host for %s", host.host, self.original)
do_sort = True
raise MultiHostError(self.original)