Я пытаюсь выполнить эту криптографическую задачу Matasano, которая включает в себя атаку по времени на сервер с искусственно замедленным функция сравнения строк вниз. В нем говорится об использовании «веб-фреймворка по вашему выбору», но мне не хотелось устанавливать веб-фреймворк, поэтому я решил использовать класс HTTPServer, встроенный в http.server
< /а> модуль.
Я придумал что-то, что работало, но работало очень медленно, поэтому я попытался ускорить его, используя (плохо документированный) пул потоков, встроенный в multiprocessing.dummy
. Это было намного быстрее, но я заметил одну странность: если я делаю 8 или меньше запросов одновременно, все работает нормально. Если у меня больше этого, это работает какое-то время и выдает мне ошибки в, казалось бы, случайное время. Ошибки кажутся непоследовательными и не всегда одинаковыми, но обычно в них есть Connection refused, invalid argument
, OSError: [Errno 22] Invalid argument
, urllib.error.URLError: <urlopen error [Errno 22] Invalid argument>
, BrokenPipeError: [Errno 32] Broken pipe
или urllib.error.URLError: <urlopen error [Errno 61] Connection refused>
.
Есть ли ограничение на количество подключений, которые может обработать сервер? Я не думаю, что количество потоков само по себе является проблемой, потому что я написал простую функцию, которая выполняла замедленное сравнение строк без запуска веб-сервера и вызывала ее с 500 одновременными потоками, и она работала нормально. Я не думаю, что проблема заключается в простом выполнении запросов из такого количества потоков, потому что я создал поисковые роботы, которые использовали более 100 потоков (все выполняли одновременные запросы к одному и тому же веб-сайту), и они работали нормально. Похоже, что HTTP-сервер не предназначен для надежного размещения рабочих веб-сайтов с большим объемом трафика, но я удивлен, что его так легко вывести из строя.
Я пытался постепенно удалять из своего кода то, что не имело отношения к проблеме, как я обычно делаю, когда диагностирую подобные загадочные ошибки, но в данном случае это не очень помогло. Казалось, что по мере того, как я удалял, казалось бы, несвязанный код, количество соединений, которые сервер мог обработать, постепенно увеличивалось, но не было ясной причины сбоев.
Кто-нибудь знает, как увеличить количество запросов, которые я могу сделать за один раз, или, по крайней мере, почему это происходит?
Мой код сложный, но я придумал эту простую программу, которая демонстрирует проблему:
#!/usr/bin/env python3
import os
import random
from http.server import BaseHTTPRequestHandler, HTTPServer
from multiprocessing.dummy import Pool as ThreadPool
from socketserver import ForkingMixIn, ThreadingMixIn
from threading import Thread
from time import sleep
from urllib.error import HTTPError
from urllib.request import urlopen
class FancyHTTPServer(ThreadingMixIn, HTTPServer):
pass
class MyRequestHandler(BaseHTTPRequestHandler):
def do_GET(self):
sleep(random.uniform(0, 2))
self.send_response(200)
self.end_headers()
self.wfile.write(b"foo")
def log_request(self, code=None, size=None):
pass
def request_is_ok(number):
try:
urlopen("http://localhost:31415/test" + str(number))
except HTTPError:
return False
else:
return True
server = FancyHTTPServer(("localhost", 31415), MyRequestHandler)
try:
Thread(target=server.serve_forever).start()
with ThreadPool(200) as pool:
for i in range(10):
numbers = [random.randint(0, 99999) for j in range(20000)]
for j, result in enumerate(pool.imap(request_is_ok, numbers)):
if j % 20 == 0:
print(i, j)
finally:
server.shutdown()
server.server_close()
print("done testing server")
По какой-то причине приведенная выше программа работает нормально, если в ней не более 100 потоков или около того, но мой реальный код для задачи может обрабатывать только 8 потоков. Если я запускаю его с 9, я обычно получаю ошибки подключения, а с 10 я всегда получаю ошибки подключения. Я попытался использовать concurrent.futures.ThreadPoolExecutor
, concurrent.futures.ProcessPoolExecutor
и multiprocessing.pool
вместо multiprocessing.dummy.pool
, и ни один из них не помог. Я попытался использовать простой объект HTTPServer
(без ThreadingMixIn
), но это только замедлило работу и не решило проблему. Я пытался использовать ForkingMixIn
, и это тоже не помогло.
Что мне с этим делать? Я использую Python 3.5.1 на MacBook Pro конца 2013 года под управлением OS X 10.11.3.
EDIT: я попробовал еще несколько вещей, включая запуск сервера в процессе вместо потока, как простой HTTPServer
, с ForkingMixIn
и с ThreadingMixIn
. Ни один из них не помог.
EDIT: Эта проблема более странная, чем я думал. Я попытался создать один скрипт с сервером, а другой — с большим количеством потоков, делающих запросы, и запускать их на разных вкладках в моем терминале. Процесс с сервером прошел нормально, но тот, который делал запросы, завис. Исключения составляли сочетания ConnectionResetError: [Errno 54] Connection reset by peer
, urllib.error.URLError: <urlopen error [Errno 54] Connection reset by peer>
, OSError: [Errno 41] Protocol wrong type for socket
, urllib.error.URLError: <urlopen error [Errno 41] Protocol wrong type for socket>
, urllib.error.URLError: <urlopen error [Errno 22] Invalid argument>
.
Я попробовал это с фиктивным сервером, подобным приведенному выше, и если я ограничил количество одновременных запросов до 5 или меньше, он работал нормально, но с 6 запросами клиентский процесс зависал. Были некоторые ошибки с сервера, но это продолжалось. Клиент аварийно завершал работу независимо от того, использовал ли я потоки или процессы для выполнения запросов. Затем я попытался установить функцию замедления на сервер, и он смог обработать 60 одновременных запросов, но произошел сбой при 70. Похоже, что это может противоречить доказательствам того, что проблема связана с сервером.
РЕДАКТИРОВАТЬ: я пробовал большинство вещей, которые я описал, используя requests
вместо urllib.request
, и столкнулся с аналогичными проблемами.
EDIT: Сейчас я использую OS X 10.11.4 и сталкиваюсь с теми же проблемами.
x = urlopen(whatever)
, а затемx.close()
, и это, похоже, не помогло. - person Elias Zamaria   schedule 18.03.2016ulimit -r $(( 32 * 1024 ))
? и что на выходе изnetstat -anp|grep SERVERPROCESSNAME
? - person Dmitry Rubanovich   schedule 11.04.2016