Сервер Python tornado теряет соединения

#python #http #tornado

#python #http #tornado

Вопрос:

У меня есть http-сервер python с фреймворком tornado. После нескольких запросов он становится недоступен. Страница в браузере тоже недоступна. Примерно через 20 секунд бездействия он снова начинает работать.

100 000 запросов содержат около 10 исключений. При такой загрузке серверный процесс потребляет около 30% ЦП.

Почему сервер становится недоступным?

Сервер:

 start_port = 4400
workers = 1

class MainHandler(tornado.web.RequestHandler):
    def get(self):
        data = ''.join(random.choice(string.ascii_uppercase   string.digits) for _ in range(1000))
        self.write(data)


def server_process(port):
    application = tornado.web.Application([
        (r"/", MainHandler),
    ])

    http_server = tornado.httpserver.HTTPServer(application)

    http_server.listen(port)
    tornado.ioloop.IOLoop.instance().start()


if __name__ == "__main__":
    for i in xrange(workers):
        port = start_port   i
        print 'process started on %d port' % port
        p = Process(target=server_process, args=(port,))
        p.start()
  

Клиент:

 def f():
    for i in xrange(500000):
        try:
            r = requests.get('http://127.0.0.1:4400')
            if i % 100 == 0:
                print i, str(r.text)
        except:
            print traceback.format_exc()
            time.sleep(5)


if __name__ == '__main__':
    for j in xrange(1):
        p = Process(target=f)
        p.start()
  

Обратная трассировка:

 Traceback (most recent call last):
  File "/home/me/PycharmProjects/test/client.py", line 16, in f
    if i % 100 == 0:
  File "/usr/local/lib/python2.7/dist-packages/requests/api.py", line 55, in get
    return request('get', url, **kwargs)
  File "/usr/local/lib/python2.7/dist-packages/requests/api.py", line 44, in request
    return session.request(method=method, url=url, **kwargs)
  File "/usr/local/lib/python2.7/dist-packages/requests/sessions.py", line 383, in request
    resp = self.send(prep, **send_kwargs)
  File "/usr/local/lib/python2.7/dist-packages/requests/sessions.py", line 486, in send
    r = adapter.send(request, **kwargs)
  File "/usr/local/lib/python2.7/dist-packages/requests/adapters.py", line 378, in send
    raise ConnectionError(e)
ConnectionError: HTTPConnectionPool(host='127.0.0.1', port=4400): Max retries exceeded with url: / (Caused by <class 'socket.error'>: [Errno 99] Cannot assign requested address)
  

Обновлено:

Экспериментально выбрано значение 300 запросов в секунду аппаратного сервера (не процесса). Это значение не зависит от количества процессов, запущенных tornado. Добавление nginx в качестве прокси-сервера не помогло.

Сервер работал на ubuntu server 12.04 и linux mint 16. Похоже, это ограничение зависит от операционной системы debian.

Комментарии:

1. tornado является однопоточным, возможно, резервное копирование запросов выполняется, поскольку сервер не может обработать их все: papercruncher.com/2013/01/15/truly-async-with-tornado

2. Ваш клиент отправляет запросы только одному процессу, который выполняется на порту 4400. Возможно, именно по этой причине вы не видите разницы при изменении количества серверных процессов.

3. Из любопытства: если вы будете возвращать только один символ вместо 1000 для каждого запроса, увеличится ли количество запросов в секунду до нескольких десятков тысяч?

4. Причина, по которой вы видите 300 запросов в секунду на процесс, заключается в том, что tornado способен обрабатывать только один запрос за раз. Для построения возвращаемой вами случайной строки на моем КОМПЬЮТЕРЕ требуется около 0,003 секунды. 1/0.003 ~ 333 запроса в секунду.

5. В этом примере порт жестко запрограммирован. Другой тест требует большего количества рабочих в серверном скрипте и случайного порта в клиентском скрипте. Я получил 400req / sec с данными в один символ, но примерно после 36000 запросов сервер снова стал недоступен. Проблема заключается в недоступности сервера, а не в частоте запросов в секунду.

Ответ №1:

Проблема решена. Многопроцессорность.Process — плохая идея для нескольких процессов tornado. Вместо этого я использую tornado.process.