Подтвердить что ты не робот

Спектр сервлета 3 и ThreadLocal

Насколько мне известно, в спецификации Servlet 3 реализована функция асинхронной обработки. Между прочим, это будет означать, что один и тот же поток может и будет использоваться повторно для обработки другого, одновременного HTTP-запроса (-ов). Это не революционно, по крайней мере, для людей, которые раньше работали с NIO.

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

Все это мое чистое предположение, основанное на чтении статей, у меня нет времени играть с любыми реализациями Servlet 3 (Tomcat 7, GlassFish 3.0.X и т.д.).

Итак, вопросы:

  • Правильно ли я предполагаю, что ThreadLocal перестанет быть удобным взломом, чтобы сохранить данные запроса?
  • Кто-нибудь играл с любыми реализациями Servlet 3 и пытался использовать ThreadLocal, чтобы доказать это?
  • Помимо хранения данных внутри сеанса HTTP, есть ли другие подобные легкодоступные хаки, которые вы могли бы посоветовать?

EDIT: не поймите меня неправильно. Я полностью понимаю опасности и ThreadLocal, будучи взломом. На самом деле, я всегда советую не использовать его в аналогичном контексте. Однако, верьте или нет, контекст нити использовался гораздо чаще, чем вы, вероятно, себе представляете. Хорошим примером может быть Spring OpenSessionInViewFilter, который, согласно его Javadoc:

Этот фильтр делает сеансы Hibernate доступный через текущий поток, который будет автоопределен менеджеров транзакций.

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

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

4b9b3361

Ответ 1

Асинхронная обработка не должна вас беспокоить, если вы не попросите ее об этом.

Например, запрос не может быть выполнен async, если сервлет или любой из фильтров в цепочке фильтра запроса не отмечен <async-supported>true</async-supported>. Таким образом, вы можете использовать регулярные методы для регулярных запросов.

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

Ответ 2

(Предостережение: я не читал спецификацию Servlet 3 подробно, поэтому я не могу точно сказать, что спецификация говорит, что вы думаете. Я просто предполагаю, что это так...)

Правильно ли я предполагаю, что ThreadLocal перестанет быть удобным взломом, чтобы сохранить данные запроса?

Использование ThreadLocal всегда было плохим подходом, потому что вы всегда подвергались риску, что информация будет протекать, когда рабочий поток завершит один запрос и начнется с другого. Сохранение материала в качестве атрибутов в объекте ServletRequest всегда было лучшей идеей.

Теперь у вас есть еще одна причина сделать это "правильным" способом.

Кто-нибудь играл с любыми реализациями Servlet 3 и пытался использовать ThreadLocals, чтобы доказать это?

Это не правильный подход. Это только говорит о конкретном поведении конкретной реализации в конкретных обстоятельствах вашего теста. Вы не можете обобщать.

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

(Не бойтесь! По-видимому, в этом случае это не происходит по умолчанию. Ваш webapp должен явно активировать функцию обработки асинхронизации. Если ваш код заражен локалями потоков, вам не рекомендуется это делать..)

Помимо хранения данных внутри сеанса HTTP, есть ли другие подобные легкодоступные хаки, которые вы могли бы посоветовать.

Неа. Единственный правильный ответ - хранить данные, специфичные для запроса, в объекте ServletRequest или ServletResponse. Даже сохранение его в сеансе HTTP может быть неправильным, поскольку одновременно может быть несколько запросов одновременно для данного сеанса.

Ответ 3

ПРИМЕЧАНИЕ. Используйте с осторожностью или просто не используйте.

Пока вы продолжаете понимать, в какой поток выполняется ваш код, нет причин, по которым вы не можете безопасно использовать ThreadLocal.

try {
    tl.set(value);
    doStuffUsingThreadLocal();
} finally {
    tl.remove();
}

Это не так, как если бы ваш стек вызовов был беспорядочно отключен. Если есть значения ThreadLocal, которые вы хотите установить в стек вызовов, а затем использовать их дальше, вы также можете взломать это:

public class Nasty {

    static ThreadLocal<Set<ThreadLocal<?>>> cleanMe = 
        new ThreadLocal<Set<ThreadLocal<?>>>() {
            protected Set<ThreadLocal<?>> initialValue() {
                return new HashSet<ThreadLocal<?>>();
            }
        };

    static void register(ThreadLocal<?> toClean) {
       cleanMe.get().add(toClean);
    }

    static void cleanup()  {
        for(ThreadLocal<?> tl : toClean)
            tl.remove();
        toClean.clear();
    }
}

Затем вы регистрируете свои ThreadLocals по мере их установки и очищаете в предложении finally где-то. Это все постыдно, что вы, вероятно, не должны делать. Мне жаль, что я написал это, но слишком поздно:/

Ответ 4

Мне все еще интересно, почему люди используют гнилой API javax.servlet, чтобы фактически реализовать свои сервлеты. Что я делаю:

  • У меня есть базовый класс HttpRequestHandler, который имеет частные поля для запроса, ответа и метода handle(), которые могут вызывать исключение и некоторые полезные методы для получения/установки параметров, атрибутов и т.д. Мне редко нужно больше чем 5-10% от API сервлета, так что это не так много, как кажется.

  • В обработчике сервлета я создаю экземпляр этого класса, а затем забуду о API сервлета.

  • Я могу расширить этот класс обработчика и добавить все поля и данные, которые мне нужны для задания. Нет огромных списков параметров, нет локального взлома потока, не беспокойтесь о concurrency.

  • У меня есть класс утилиты для модульных тестов, который создает HttpRequestHandler с макетными реализациями запроса и ответа. Таким образом, мне не нужна среда сервлета для проверки моего кода.

Это решает все мои проблемы, потому что я могу получить сеанс БД и другие вещи в методе init(), или я могу вставить factory между сервлетом и реальным обработчиком для выполнения более сложных задач.

Ответ 5

Ты психический! (+1 для этого)

Моя цель - получить доказательство, что оно перестало работать в контейнере Servlet 3.0

Здесь является доказательством того, что вы просили.

Кстати, он использует тот же самый фильтр OEMIV, который вы упомянули в своем вопросе, и, угадайте, что он нарушает обработку сервлета Async!

Изменить: Вот еще один доказательство.