Статьи

Что такое config.threadsafe!

Коннектикут

Мы все любим Rails за его элегантность и огромную поддержку функций, которые он предоставляет для выполнения наших задач. Rails является многофункциональным и действительно позволяет разработчику сосредоточиться на приложении, а не на повторяющихся задачах, таких как запросы к базе данных, управление маршрутизацией, объединение шаблонов и т. Д.

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

Начинающим и промежуточным разработчикам часто сложно понять некоторые важные понятия в Rails, такие как Rack, промежуточное программное обеспечение Rack, ActiveRecord, Asset Pipeline, безопасность потоков и т. Д. В этой статье я остановлюсь на безопасности потоков в Rails.

Давайте сначала поговорим о многопоточности. Многопоточность является очень важной концепцией в области компьютерных наук, и мы не можем недооценивать важность потоков сегодня. Нити повсюду, используются для выполнения очень важной работы. Согласно Википедии :

В информатике поток выполнения — это наименьшая последовательность запрограммированных инструкций, которой может независимо управлять планировщик операционной системы. Нить — это легкий процесс.

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

Условия гонки возникают в программном обеспечении, когда отдельные компьютерные процессы или потоки выполнения зависят от некоторого общего состояния. Операции над общими состояниями являются критическими разделами, которые должны быть взаимоисключающими. Несоблюдение этого правила открывает возможность повреждения общего состояния.

Мы рассмотрели некоторые полезные концепции в области компьютерных наук, теперь давайте представим безопасность потоков в Rails. Потоковая безопасность в Rails не нова, на самом деле она восходит к Rails 2.3. *. Джош Пик проделал невероятную работу, чтобы сделать Rails безопасным. Безопасность нитей в Rails позволяет избежать вышеупомянутых условий гонки.

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

Но Rails не может этого гарантировать, потому что разработчики могут допускать ошибки, которые приводят к созданию не поточно-безопасного кода. Итак, как Rails дает нам гарантию поточно-ориентированного кода?

Rails по умолчанию добавляет промежуточное ПО под названием «Rack :: Lock» в наш стек промежуточного ПО. Это промежуточное ПО является вторым промежуточным ПО в стеке по умолчанию. Чтобы узнать, какое промежуточное ПО включено в приложение Rails, просто введите rake middleware в каталог вашего приложения.

Первое промежуточное программное обеспечение, ActionDispatch::Static , используется для обслуживания статических ресурсов, таких как JavaScript, файлы CSS и изображения.

Rack::Lock гарантирует, что в данный момент будет выполняться только один поток. Если мы удалим это промежуточное ПО, то несколько потоков могут быть выполнены одновременно. MRI Ruby имеет механизм, называемый GIL (глобальная блокировка интерпретатора) или GVL (глобальная блокировка виртуальной машины / гигантская блокировка виртуальной машины) начиная с Ruby 1.9. GIL гарантирует, что только один поток будет выполняться в любой момент времени, и, в случае многопоточности Ruby, он выполняет переключение контекста. Ruby достаточно умен, чтобы начать обработку другого потока, если какой-то исполняющий поток ожидает завершения операции.

Давайте рассмотрим практический пример безопасности потоков в Rails в действии.

Создайте пример приложения Rails.

 rails new test_app 

Теперь cd во вновь созданный проект Rails и установите пакет для установки необходимых гемов.

 bundle install 

После этого создайте контроллер с некоторыми основными действиями.

 rails generate controller thread_safety index simple infinite 

Это создаст контроллер с именем ThreadSafetyController с index , simple и infinite действиями. Откройте app/views/thread_safety/index.html.erb и вставьте следующий код:

 <button id="simple_request">Simple Request</button> &nbsp; <button id="infinite_request">Infinite Request</button> <script type="text/javascript"> $("#simple_request").click(function() { $.get("/users/simple", function(data) { alert(data) }); }); $("#infinite_request").click(function() { $.get("/users/infinite", function(data) { alert(data) }); }); </script> 

Это создаст страницу с двумя кнопками. Назначение двух кнопок — отправлять запросы Ajax на соответствующие действия и отображать возвращенные данные в окне предупреждения.

Давайте добавим некоторый серверный код в app/controllers/thread_safety_controller.rb

 def simple sleep(1) render :text => "Welcome from simple method" end def infinite while true end end 

Приведенный выше код довольно прост. simple спит 1 секунду, а затем выдает простой text/plain ответ клиенту. Хотя infinite метод представляет собой бесконечный цикл и никогда ничего не вернет. Запустите сервер, набрав rails s и перейдите по адресу http: // localhost: 3000 / thread_safety / index

Нажмите кнопку «Простой», и через одну секунду вы получите ответ в окне предупреждения, отправленном с сервера. Теперь нажмите кнопку «Бесконечно» и дождитесь ответа.

infinite метод никогда не вернется из-за преднамеренного бесконечного цикла. Нажмите кнопку «Простой» еще раз. Если вы ожидаете увидеть тот же ответ от сервера, который мы получили ранее, то вы ошибаетесь :-).

Это безопасность потока. Rails гарантирует, что наш код является потокобезопасным, позволяя одновременно выполнять только один поток. Никакой другой поток (-ы) не может выполняться, пока исполняющий поток не завершит свою обработку. Поскольку infinite поток никогда не завершится из-за бесконечного цикла, другие потоки не получат возможность обработать.

Даже если мы запустим наше приложение в производственной среде, мы получим тот же результат. Это здорово, потому что исполняется только один поток, и мы можем гарантировать, что наш код Rails является потокобезопасным. Но существует проблема.

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

Наши ресурсы не столкнутся с этой проблемой, поскольку после отправки ответа клиенту наш сервер может обслуживать статические активы сразу нескольким клиентам. Это связано с тем, что статические ресурсы обрабатываются ActionDispatch::Static который является первым промежуточным программным обеспечением в стеке.

Как мы можем обработать несколько запросов, не блокируя другие? Мы можем просто отключить безопасность потоков, включив config.threadsafe! в файле development.rb или production.rb . Когда мы включаем эту опцию, в нашем приложении происходят значительные изменения.

Теперь вы можете нажать кнопку «Простой» несколько раз после нажатия кнопки «Бесконечно», и она вернет ответ от сервера. Мы включили многопоточность в нашем приложении Rails, но теперь наша задача — написать потокобезопасный код. Для получения более подробной информации о config.threadsafe! пожалуйста, прочитайте эту удивительную статью Аарона Паттерсона.

Мы успешно продемонстрировали концепцию безопасности потоков в Rails. Позвольте мне поделиться с вами хорошими новостями. Вы можете использовать веб-сервер на основе процессов для обработки нескольких запросов, даже с config.threadsafe! опция отключена. Если мы запускаем наше приложение на таких серверах, как Unicorn или Apache Passenger / Nginx Passenger, простые запросы Ajax всегда будут возвращаться успешно, даже если мы запустили бесконечный запрос Ajax.

Это связано с тем, что веб-сервер на основе процессов создает рабочие процессы, и каждый процесс содержит один экземпляр нашего приложения. Apache Passenger создает дочерний процесс для каждого обрабатываемого им запроса. Итак, когда мы выдаем бесконечный Ajax-запрос, один рабочий поток начинает обрабатывать этот запрос, а когда мы создаем простой Ajax-запрос, он обрабатывается вновь порожденным дочерним элементом. Аарон Паттерсон упоминает в своей статье, что config.threadsafe! не влияет на серверы на основе процессов.

Я надеюсь, что вы узнали что-то о Rails и безопасности потоков сегодня. Спасибо за прочтение!