Отправка большого тела разрушает наш стек - но только в производственном режиме? - PullRequest
2 голосов
/ 10 октября 2009

В нашем приложении Rails есть две среды, которые мы развертываем на серверах: промежуточная среда и производственная среда по умолчанию.

Файл staging.rb является копией production.rb из папки config / environment. Разница между этими двумя значениями равна true:

config.whiny_nils = true

Поскольку приложение rails чаще всего используется для его API, мы запустили его на одном из наших внутренних промежуточных серверов, чтобы разработчики могли с ним работать. Это работало без сучка почти 4 месяца. Когда пришло время переносить на наш рабочий сервер, стек начал постоянно падать всякий раз, когда приходили POST или PUT с большим (иногда ОЧЕНЬ ОЧЕНЬ большим) телом. При тестировании между двумя серверами промежуточные серверы прекрасно обрабатывали одни и те же запросы.

Наиболее неприятной частью аварий / зависаний было отсутствие журналов или отслеживание места в стеке (nginx, phusion passenger, ruby ​​1.9 уровень патча 243, рельсы 2.3.4), где происходил сбой. Ничего не появилось в журнале ошибок nginx, журнале rails или в любом другом месте, которое мы могли бы найти. Так как мы работали на производственном сервере с обновленными версиями nginx, passenger и ruby ​​(более высокий уровень исправления, чем промежуточная, но все еще 1.9), мы начали поочередно возвращать каждый компонент, вплоть до передачи всех исполняемых файлов и поддержки файлы (в основном все, что мы установили в / usr / local) на производственную машину безрезультатно. Когда мы собирались стереть машину и попробовать каждый шаг снова, кто-то предложил переключить производственную машину в «промежуточную» среду. , , и как магия, проблема решена!

Желая узнать, что могло вызвать ошибку, мы начали прочесывать ядро ​​rails, наш собственный код и все наши плагины, пытаясь понять, что может вызвать такое массовое зависание / сбой в работе среда, опять же безрезультатно.

Единственной подсказкой, которую я смог найти, было поведение. При тестировании «на» приложении (одна из страниц, на которой фактически работает приложение rails), я бы аварийно завершил работу приложения, отправив запрос, а затем после частых обновлений (обычно 3-4) я мог бы выдать ошибку из Журнал Nginx и в конечном итоге приложение снова начнет обрабатывать запросы. Ошибка выглядит следующим образом:

    Error during failsafe response: incompatible character encodings: UTF-8 and ASCII-8BIT
    2009/10/09 17:52:40 [error] 8691#0: *88 upstream prematurely closed connection while reading response header from upstream, client: *my ip address*, server: myapp.mydomain.com, request: "GET /api/sections/4/edit        HTTP/1.1", upstream: "passenger://unix:/tmp/passenger.8677/master/helper_server.sock:", host: "myapp.mydomain.com"
    *** Exception NoMethodError in application (undefined method `each' for nil:NilClass) (process 8703): from /usr/local/lib/ruby/gems/1.9.1/gems/passenger-2.2.4/lib/phusion_passenger/rack/request_handler.rb:95:in `process_request'
    from /usr/local/lib/ruby/gems/1.9.1/gems/passenger-2.2.4/lib/phusion_passenger/abstract_request_handler.rb:206:in `main_loop'
    from /usr/local/lib/ruby/gems/1.9.1/gems/passenger-2.2.4/lib/phusion_passenger/railz/application_spawner.rb:376:in `start_request_handler'
    from /usr/local/lib/ruby/gems/1.9.1/gems/passenger-2.2.4/lib/phusion_passenger/railz/application_spawner.rb:334:in `block in handle_spawn_application'
    from /usr/local/lib/ruby/gems/1.9.1/gems/passenger-2.2.4/lib/phusion_passenger/utils.rb:182:in `safe_fork'
    from /usr/local/lib/ruby/gems/1.9.1/gems/passenger-2.2.4/lib/phusion_passenger/railz/application_spawner.rb:332:in `handle_spawn_application'
    from /usr/local/lib/ruby/gems/1.9.1/gems/passenger-2.2.4/lib/phusion_passenger/abstract_server.rb:351:in `main_loop'
    from /usr/local/lib/ruby/gems/1.9.1/gems/passenger-2.2.4/lib/phusion_passenger/abstract_server.rb:195:in `start_synchronously'
    from /usr/local/lib/ruby/gems/1.9.1/gems/passenger-2.2.4/lib/phusion_passenger/abstract_server.rb:162:in `start'
    from /usr/local/lib/ruby/gems/1.9.1/gems/passenger-2.2.4/lib/phusion_passenger/railz/application_spawner.rb:213:in `start'
    from /usr/local/lib/ruby/gems/1.9.1/gems/passenger-2.2.4/lib/phusion_passenger/spawn_manager.rb:261:in `block (2 levels) in spawn_rails_application'
    from /usr/local/lib/ruby/gems/1.9.1/gems/passenger-2.2.4/lib/phusion_passenger/abstract_server_collection.rb:126:in `lookup_or_add'
    from /usr/local/lib/ruby/gems/1.9.1/gems/passenger-2.2.4/lib/phusion_passenger/spawn_manager.rb:255:in `block in spawn_rails_application'
    from /usr/local/lib/ruby/gems/1.9.1/gems/passenger-2.2.4/lib/phusion_passenger/abstract_server_collection.rb:80:in `block in synchronize'
    from :8:in `synchronize'
    from /usr/local/lib/ruby/gems/1.9.1/gems/passenger-2.2.4/lib/phusion_passenger/abstract_server_collection.rb:79:in `synchronize'
    from /usr/local/lib/ruby/gems/1.9.1/gems/passenger-2.2.4/lib/phusion_passenger/spawn_manager.rb:254:in `spawn_rails_application'
    from /usr/local/lib/ruby/gems/1.9.1/gems/passenger-2.2.4/lib/phusion_passenger/spawn_manager.rb:153:in `spawn_application'
    from /usr/local/lib/ruby/gems/1.9.1/gems/passenger-2.2.4/lib/phusion_passenger/spawn_manager.rb:286:in `handle_spawn_application'
    from /usr/local/lib/ruby/gems/1.9.1/gems/passenger-2.2.4/lib/phusion_passenger/abstract_server.rb:351:in `main_loop'
    from /usr/local/lib/ruby/gems/1.9.1/gems/passenger-2.2.4/lib/phusion_passenger/abstract_server.rb:195:in `start_synchronously'
    from /usr/local/lib/ruby/gems/1.9.1/gems/passenger-2.2.4/bin/passenger-spawn-server:61:in `'

Обычно, когда возникает ошибка кодировки символов, мой первый ход - ruby ​​1.9. , , однако, как вы можете заметить из моего тестирования, это была одна и та же версия на обеих машинах!

После всего этого, я думаю, мне интересно. , , Кто-нибудь есть какие-либо идеи, что происходит? Очевидно, что мы можем пока запустить наше приложение в стадии подготовки, но я волнуюсь, что, возможно, я нашел что-то более глубокое, что необходимо решить. Есть ли какие-нибудь идеи относительно следующего места, где я должен искать, где это происходит?

Наша установка: Mac OS X Server: 10.6.1,
Рельсы 2.3.4,
Ruby 1.9p243,
Nginx 0.8.17,
Пассажир 2.2.5

Наши необходимые драгоценные камни:
environment.rb
Демоны
RMagick
test.rb
RSpec
RSpec рельсы
завод-девочка
рейк-тест

Наши установленные плагины:
actions-as-dag (плагин активной записи для создания ориентированных ациклических графов)
daemon_generator
globalize2
без подглядывания (для тестирования)
думающий сфинкс


ОБНОВЛЕНИЕ (в ответ на хеллл):

Я попытался добавить config.whiny_nils = true в производственной среде, однако сбой все еще происходит.

Кроме того, я вернулся на наш промежуточный сервер и установил для среды «Производство». , .Такой сбой!

Некоторые пояснения к тому, что я имел в виду под "большим" телом запроса. Один из методов POST / PUT, который постоянно приводил к аварийному завершению работы приложения, составлял приблизительно 20 000 символов (json). Поскольку API постоянно использовался в течение дня с небольшими PUTS / POSTS и оставался включенным, но падал / зависал только при выполнении этих более крупных запросов, я предположил, что они были подключены.

Что касается Rack / Ruby 1.9. Из-за большого количества информации о Rack и 1.9 я обновил наш Rack gem до последней версии в git-репозитории (которая предположительно исправила некоторые из проблем 1.9). Я читал о значительном количестве трудностей, связанных с rewindable_input, ruby ​​1.9 и более… однако, так как я не получал ошибку rewindable_input, с которой я столкнулся в моем другом приложении 1.9, я предположил, что это была другая проблема. Кроме того, я исключил Rack, когда изменение среды rails решило проблему (когда я искал в исходном коде Rack, казалось, что не было никаких специфических для среды методов, которые могли бы вызвать ошибку).

Надеюсь, это поможет!


ОБНОВЛЕНИЕ в ответ на pauliephonic

Нет сообщений, попадающих на логи рельсов вообще (что фактически подтолкнуло меня к поиску проблемы в нашем веб-стеке некоторое время). Моя подсказка о том, что происходит сбой / зависание, заключается в том, что после выполнения большого запроса приложение возвращает только 500 ошибок на каждый запрос, однако эти 500 ошибок не отображаются в журналах Rails.

Конфигурация нашей базы данных идентична (мы использовали кластер mysql, поэтому он был буквально идентичен, сейчас использует локальную базу данных mysql, но подтвердил, что ошибка завершается независимо от используемой базы данных)

Что касается нескольких байтов / юникод. , , мы работаем в интернационализированном приложении. , , однако я не думаю, что способ, которым рельсы обрабатывают изменения Unicode между производством и другими, не так ли? Как я уже говорил выше, это произошло POST или PUT. Во время отладки я проверял, как перейти к тем же страницам редактирования одной из моих больших, сильно вложенных моделей и просто попытаться "сохранить" ее. Это может привести к сбою приложения в рабочей среде, но не приведет к сбою приложения в процессе подготовки. Каждый раз, когда я тестировал одни и те же символы, один и тот же контент, одну и ту же кнопку, одинаковое поведение. , , другой ответ, основанный на окружающей среде. Я даже не мог перенаправить операторы puts повсюду в моем коде, потому что (казалось, что) запросы не поступали в приложение rails. Я не получал сообщений об ошибках в моих журналах Rails или журналах ошибок Nginx (сохраните сообщение, которое я опубликовал при нескольких обновлениях).

Ответы [ 4 ]

0 голосов
/ 06 ноября 2009

У меня была похожая проблема в последние несколько месяцев - не достаточно часто, чтобы действительно отладить ее до недавнего времени. В моем случае, сказать Пассажиру, куда поместить файлы временного буфера, удалось. Трудно было найти не только отсутствие сообщений об ошибках в файлах журналов, но и тот факт, что этот буфер используется не только для составных сообщений, но и для любого большого тела сообщения.

PassengerUploadBufferDir /tmp
0 голосов
/ 20 октября 2009

Судя по всему, этой ошибкой является то, что Ruby привередлив в кодировках. У вас есть строка, которую он считает UTF-8, и вы рассматриваете ее как необработанные байты, возможно, правильно. Вам нужно определить проблемную строку и вызвать buggy_string.force_encoding(Encoding::ASCII_8BIT) на ней. Но, черт возьми, если я знаю, какой фрагмент кода работает с этой строкой или почему это происходит только в производстве. Я не удивлюсь, обнаружив, что проблема на самом деле глубоко в недрах Пассажира.

Что касается различия между подготовкой и производством, то это почти наверняка проблема с чем-то, что должно быть перемещено как строка байтов, и это рассматривается как строка символов. Существует масса кода, который происходит только в производственной среде (например, кеширование), и если любой из этого кода делает это, это ваша проблема.

whiny_nils вещь, вероятно, не имеет значения.

0 голосов
/ 20 октября 2009

Я бы попробовал запустить приложение в работе на apache / passenger, посмотреть, является ли проблема специфичной для nginx / passenger

0 голосов
/ 10 октября 2009

Что я могу понять, так это то, что config.whiny_nils имеет все значение. Если бы вы были, я бы посмотрел файл activesupport/lib/active_support/whiny_nils.rb (который выглядит так просто) и попытался отыграть его, чтобы понять, в чем разница. Я полагаю, что это связано с типом исключения, которое вы получаете в производственной среде, которое может не вызываться при использовании whiny_nils.

Я полагаю, что вам нужно предоставить более подробную информацию, также касающуюся части "Публикация большого тела приводит к сбою в нашем стеке", поскольку это может быть проблемой с Rack и Ruby 1.9

...