Что мне делать, если socket.setdefaulttimeout() не работает?
Я пишу сценарий (многопоточный) для извлечения содержимого с веб-сайта, и сайт не очень стабилен, поэтому время от времени возникает запрос http, который даже не может быть отключен socket.setdefaulttimeout()
, Так как я не могу контролировать этот сайт, единственное, что я могу сделать, - это улучшить свои коды, но сейчас у меня заканчиваются идеи.
Образцы кодов:
socket.setdefaulttimeout(150)
MechBrowser = mechanize.Browser()
Header = {'User-Agent': 'Mozilla/5.0 (Windows; U; Windows NT 5.1; en-US; rv:1.9.2.8) Gecko/20100722 Firefox/3.6.8 GTB7.1 (.NET CLR 3.5.30729)'}
Url = "http://example.com"
Data = "Justatest=whatever&letstry=doit"
Request = urllib2.Request(Url, Data, Header)
Response = MechBrowser.open(Request)
Response.close()
Что я должен сделать, чтобы принудительно завершить запрос на зависание? На самом деле я хочу знать, почему socket.setdefaulttimeout(150)
не работает в первую очередь. Кто-нибудь может мне помочь?
Добавлено:(и да, проблема все еще не решена)
Хорошо, я последовал предложению Томаша и изменил коды на MechBrowser.open(Request, timeout = 60)
, но то же самое происходит. До сих пор я до сих пор получал произвольные запросы, иногда это несколько часов, а иногда - несколько дней. Что мне теперь делать? Есть ли способ заставить эти зависающие запросы выйти?
4 ответа
В то время как socket.setsocketimeout
установит время ожидания по умолчанию для новых сокетов, если вы не используете сокеты напрямую, этот параметр можно легко перезаписать. В частности, если библиотека вызывает socket.setblocking
на своем сокете это сбросит тайм-аут.
urllib2.open
имеет аргумент тайм-аута, но в тайм-ауте нет urllib2.Request
, Как вы используете mechanize
, вы должны обратиться к их документации:
Начиная с Python 2.6, urllib2 использует атрибут.timeout для внутренних объектов Request. Однако urllib2.Request не имеет аргумента конструктора тайм-аута, и urllib2.urlopen() игнорирует этот параметр. mechanize.Request имеет аргумент конструктора timeout, который используется для установки атрибута с тем же именем, а mechanize.urlopen() не игнорирует атрибут timeout.
источник: http://wwwsearch.sourceforge.net/mechanize/documentation.html
---РЕДАКТИРОВАТЬ---
Если либо socket.setsockettimeout
или время ожидания mechanize
работает с небольшими значениями, но не с более высокими, источник проблемы может быть совершенно другим. Во-первых, ваша библиотека может открывать несколько соединений (в данном случае @Cédric Julien), поэтому время ожидания применяется к каждой отдельной попытке socket.open, а если она не останавливается при первом сбое - может занять до timeout * num_of_conn
секунд. Другое дело socket.recv
: если соединение очень медленное и вам не повезло, весь запрос может занять до timeout * incoming_bytes
как с каждым socket.recv
мы можем получить один байт, и каждый такой вызов может занять timeout
секунд. Поскольку вы вряд ли пострадаете именно от этого темного сценария (один байт на секунды времени ожидания, вы должны быть очень грубым мальчиком), очень вероятно, что потребуются возрасты для очень медленных соединений и очень больших тайм-аутов.
Единственное решение, которое у вас есть, это форсировать тайм-аут для всего запроса, но здесь нет ничего общего с сокетами. Если вы работаете в Unix, вы можете использовать простое решение с ALARM
сигнал. Вы устанавливаете сигнал для повышения в timeout
секунд, и ваш запрос будет прекращен (не забудьте его поймать). Вы могли бы использовать with
заявление, чтобы сделать его простым и понятным, например:
import signal, time
def request(arg):
"""Your http request"""
time.sleep(2)
return arg
class Timeout():
"""Timeout class using ALARM signal"""
class Timeout(Exception): pass
def __init__(self, sec):
self.sec = sec
def __enter__(self):
signal.signal(signal.SIGALRM, self.raise_timeout)
signal.alarm(self.sec)
def __exit__(self, *args):
signal.alarm(0) # disable alarm
def raise_timeout(self, *args):
raise Timeout.Timeout()
# Run block of code with timeouts
try:
with Timeout(3):
print request("Request 1")
with Timeout(1):
print request("Request 2")
except Timeout.Timeout:
print "Timeout"
# Prints "Request 1" and "Timeout"
Если вы хотите быть более портативным, чем этот, вы должны использовать, например, оружие большего размера multiprocessing
, так что вы вызовете процесс для вызова вашего запроса и прекратите его, если просрочено. Поскольку это будет отдельный процесс, вам придется что-то использовать для передачи результата обратно в ваше приложение, это может быть multiprocessing.Pipe
, Вот пример:
from multiprocessing import Process, Pipe
import time
def request(sleep, result):
"""Your http request example"""
time.sleep(sleep)
return result
class TimeoutWrapper():
"""Timeout wrapper using separate process"""
def __init__(self, func, timeout):
self.func = func
self.timeout = timeout
def __call__(self, *args, **kargs):
"""Run func with timeout"""
def pmain(pipe, func, args, kargs):
"""Function to be called in separate process"""
result = func(*args, **kargs) # call func with passed arguments
pipe.send(result) # send result to pipe
parent_pipe, child_pipe = Pipe() # Pipe for retrieving result of func
p = Process(target=pmain, args=(child_pipe, self.func, args, kargs))
p.start()
p.join(self.timeout) # wait for prcoess to end
if p.is_alive():
p.terminate() # Timeout, kill
return None # or raise exception if None is acceptable result
else:
return parent_pipe.recv() # OK, get result
print TimeoutWrapper(request, 3)(1, "OK") # prints OK
print TimeoutWrapper(request, 1)(2, "Timeout") # prints None
У вас действительно нет особого выбора, если вы хотите принудительно завершить запрос через фиксированное количество секунд. socket.timeout
предоставит тайм-аут для операции с одним сокетом (connect/recv/send), но если у вас их несколько, вы можете страдать от очень длительного времени выполнения.
Из их документации:
Начиная с Python 2.6, urllib2 использует атрибут.timeout для внутренних объектов Request. Однако urllib2.Request не имеет аргумента конструктора тайм-аута, и urllib2.urlopen() игнорирует этот параметр. mechanize.Request имеет аргумент конструктора timeout, который используется для установки атрибута с тем же именем, а mechanize.urlopen() не игнорирует атрибут timeout.
Возможно, вам следует попробовать заменить urllib2.Request на mechanize.Request.
Вы можете попробовать использовать механизировать с eventlet. Это не решит проблему тайм-аута, но greenlet не блокирует, поэтому может решить вашу проблему с производительностью.
Я предлагаю простой обходной путь - переместите запрос в другой процесс, и, если он не завершится, завершите его, вызвав процесс, следующим образом:
checker = Process(target=yourFunction, args=(some_queue))
timeout = 150
checker.start()
counter = 0
while checker.is_alive() == True:
time.sleep(1)
counter += 1
if counter > timeout :
print "Son process consumed too much run-time. Going to kill it!"
kill(checker.pid)
break
просто, быстро и эффективно.