Globalny kontekst żądania - anty-wzór?

12

Rozmawiałem dzisiaj z moim kolegą na temat frameworków internetowych Pythona i naszych wrażeń na ich temat. Powiedziałem mu, że myślę, że Flask, który ma globalne życzenie, źle pachnie i jest anty-wzorem.

W docs powiedzieć o kontekście żądanie:

Natomiast podczas obsługi żądań istnieje kilka innych reguł:

  • gdy żądanie jest aktywne, kontekstowe obiekty lokalne (flask.request i inne) wskazują na bieżące żądanie.
  • dowolny kod może uzyskać te obiekty w dowolnym momencie.

Myślę, że rozumiem ideę tej decyzji projektowej - uproszczenie aplikacji. To tylko kompromis, jak w przypadku Lokalnych wątków :

Tak, zwykle nie jest tak dobrym pomysłem stosowanie miejscowych wątków. Powodują problemy dla serwerów, które nie są oparte na koncepcji wątków i utrudniają utrzymanie dużych aplikacji. Jednak Flask po prostu nie jest przeznaczony do dużych aplikacji lub serwerów asynchronicznych. Flask chce, aby pisanie tradycyjnej aplikacji internetowej było szybkie i łatwe.

Czy łatanie obiektu globalnego informacjami o bieżącym żądaniu jest anty-wzorcem?

Wierzę, że tak jest, ponieważ zdaniem analizatora kodów statycznych jest to stan globalny, chociaż tak nie jest. A ja jako programista nie zrozumiem, jak to działa bez uważnego przeczytania dokumentacji . Ma to konsekwencje dla testów .

Czy nie jest dobrą praktyką przekazywanie żądania jako argumentu do widoków? Myślę, że jest bardziej czytelny, wyraźny i łatwiejszy do debugowania. I unika stanu globalnego.

warvariuc
źródło
2
Tak naprawdę nie powiedziałeś, jakie mogą być konkretne negatywne skutki takiego antypatternu. Nie ufam zamiatającym ogólnikom, które nie mają podstaw faktycznych.
Robert Harvey
2
Dobre pytanie, ale niestety niewiele
dobrych

Odpowiedzi:

4

Wiele frameworków ma tę samą strukturę: żądanie globalne. W pewnym sensie jest to właściwe, ponieważ hej, tak naprawdę jest tylko jedna prośba na raz.

Czy jest więc sens przekazywanie żądania jako parametru? Nie. Żądanie jest żądaniem, a parametry dotyczą przekazywania różnych rzeczy w różnych momentach.

Prawdziwym problemem jest jak zaczniesz rozważać niższe poziomy większego zastosowania. W przypadku globalnego żądania istnieje pokusa, aby pisać kod w całym miejscu, w którym globalnie uzyskuje się dostęp do żądania. To bardzo zła rzecz . Powoduje sprzężenie między różnymi częściami kodu, utrudnia zmianę rzeczy i utrudnia testowanie rzeczy.

Więc moja odpowiedź brzmi: zachowaj globalną prośbę i żyj z nią. Jednak wszędzie tam, gdzie pojedynczy moduł lub funkcja nie potrzebuje całego żądania, jako parametr przekaż tylko potrzebne dane. Przekaż tylko polecenia, adres URL lub ogon polecenia i dowolne potrzebne bity do swoich funkcji. Pomoże to utrzymać modułowość kodu, zmniejszy sprzężenie i poprawi testowalność.

W przypadku małych programów nie ma to większego znaczenia, ale w przypadku większych może to być prawdziwy ratownik.

david.pfx
źródło
3

(Zamierzam odważnie i odpowiem na to pytanie, chociaż mogę dostać trochę głosów negatywnych).

Kolba jest mikrocząsteczką; korzystasz z prostoty, rezygnując z dodatków. Chociaż jestem na poziomie jelit, zgadzam się z tobą, wiem, że użyłem flask + gunicorn w jednym sklepie, aby dać mi wielowątkowość, której potrzebowałem. Działa naprawdę dobrze. Każde wystąpienie skryptu po prostu obsłużyło jedno żądanie (tj. Jeden wątek), a gunicorn obsłużył „fan out” wśród wielu wątków. Było w tym świetnie.

Więc odczuwany minus, który odczuwasz - że wiele wątków może walczyć o stan globalny - po prostu nie stanowi problemu, ponieważ jest to jeden skrypt na wątek.

(Tutaj mogę wpaść w kłopoty) Wątki i współbieżność są po prostu inne w świecie Pythona, a jeśli przyjdziesz do tego z umysłem Java, trudno to wcisnąć. Moje doświadczenie było takie, że problemy z współbieżnością, które wziąłem za przyznane w Javie lub obsługiwane przezroczyście przez kontener aplikacji, są znacznie bliżej powierzchni w Pythonie.

Dziwne mi było, że jeden wątek poradziłby sobie z jednym wywołaniem mojego skryptu, ale po tym, jak kilkadziesiąt uruchomiłem jednocześnie na pudełku, poczułem się lepiej.

Obrabować
źródło
4
Nie martwię się o bezpieczeństwo wątków i tym podobne. Wierzę, że Flask działa dobrze w takich przypadkach. Moje pytanie dotyczy projektowania i architektury aplikacji. Czy nie jest dobrą praktyką przekazywanie żądania jako argumentu do widoków? Myślę, że jest bardziej czytelny, wyraźny i łatwiejszy do debugowania.
warvariuc
2

W Pythonie masz printpolecenie (funkcja od wersji 3), które wypisuje na standardowe wyjście. Nie określasz jawnie, że chcesz drukować do STDOUT - jest to zrobione dla ciebie niejawnie za kulisami.

Niejawnie. W Pythonie. I nikt nie ma z tym problemu. Czemu?

printjest częścią języka Python, a jednym z wymogów programowania w języku Python jest ... no cóż ... znajomość języka Python. A jeśli znasz Python, wiesz, że jest on printukierunkowany na STDOUT. Żadnych niespodzianek.

Python - jako język - może zdefiniować własną konwencję i założyć, że programiści są tego świadomi.

Frameworki również korzystają z tego przywileju - to jedna z kluczowych różnic między frameworkiem a biblioteką. Nie musisz uczyć się biblioteki, aby z niej korzystać - wystarczy znaleźć potrzebną część interfejsu API i założyć, że jest on zgodny z konwencjami języka (lub frameworka). Dlatego nie widzisz rekruterów szukających ludzi z wiedzą w GSON lub Apache Commons. Ale widzisz rekruterów szukających ludzi z doświadczeniem w JQuery lub Ruby on Rails lub ASP.NET MVC - ponieważ są to ramy, które definiują własne konwencje, których musisz się nauczyć i być świadomym.

Flask, jako struktura, może definiować konwencję przechowywania kontekstu w globalnym wątku lokalnym - i nie powinien nikogo dziwić, więc nie jest to anty-wzorzec.

Idan Arye
źródło
2
Zauważ, że „stdout” oznacza dowolny deskryptor pliku, na który wskazuje sys.stdout. Jeśli to zmienisz, druk trafi gdzie indziej.
Phoshi,
1
Możesz również przesłonić strumień wyjściowy, używając >>operatora lub przekazując fileargument do printdziałania w Python3. Tak, sys.stdoutto tylko wartość domyślną, która może być zmieniona.
warvariuc