В Rails нет общего доступа или отдельные запросы могут обращаться к одним и тем же переменным времени выполнения?
-
06-07-2019 - |
Вопрос
PHP работает в среде без общего доступа, что в данном контексте означает, что каждый веб-запрос выполняется в чистой среде.Вы не можете получить доступ к данным другого запроса, кроме как через отдельный уровень персистентности (файловая система, база данных и т. д.).
А как насчет Ruby on Rails?Я просто читал сообщение в блоге заявляя, что отдельные запросы могут обращаться к одной и той же переменной класса.
Мне пришло в голову, что это, вероятно, зависит от веб-сервера. Часто задаваемые вопросы о дворняге утверждает, что Mongrel использует один поток для каждого запроса, что предполагает среду без общего доступа.Далее в FAQ говорится, что RoR не является потокобезопасным, что также предполагает, что RoR не будет существовать в общей среде, если новый запрос не будет повторно использовать объекты в памяти, созданные из предыдущего запроса.
Очевидно, это имеет огромные последствия для безопасности.Итак, у меня есть два вопроса:
- Является ли среда RoR ничем не разделенной?
- Если RoR запускается (или мощь запускаться в некоторых обстоятельствах) общая среда, в отношении каких переменных и других хранилищ данных мне следует опасаться?
Обновлять:Я уточню дальше. В контейнере сервлетов Java вы можете иметь объекты, которые сохраняются при нескольких запросах.Обычно это делается для кэширования данных, к которым имеют доступ несколько пользователей, подключений к базе данных и т. д.В PHP это невозможно сделать на уровне приложения, это необходимо сделать на отдельном уровне персистентности, таком как Memcached.Итак, двойной вопрос:какой сценарий похож на RoR (PHP или Java), а если похож на Java, который типы данных сохраняются в нескольких запросах?
Решение
Короче:
<Ол>Более длинная версия:
Процессы Rails начинают свой жизненный цикл с загрузки платформы и приложения. Как правило, они будут запускать только один поток, который будет обрабатывать много запросов в течение срока его службы. Поэтому запросы будут отправляться строго последовательно .
Тем не менее, все классы сохраняются в разных запросах. Это означает, что любой объект, на который ссылаются ваши классы и метаклассы (например, переменные класса и переменные экземпляра класса), будет использоваться совместно для всех запросов. Это может вас укусить , например, если вы попытаетесь запоминать методы ( @var || = дорогая_калькуляция
) в своих методах class , ожидая этого будет сохраняться только во время текущего запроса. В действительности, расчет будет выполняться только по первому запросу.
На первый взгляд может показаться целесообразным реализовать кэширование или другое поведение, которое зависит от постоянства запросов. Как правило, это не так. Это связано с тем, что в большинстве стратегий развертывания используется несколько процессов Rails для противодействия их собственной однопоточной природе. Просто не круто блокировать все запросы во время ожидания медленного запроса к базе данных, поэтому самый простой выход - порождать больше процессов. Естественно, эти процессы не разделяют ничего (кроме некоторой памяти, которую вы не заметите). Это может вас укусить , если вы сохраняете вещи в своих переменных класса или переменных экземпляра класса во время запросов . Затем, так или иначе, иногда материал кажется присутствующим, а иногда, кажется, ушел. (На самом деле, конечно, данные могут присутствовать или не присутствовать в одном процессе , а в других - отсутствовать).
Некоторые конфигурации развертывания (особенно JRuby + Glassfish) являются фактически многопоточными. Rails является потокобезопасным, поэтому он может справиться с этим. Но ваше приложение не может быть потокобезопасным. Все экземпляры контроллера выбрасываются после каждого запроса, но, как мы знаем, классы являются общими. Это может вас укусить , если вы передадите информацию в переменные класса или в переменные экземпляра класса. Если вы не используете методы синхронизации должным образом, вы вполне можете оказаться в аду состояния гонки.
<Ч>В качестве примечания: Rails обычно запускается в однопоточных процессах, потому что реализация потоков в Ruby отстой. К счастью, в Ruby 1.9 дела обстоят немного лучше. И намного лучше в JRuby.
Поскольку обе эти реализации Ruby набирают популярность, представляется вероятным, что многопоточные стратегии развертывания Rails также получат популярность и количество. Хорошая идея - написать приложение с учетом многопоточной диспетчеризации запросов.
Другие советы
Вот относительно простой пример, иллюстрирующий, что может произойти, если вы не будете осторожны с изменением общих объектов.
Создайте новый проект Rails:
rails test
Создать новый файл
lib/misc.rb
и вставьте туда это:class Misc @xxx = 'Hello' def Misc.contents() return @xxx end end
- Создайте новый контроллер:
ruby script/generate controller Posts index
Изменять
app/views/posts/index.html.erb
содержать этот код:<% require 'misc'; y = Misc.contents() ; y << ' (goodbye) ' %> <pre><%= y %></pre>
(Здесь мы модифицируем неявно общий объект.)
- Добавьте маршруты RESTful в
config/routes.rb
. - Запустить сервер
ruby script/server
и загрузите страницу/posts
несколько раз.Вы увидите количество( goodbye)
строки увеличиваются на единицу при каждой последующей перезагрузке страницы.
При обычном развертывании с использованием Passenger у вас, вероятно, есть несколько процессов приложения, которые не разделяют между ними ничего, кроме классов внутри каждого процесса, которые поддерживают свое (статическое) состояние от запроса к запросу. Каждый запрос, тем не менее, создает новый экземпляр ваших контроллеров.
Вы можете назвать это кластером отдельных сред общего состояния.
Чтобы использовать аналогию с Java, вы можете выполнить кэширование и заставить его работать от запроса к запросу, вы просто не можете предположить, что оно будет доступно при каждом запросе.
Разделили - ничего, иногда хорошая идея. Но не тогда, когда вам нужно загружать большую прикладную инфраструктуру и модель большого домена и большое количество конфигурации при каждом запросе.
Для эффективности Rails хранит некоторые данные, доступные в памяти, для совместного использования всеми запросами в течение всего срока службы приложения. Большая часть этих данных доступна только для чтения, поэтому не стоит беспокоиться.
Когда вы пишете свое приложение, держитесь подальше от записи в общие объекты (например, за исключением базы данных, которая поставляется из коробки с хорошим контролем параллелизма), и у вас все будет в порядке.