Как я могу обслуживать запросы одновременно с Rails 4?

Я пытаюсь одновременно обслуживать несколько запросов в Rails 4, что мне очень легко удалось сделать с config.threadsafe! и Puma в Rails 3.

Скажем, у меня есть этот контроллер

class ConcurrentController < ApplicationController
  def index
    sleep 10000
  end

  def show
  end
end

Раньше я мог просто запускать puma с puma -t 2:16 -p 3000 (минимум 2 потока) и нажимать index, а затем show, и при этом show рендерился правильно.

В Rails 4, если я попытаюсь сделать то же самое, Puma теперь блокирует запрос index, и show никогда не будет отрисован. Когда я нажимаю Ctrl-C для сервера, Puma выдает мне эту ошибку:

Rack app error: #<ThreadError: Attempt to unlock a mutex which is locked by another thread>

Что мне здесь не хватает, чтобы заставить параллелизм работать с Rails 4? config.threadsafe! предполагается, что он не нужен (и не имеет значения, даже если я попытаюсь)


person Fredrik    schedule 06.02.2014    source источник
comment
Вы пробовали запустить это в производственной среде?   -  person Abdo    schedule 07.02.2014
comment
Не должно быть разницы с Rails 4. Действительно, Rails 4 по умолчанию является многопоточным. В вашей среде / конфигурации должно быть что-то еще, что вызывает разницу. Как вы начинаете puma? С помощью этой команды в командной строке? Вы можете поделиться чем-нибудь еще из своего файла development.rb?   -  person John Bachir    schedule 15.03.2014
comment
@fredrik - Пожалуйста, прочтите мой ответ. Я нашел время, чтобы объяснить вариант, который разрешает параллелизм, и дал несколько ссылок. К сожалению, хотя ваш ответ может привести к практическому решению, он не совсем правильный.   -  person Ely    schedule 14.05.2015
comment
Я использую rails 5.0.3 с config.cache_classes = false в config/environments/development.rb по умолчанию, но не сталкиваюсь с этой проблемой.   -  person kbridge4096    schedule 14.05.2017
comment
Попробуйте использовать несколько рабочих процессов вместо нескольких потоков с -w 2. Это приведет к запуску двух экземпляров приложения для обработки запросов.   -  person Joshua Pinter    schedule 20.08.2019


Ответы (3)


Я предлагаю вам прочитать о параметрах конфигурации config.threadsafe! в этой статье Удаление config. threadsafe! Это поможет вам лучше понять возможности config.threadsafe!, в частности, разрешение параллелизма.

В Rails 4 по умолчанию установлено значение config.threadsafe!.


Теперь к ответу

В Rails 4 запросы по умолчанию обертываются вокруг Mutex промежуточным программным обеспечением Rack :: Lock в средах DEV.

Если вы хотите включить параллелизм, вы можете установить config.allow_concurrency=true. Это отключит промежуточное ПО Rack :: Lock. Я бы не стал удалять его, как указано в другом ответе на ваш вопрос; для меня это похоже на взлом.

Примечание. Если у вас config.cache_classes=true, то назначение config.allow_concurrency (Rack :: Lock request mutex) не вступит в силу, параллельные запросы разрешены по умолчанию. Если у вас config.cache_classes=false, вы можете установить config.allow_concurrency на true или false. В среде DEV вы бы хотели, чтобы это было так

config.cache_classes=false
config.allow_concurrency=true

Утверждение: Это означает, что если config.cache_classes = false (что по умолчанию в dev env), у нас не может быть одновременных запросов. неверно.

Приложение

Вы можете обратиться к этому ответу, в котором настраивается эксперимент по тестированию параллелизма с использованием MRI и JRuby. Результаты удивительны. МРТ была быстрее, чем у Дж. Руби.

Эксперимент с параллелизмом MRI находится на GitHub. В эксперименте проверяется только одновременный запрос. В контроллере нет условий гонки. Однако я думаю, что реализовать пример из статьи выше для тестирования условий гонки в контроллере не так уж и сложно.

person Ely    schedule 14.05.2015
comment
Это кажется правильным, и я отмечу его как правильный ответ, хотя я не нашел времени на его проверку. Однако я отмечу, что config.allow_concurrency - это полностью недокументированная функция [1]. Он содержится в документации для Rails 3 [2], но не для Rails 4. - person Fredrik; 18.05.2015
comment
Да это правильно. У нас осталось не так много документации о будущем использовании этой опции конфигурации. Кроме того, я только что вспомнил, подумайте о том, чтобы установить пул соединений вашего соединения с базой данных того же размера, что и максимальное количество потоков. В вашем случае 16. - person Ely; 18.05.2015

Похоже, что по умолчанию в Rails 4 одновременные запросы не включены в среде разработки.

Я нашел эту цитату в документации.

Rack :: Lock заключает приложение в мьютекс, поэтому его может вызывать только один поток за раз. Доступно только тогда, когда config.cache_classes имеет значение false.

Это означает, что если config.cache_classes = false (что по умолчанию в dev env), у нас не может быть одновременных запросов.

Параллельные запросы действительно работают с моим примером в производственной среде.

Одно из решений - установить config.cache_classes = true в среде разработки, но тогда код не перезагружается при изменении, что на самом деле не работает для разработки.

Второй способ взлома - отключить Rack::Lock промежуточное ПО, находящееся в разработке.

Итак, если бы вы добавили development.rb следующую строку:

config.middleware.delete Rack::Lock

у вас должна быть возможность иметь одновременные запросы в среде разработки с отключенными классами кеша.

person Fredrik    schedule 12.05.2014
comment
Где вы используете МРТ Ruby или JRuby? У меня создалось впечатление, что многопоточность не работает в подобном примере с MRI Ruby. - person Arthur Frankel; 30.04.2015
comment
Я вижу, что с Puma есть кластерный режим (для указания рабочих): github.com/puma/ puma # cluster-mode, но я не могу заставить этот пример работать (со сном) - person Arthur Frankel; 30.04.2015
comment
Каковы недостатки удаления этого промежуточного программного обеспечения @Fredrik? - person João Paulo Motta; 02.05.2015
comment
@ JoãoPauloMotta Пожалуйста, посмотрите мой ответ. Я доказал ссылку на сообщение в блоге, в котором обсуждается ваш вопрос. Недостатком является то, что вы должны заботиться об условиях гонки в ваших контроллерах. - person Ely; 14.05.2015
comment
Удалить Rack :: Lock у меня не получилось. Он загружает страницу, но дает мне время ожидания для подключения к базе данных: ActiveRecord :: ConnectionNotEstablished (ActiveRecord :: ConnectionNotEstablished): - person Andre Figueiredo; 13.08.2015
comment
@AndreFigueiredo, вы уверены, что настроили пул соединений с базой данных на столько потоков, сколько вы пытаетесь запустить в своем приложении? - person Fredrik; 13.08.2015
comment
Да, все настроено правильно. Без Delete Rack:Lock работает, с - не работает. Даже статические ресурсы не загружаются. - person Andre Figueiredo; 20.08.2015

вы можете попробовать единорога, это очень просто в режиме разработки:

http://dave.is/unicorn.html

person jbmyid    schedule 09.05.2014