Запросы обратной связи Ruby on Rails вызывают взаимоблокировку?

Решение

Я переключаюсь на единорога в режиме разработки. Для предотвращения тупиковой ситуации необходим один рабочий процесс для каждого уровня рекурсии, поэтому я использую 2 рабочих процесса.

Проблема

Я работаю над сервером Thin в своей среде разработки. Я использую порт 3000 (по умолчанию в среде разработки). Моя проблема заключается в том, чтобы заставить сервер делать запросы самому себе.

Допустим, у меня есть следующий контроллер:

# app/controllers/recursions_controller.rb
class RecursionsController < ApplicationController

    # /recursions
    def index

        # synchronously call recursions#show
        RestClient.get("http://localhost:3000/recursions/1") 

        # finish!
        render :text => 'index'

    end

    # /recursions/:id
    def show

        # finish immediately
        render :text => 'show'

    end

end

Вот соответствующий маршрут:

# config/routes.rb
resources :recursions

Вот вывод из журнала запросов, когда я первоначально запрашиваю recursions#index:

[INFO] 2013-01-15 12:09:05 -0800 Started GET "/recursions" for 127.0.0.1 at 2013-01-15 12:09:05 -0800
Processing by RecursionsController#index as HTML
Completed 500 Internal Server Error in 60049ms

[FATAL] 2013-01-15 12:10:05 -0800 RestClient::RequestTimeout (Request Timeout):
  app/controllers/recursions_controller.rb:8:in `index'
  Rendered /usr/local/lib/ruby/gems/1.9.1/gems/actionpack-3.2.11/lib/action_dispatch/middleware/templates/rescues/_trace.erb (0.8ms)
  Rendered /usr/local/lib/ruby/gems/1.9.1/gems/actionpack-3.2.11/lib/action_dispatch/middleware/templates/rescues/_request_and_response.erb (0.7ms)
  Rendered /usr/local/lib/ruby/gems/1.9.1/gems/actionpack-3.2.11/lib/action_dispatch/middleware/templates/rescues/diagnostics.erb within rescues/layout (7.4ms)

[INFO] 2013-01-15 12:10:05 -0800 

[INFO] 2013-01-15 12:10:05 -0800 

[INFO] 2013-01-15 12:10:05 -0800 Started GET "/recursions/1" for 127.0.0.1 at 2013-01-15 12:10:05 -0800
Processing by RecursionsController#show as XML
  Parameters: {"id"=>"1"}
  Rendered text template (0.0ms)
Completed 200 OK in 7ms (Views: 5.5ms)

Я подозреваю, что здесь происходит какая-то тупиковая ситуация. Запрос A не может вернуться, пока не вернется запрос B (рекурсивный порядок, ничего не поделаешь), но запрос B не может быть обработан до тех пор, пока не вернется запрос A (очевидное ограничение, встроенное в мой веб-сервер?). Тупик разрешается, когда истекает время ожидания RestClient, вызывая исключение и завершая запрос A с кодом 500. Только тогда обрабатывается запрос B, хотя на данный момент это спорный вопрос.

Мне кажется, что мой веб-сервер не может обрабатывать одновременные запросы. Тем не менее, вот мои вопросы:

  1. Есть ли веб-сервер, на который я могу переключиться в своей среде разработки, который не ограничен таким образом? Мы используем Unicorn в производстве, который может создавать несколько рабочих процессов и, следовательно, обрабатывать параллельные запросы, но Unicorn кажется слишком тяжелым для среды разработки. То же самое, что делает Unicorn решением моей проблемы, может затруднить чтение вывода журнала. Это мое последнее средство.

  2. Есть ли сложный способ сделать запрос к платформам Rails/Rack, чтобы обойти очевидное ограничение параллельных запросов?

  3. Может ли кто-нибудь предоставить мне документы, в которых явно указывается это ограничение? Я не знаю, является ли это ограничением, присущим всем однопроцессным веб-серверам Ruby on Rails или только Thin.

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


person Sebastian Goodman    schedule 15.01.2013    source источник
comment
Решение имеет на самом деле сложную отладку... Вывод рабочих процессов чередуется в логе.   -  person Sebastian Goodman    schedule 16.01.2013


Ответы (2)


Вы правы, что по умолчанию ваше приложение будет обрабатывать только один запрос за раз. Это ограничение реализовано промежуточным программным обеспечением Rack::Lock — оно будет там независимо от того, какой веб-сервер вы используете.

Вы можете изменить это, вызвав config.thread_safe! в соответствующем файле environment.rb. Однако перезагрузка кода в режиме разработки rails не является потокобезопасной и отключена этим параметром, что делает ее непригодной для использования в разработке. Именно что config.thread_safe! описан в руководстве по настройке rails.

пассажир, пау, единорог позволяют легко запускать несколько экземпляров.

person Frederick Cheung    schedule 15.01.2013

Я столкнулся с этой точной проблемой с одним из моих клиентов. Это именно то, что вы описываете - поскольку вы используете однопоточный веб-сервер только с одним процессом, вы ограничены одним клиентом за раз. Если вы делаете второй запрос или, так сказать, циклический запрос, ваш второй запрос ставится в очередь ОС до тех пор, пока не истечет время ожидания первого.

Переключение на unicorn по сути не решает проблему, хотя в нашем случае мы использовали unicorn и просто увеличили количество рабочих до «2». Я недостаточно знаком с тонкими, чтобы рекомендовать, как увеличить количество рабочих, но, без сомнения, способ есть. В противном случае используйте unicorn — вы также получите лучшее соотношение dev/prod, если сделаете переход, так что это может быть целесообразно.

person Dave S.    schedule 15.01.2013
comment
Рад, что я не единственный с этой проблемой. Были ли проблемы с запуском Unicorn в dev? Проблемы с производительностью или, может быть, трудности с отладкой с несколькими работниками? - person Sebastian Goodman; 16.01.2013
comment
Отладка нескольких рабочих процессов, безусловно, является сложной задачей. С тех пор мы отказались от HTTP-вызовов самим себе, главным образом по этой причине. Его сложнее отлаживать, и в нашем случае в этом не было особой необходимости. Производительность unicorn неплохая — это не наш ограничивающий фактор. - person Dave S.; 16.01.2013