Глобальный контекст запроса - анти-паттерн?

12

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

В документах говорят о контексте запроса:

Напротив, во время обработки запроса существует пара других правил:

  • пока запрос активен, локальные объекты контекста (flask.request и другие) указывают на текущий запрос.
  • любой код может получить доступ к этим объектам в любое время.

Я думаю, что понимаю идею этого дизайнерского решения - сделать приложение проще. Это просто компромисс, как в случае с Thread Locals :

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

Является ли исправление глобального объекта с информацией о текущем запросе антипаттерном?

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

Разве не рекомендуется передавать запрос в качестве аргумента представлениям? Я думаю, что это более читабельно, явно и легче для отладки. И избегает глобального состояния.

warvariuc
источник
2
Вы на самом деле не указали, какими могут быть специфические негативные последствия такого антипаттерна. Я не доверяю широким общностям, которые не имеют фактической основы.
Роберт Харви
2
Хороший вопрос, но, к сожалению, не так много качественных ответов
сонный 18.01.16

Ответы:

4

Многие веб-фреймворки имеют такую ​​же структуру: глобальный запрос. В некотором смысле, это правильно, потому что на самом деле существует только один запрос за раз.

Так есть ли смысл передавать запрос как параметр? Нет. Запрос - это запрос, а параметры - для передачи в разные вещи в разное время.

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

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

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

david.pfx
источник
3

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

Колба представляет собой микрорамку; Вы выигрываете от простоты, отказываясь от излишеств. Хотя я согласен с вами, но знаю, что в одном магазине я использовал колбу + gunicorn, чтобы дать мне многопоточность, в которой я нуждался. Это сработало очень хорошо. Каждый экземпляр скрипта просто передавал один запрос (т. Е. Один поток), а gunicorn обрабатывал «веер» среди нескольких потоков. Это было здорово.

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

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

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

обкрадывать
источник
4
Я не беспокоюсь о безопасности потоков и тому подобное. Я считаю, что Flask хорошо работает в этих случаях. Мой вопрос касается дизайна приложений и архитектуры. Разве не рекомендуется передавать запрос в качестве аргумента представлениям? Я думаю, что это более читабельно, явно и легче для отладки.
warvariuc
2

В Python у вас есть printкоманда (функция начиная с v3), которая печатает на стандартный вывод. Вы не указываете явно, что хотите печатать в STDOUT - это сделано для вас негласно за кулисами.

Косвенно. В питоне. И ни у кого нет проблем с этим. Зачем?

printявляется частью языка Python, и одним из требований программирования на Python является ... хорошо ... знание Python. И если вы знаете Python, вы знаете, что он printнацелен на STDOUT. Там нет сюрпризов.

Python - как язык - может определить свое собственное соглашение и предположить, что программисты знают о них.

Фреймворки также пользуются этой привилегией - это одно из ключевых отличий между фреймворком и библиотекой. Вам не нужно изучать библиотеку, чтобы использовать ее - вам просто нужно найти ту часть API, которая вам нужна, и предположить, что она соответствует соглашениям языка (или фреймворка). Вот почему вы не видите рекрутеров, ищущих людей со знаниями в GSON или Apache Commons. Но вы видите, что рекрутеры ищут людей с опытом работы с JQuery или Ruby on Rails или ASP.NET MVC - потому что это фреймворки, которые определяют свои собственные соглашения, которые вы должны изучить и знать.

Flask, как фреймворк, может определять соглашение для хранения контекста в глобальном локальном потоке - и это никого не должно удивлять, так что это не анти-паттерн.

Идан Арье
источник
2
Обратите внимание, что «stdout» означает любой дескриптор файла, на который указывает sys.stdout. Если вы измените это, печать отправится в другое место.
Фоши
1
Кроме того, вы можете переопределить поток вывода, используя >>оператор или передавая fileаргумент printфункции в Python3. Таким образом, sys.stdoutэто просто значение по умолчанию, которое может быть переопределено.
warvariuc