Rails не имеет общего доступа или может отдельным запросам обращаться к одним и тем же переменным времени выполнения? - PullRequest
20 голосов
/ 22 июня 2009

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

А как насчет Ruby on Rails? Я только что прочитал сообщение в блоге о том, что отдельные запросы могут обращаться к одной и той же переменной класса.

Мне пришло в голову, что это, вероятно, зависит от веб-сервера. Часто задаваемые вопросы Mongrel гласит, что Mongrel использует один поток на запрос, что предполагает использование среды без общего доступа. Далее в FAQ говорится, что RoR не является потокобезопасным, что также предполагает, что RoR не будет существовать в общей среде, если в новом запросе не используются объекты в памяти, созданные из предыдущего запроса.

Очевидно, что это имеет огромные последствия для безопасности. Итак, у меня есть два вопроса:

  1. Разделяется ли среда RoR - ничего?
  2. Если RoR запускается (или может работать в некоторых обстоятельствах) в совместно используемой среде, о каких переменных и других хранилищах данных я должен быть параноиком?

Обновление: поясню дальше. В контейнере сервлетов Java вы можете иметь объекты, которые сохраняются в нескольких запросах. Обычно это делается для кэширования данных, к которым могут иметь доступ несколько пользователей, соединений с базой данных и т. Д. В PHP это не может быть сделано на уровне приложений, это должно быть сделано на отдельном уровне персистентности, таком как Memcached. Таким образом, двойной вопрос: какой сценарий похож на RoR (PHP или Java) и, если похож на Java, , какие типы данных сохраняются в нескольких запросах?

Ответы [ 4 ]

31 голосов
/ 23 июня 2009

Короче говоря:

  1. Нет, Rails никогда не работает в среде без общего доступа.
  2. Будьте параноиком по поводу переменных класса и переменных экземпляра класса .

Более длинная версия:

Процессы Rails начинают свой жизненный цикл с загрузки платформы и приложения. Как правило, они будут запускать только один поток, который будет обрабатывать много запросов в течение срока его службы. Поэтому запросы будут отправляться строго последовательно .

Тем не менее, все классы сохраняются при разных запросах. Это означает, что любой объект, на который ссылаются ваши классы и метаклассы (например, переменные класса и переменные экземпляра класса) , будет совместно использоваться запросами. Это может укусить вас , например, если вы попытаетесь запоминать методы (@var ||= expensive_calculation) в ваших class методах, ожидая, что они сохранятся только во время текущего запроса. В действительности, расчет будет выполняться только по первому запросу.

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

Некоторые конфигурации развертывания (в частности, JRuby + Glassfish) на самом деле являются многопоточными. Rails является потокобезопасным, поэтому он может справиться с этим. Но ваше приложение не может быть потокобезопасным. Все экземпляры контроллера выбрасываются после каждого запроса, но, как мы знаем, классы являются общими. Это может укусить вас , если вы передадите информацию в переменные класса или в переменные экземпляра класса. Если вы не используете методы синхронизации должным образом, вы вполне можете оказаться в аду состояния гонки.


В качестве примечания: Rails обычно запускается в однопоточных процессах, потому что реализация потоков в Ruby отстой. К счастью, в Ruby 1.9 дела обстоят немного лучше. И много лучше в JRuby.

Поскольку обе эти реализации Ruby набирают популярность, представляется вероятным, что многопоточные стратегии развертывания Rails также получат популярность и количество. Хорошей идеей будет написать приложение с учетом многопоточной обработки запросов.

5 голосов
/ 08 февраля 2010

Вот сравнительно простой пример, который иллюстрирует, что может произойти, если вы не будете осторожны при изменении общих объектов.

  1. Создать новый проект Rails: rails test

  2. Создайте новый файл lib/misc.rb и вставьте в него следующее:

    class Misc
      @xxx = 'Hello'
      def Misc.contents()
        return @xxx
      end
    end
    
  3. Создать новый контроллер: ruby script/generate controller Posts index
  4. Измените app/views/posts/index.html.erb на этот код:

    <code><%
      require 'misc'; y = Misc.contents() ; y << ' (goodbye) '
    %>
    <pre><%= y %>

    (Здесь мы модифицируем неявно разделяемый объект.)

  5. Добавить RESTful маршруты к config/routes.rb.
  6. Запустите сервер ruby script/server и загрузите страницу /posts несколько раз. Вы увидите, что число ( goodbye) строк увеличивается на одну при каждой последующей перезагрузке страницы.
3 голосов
/ 23 июня 2009

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

Вы можете назвать это кластером отдельных сред с общим состоянием.

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

1 голос
/ 22 июня 2009

Shared-ничто, иногда хорошая идея. Но не тогда, когда вам нужно загружать большую платформу приложений и модель большого домена и большой объем конфигурации при каждом запросе.

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

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

...