Загрузка большого количества файлов в мое веб-приложение приводит к взаимоблокировке в Rack Unicorn

#ruby-on-rails #ruby #rack #unicorn

#ruby-on-rails #ruby #rack #unicorn

Вопрос:

У меня есть приложение Rails 3.1rc1, которое позволяет загружать несколько файлов одновременно с помощью плагина jQuery, называемого File Upload:http://aquantum-demo.appspot.com/file-upload Это позволяет пользователю загружать кучу изображений, размер которых я затем изменяю с помощью convert команды ImageMagick и выгружаю на S3. Мой сайт обслуживается двумя работниками Unicorn.

Прошлой ночью я загружал пакет из примерно 30 изображений одновременно. Похоже, что Chrome будет выполнять 6 операций одновременно, и он обработал примерно половину пакета, прежде чем это начало отображаться в моем unicorn.stderr.log :

 E, [2011-06-01T07:01:39.806164 #21751] ERROR -- : Read error: #<ThreadError: deadlock; recursive locking>
E, [2011-06-01T07:01:39.806316 #21751] ERROR -- : /var/www/shared/bundle/ruby/1.9.1/gems/rack-1.3.0/lib/rack/lock.rb:33:in `lock'
/var/www/shared/bundle/ruby/1.9.1/gems/rack-1.3.0/lib/rack/lock.rb:33:in `call'
/var/www/shared/bundle/ruby/1.9.1/gems/rack-cache-1.0.2/lib/rack/cache/context.rb:132:in `forward'
/var/www/shared/bundle/ruby/1.9.1/gems/rack-cache-1.0.2/lib/rack/cache/context.rb:243:in `fetch'
/var/www/shared/bundle/ruby/1.9.1/gems/rack-cache-1.0.2/lib/rack/cache/context.rb:181:in `lookup'
/var/www/shared/bundle/ruby/1.9.1/gems/rack-cache-1.0.2/lib/rack/cache/context.rb:65:in `call!'
/var/www/shared/bundle/ruby/1.9.1/gems/rack-cache-1.0.2/lib/rack/cache/context.rb:50:in `call'
/var/www/shared/bundle/ruby/1.9.1/gems/railties-3.1.0.rc1/lib/rails/rack/content_length.rb:16:in `call'
/var/www/shared/bundle/ruby/1.9.1/gems/hoptoad_notifier-2.4.10/lib/hoptoad_notifier/user_informer.rb:12:in `call'
/var/www/shared/bundle/ruby/1.9.1/gems/railties-3.1.0.rc1/lib/rails/engine.rb:438:in `call'
/var/www/shared/bundle/ruby/1.9.1/gems/railties-3.1.0.rc1/lib/rails/railtie/configurable.rb:28:in `method_missing'
/var/www/shared/bundle/ruby/1.9.1/gems/unicorn-3.6.2/lib/unicorn/http_server.rb:545:in `process_client'
/var/www/shared/bundle/ruby/1.9.1/gems/unicorn-3.6.2/lib/unicorn/http_server.rb:623:in `block in worker_loop'
/var/www/shared/bundle/ruby/1.9.1/gems/unicorn-3.6.2/lib/unicorn/http_server.rb:621:in `each'
/var/www/shared/bundle/ruby/1.9.1/gems/unicorn-3.6.2/lib/unicorn/http_server.rb:621:in `worker_loop'
/var/www/shared/bundle/ruby/1.9.1/gems/unicorn-3.6.2/lib/unicorn/http_server.rb:509:in `block (2 levels) in spawn_missing_workers'
/var/www/shared/bundle/ruby/1.9.1/gems/unicorn-3.6.2/lib/unicorn/http_server.rb:507:in `fork'
/var/www/shared/bundle/ruby/1.9.1/gems/unicorn-3.6.2/lib/unicorn/http_server.rb:507:in `block in spawn_missing_workers'
/var/www/shared/bundle/ruby/1.9.1/gems/unicorn-3.6.2/lib/unicorn/http_server.rb:503:in `each'
/var/www/shared/bundle/ruby/1.9.1/gems/unicorn-3.6.2/lib/unicorn/http_server.rb:503:in `spawn_missing_workers'
/var/www/shared/bundle/ruby/1.9.1/gems/unicorn-3.6.2/lib/unicorn/http_server.rb:516:in `maintain_worker_count'
/var/www/shared/bundle/ruby/1.9.1/gems/unicorn-3.6.2/lib/unicorn/http_server.rb:166:in `start'
/var/www/shared/bundle/ruby/1.9.1/gems/unicorn-3.6.2/lib/unicorn.rb:30:in `run'
/var/www/shared/bundle/ruby/1.9.1/gems/unicorn-3.6.2/bin/unicorn:120:in `<top (required)>'
/var/www/shared/bundle/ruby/1.9.1/bin/unicorn:19:in `load'
/var/www/shared/bundle/ruby/1.9.1/bin/unicorn:19:in `<main>'
  

Время ожидания остальных моих загрузок изображений истекло, и Unicorn перезапустил себя. Я мог бы загрузить снова, но одновременное выполнение нескольких файлов продолжало вызывать эту ошибку (и гораздо чаще после того, как это произошло в первый раз — иногда я мог привести к взаимоблокировке при загрузке двух файлов одновременно).

Я надеюсь на некоторые идеи относительно того, с чего начать устранение неполадок. Какая часть запроса заблокирована? Ни один из моих Rails-кодов нигде не отображается в этом стеке, поэтому я предполагаю, что это происходит в жесткой логике запроса, что заставляет меня думать, что это будет нелегко исправить. Должен ли я вернуться к чему-то менее управляемому событиями, такому как Mongrel, для загрузки моих файлов? Я предполагаю, что это означает, что вы сможете загружать одновременно столько файлов, сколько запущено экземпляров mongrel, но, по крайней мере, весь сайт не отключится, если кто-то загрузит огромную партию файлов (что, я надеюсь, будет обычным явлением).

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

Обновление Я вижу, что команда ImageMagick convert немного блокируется … Я не уверен, как работает внутренняя часть Rack, когда у вас есть запрос, который «застрял» на сервере. Я собираюсь переключиться на freeimage и посмотреть, что произойдет. Я также собираюсь взглянуть на Rainbows! в качестве альтернативы Unicorn на странице загрузки, поскольку это длительные запросы, в которых Unicorn признает, что он не очень хорош.

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

1. Похоже, основная проблема заключается в том, что ImageMagick не очень хорош в одновременном запуске нескольких копий самого себя. Я переключился на freeimage, и все мои проблемы исчезли. Я все еще нахожу странным, что Rack выходит из себя… Я бы подумал, что при запросе будет встроенный тайм-аут, если серверная часть не отвечает своевременно, но, по-видимому, нет?

Ответ №1:

Вам необходимо настроить плагин загрузки jQuery для загрузки по одному файлу за раз (насколько я помню, это возможно с помощью этого плагина). Это не проблема Google Chrome.

Также используйте какое-либо фоновое задание для обработки изображений (например: delayed_job или resque)

В заключение, если вы планируете, что ваш сайт будет получать тонны загрузок, возможно, вам следует переключиться на какой-либо внешний сервис для обработки мультимедиа (например, мы используем transloadit.com для проекта, над которым я работаю).

ОБНОВЛЕНИЕ: Я обнаружил эту ошибку в rack:https://github.com/rack/rack/issues/87

также нашел ваш ответ на github 😉 https://github.com/rack/rack/issues/183

вы решили это?

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

1. Спасибо! На сервере возникает базовое состояние race, независимо от того, как клиент выбирает загрузку файлов… это то, что я действительно хотел бы видеть исправленным. Я согласен, что простая «однопоточность» загрузки помогла бы, когда одновременно загружает только один человек, но представьте, что все файлы загружают дюжина человек… это должно вызвать ту же проблему на сервере.

2. Я согласен. вот почему я предложил вам использовать какой-нибудь внешний сервис для выполнения загрузок. кстати, прочитайте мой отредактированный ответ.