python http server, несколько одновременных запросов

Я разработал довольно обширный http-сервер, написанный на питоне, использующий торнадо. Не устанавливая ничего особенного, сервер блокирует запросы и может обрабатывать только по одному. Запросы в основном получают доступ к данным (mysql / redis) и распечатывают их в json. Эти запросы могут занимать больше секунды в худшем случае. Проблема заключается в том, что запрос приходит, который занимает много времени (3 с), затем простой запрос приходит сразу после этого займет 5 мс для обработки. Ну, так как этот первый запрос займет 3 секунды, второй не начинается, пока первый не будет выполнен. Таким образом, второй запрос занимает> 3s для обработки.

Как я могу улучшить ситуацию? Мне нужен этот второй простой запрос для начала выполнения независимо от других запросов. Я новичок в python и более опытен с apache / php, где нет понятия двух отдельных запросов, блокирующих друг друга. Я изучил mod_python, чтобы эмулировать пример php, но это также блокирует. Могу ли я изменить свой сервер торнадо, чтобы получить необходимую функциональность? Всюду, где я читаю, говорится, что торнадо отлично справляется с несколькими одновременными запросами.

Вот демо-код, с которым я работаю. У меня есть команда sleep, которую я использую, чтобы проверить, работает ли параллелизм. Является ли сон хорошим способом проверки параллелизма?

import tornado.httpserver import tornado.ioloop import tornado.web import tornado.gen import time class MainHandler(tornado.web.RequestHandler): @tornado.web.asynchronous @tornado.gen.engine def handlePing1(self): time.sleep(4)#simulating an expensive mysql call self.write("response to browser ....") self.finish() def get(self): start = time.time() self.handlePing1() #response = yield gen.Task(handlePing1)#i see tutorials around that suggest using something like this .... print "done with request ...", self.request.path, round((time.time()-start),3) application = tornado.web.Application([ (r"/.*", MainHandler), ]) if __name__ == "__main__": http_server = tornado.httpserver.HTTPServer(application) port=8833; http_server.listen(port) print "listening on "+str(port); tornado.ioloop.IOLoop.instance().start() 

Спасибо за любую помощь!

2 Solutions collect form web for “python http server, несколько одновременных запросов”

Изменить: помните, что Redis также однопоточный, поэтому, даже если у вас есть параллельные запросы, вашим узким местом будет Redis. Вы не сможете обрабатывать больше запросов, потому что Redis не сможет обработать их.

Tornado – это однопоточный сервер на основе событий.

Из документации:

Используя неблокирующий сетевой ввод-вывод, Tornado может масштабироваться до десятков тысяч открытых подключений, что делает его идеальным для длительного опроса, WebSockets и других приложений, для которых требуется долговременное подключение к каждому пользователю.

Параллельность в торнадо достигается за счет асинхронных обратных вызовов. Идея состоит в том, чтобы сделать как можно меньше в цикле основного события (однопоточное), чтобы избежать блокировки и отсрочки операций ввода-вывода через обратные вызовы.

Если использование асинхронных операций для вас не работает (например, без async-драйвера для MySQL или Redis), ваш единственный способ обработки более параллельных запросов – запустить несколько процессов.

Самый простой способ – перенести свои процессы торнадо с помощью обратного прокси-сервера, такого как HAProxy или Nginx. Документ о торнадо рекомендует Nginx: http://www.tornadoweb.org/en/stable/overview.html#running-tornado-in-production

В основном вы запускаете несколько версий вашего приложения на разных портах. Пример:

 python app.py --port=8000 python app.py --port=8001 python app.py --port=8002 python app.py --port=8003 

Хорошим правилом является запуск 1 процесса для каждого ядра на вашем сервере.

Nginx позаботится о балансировании каждого входящего запроса на разные серверы. Поэтому, если один из запросов медленный (~ 3 с), у вас есть n-1 других процессов, которые прослушивают входящие запросы. Возможно и очень вероятно, что все процессы будут заняты обработкой медленного запроса, и в этом случае запросы будут поставлены в очередь и обработаны, когда любой процесс станет бесплатным, например. завершена обработка запроса.

Я настоятельно рекомендую вам начать с Nginx перед тем, как попробовать HAProxy, поскольку последний немного более продвинутый и, следовательно, немного сложнее правильно настроить (много переключателей для настройки).

Надеюсь это поможет. Key take-away: Tornado отлично подходит для асинхронного ввода-вывода, тем более для тяжелых нагрузок процессора.

У меня была такая же проблема, но нет торнадо, нет mysql. У вас есть одно соединение с базой данных, доступное для всего сервера?

Я создал multiprocessing.Pool . Каждый из них имеет собственное соединение db, предоставляемое функцией init . Я переношу медленный код в функцию и map его с пулом. Поэтому у меня нет общих переменных и соединений.

Сон не блокирует другие потоки, но транзакция БД может блокировать потоки.

Вам нужно настроить Pool поверх кода.

 def spawn_pool(fishes=None): global pool from multiprocessing import Pool def init(): from storage import db #private connections db.connect() #connections stored in db-framework and will be global in each process pool = Pool(processes=fishes,initializer=init) if __name__ == "__main__": spawn_pool(8) from storage import db #shared connection for quick-type requests. #code here if __name__ == "__main__": start_server() 

Многие одновременные быстрые запросы могут замедлить один большой запрос, но этот параллелизм будет размещен только на сервере базы данных.

  • Запустить pdb для исключения в ioloop Tornado
  • Настройка переменных окружения superisord
  • Колба и / или Торнадо - обработка трудоемкого вызова на внешний веб-сервис
  • Как я могу периодически выполнять функцию с asyncio?
  • Подключение к Tornado Web Framework Mysql
  • Торнадо не перезагружается в супервизоре
  • Python - лучший язык программирования в мире.