Async w Pythonie - wciąż daleko do ideału

Ten post został napisany ponad 2 lata temu, do wszystkich porad technologicznych w nim zawartych lepiej będzie podejść z dużą rezerwą, bo bardzo możliwe że tego rodzaju informacje są już nieaktualne.

Opublikowano: 11.03.2021

Ostatnia modyfikacja: 07.02.2024

async

programowanie

python

Co jakiś czas lubię sobie dłubnąć coś nowego, a to jakaś aplikacyjka na telefon, a to coś w Javascripcie, a to jakieś nowe narzędzie przetestować, wyciskając z niego ile się da (a czasem zwyczajnie nadużywając). Po ostatnich przygodach z Preactem i Flutterem zapragnąłem wrócić do Pythona, przy okazji sprawdzając jak się w początku 2021 roku pisze aplikacje asynchroniczne.

Greens

Asynchroniczne programowanie współbieżne w Pythonie. Tyle lat się już tym jaramy, a do czego doszliśmy? W ramach testu postanowiłem napisać mały serwerek czatu oparty na WebSocketach i Redisie, a do obskoczenia wybrałem bibliotekę Trio.

Wybory miss

Jakie mamy alternatywy? Można tłuc w żywym asyncio jak zwierzęta, ale można spróbować z wyższego poziomu. Curio niestety dostarcza tylko podstawowych funkcji obsługi współbieżności, AIOHTTP (wbrew nazwie sugerującej specjalizację) już ma wszystko co trzeba by być ramówką aplikacyjną, podobnie jak Trio. Oczywiście, jest jeszcze Twisted, ale Twisted znam już na tyle dobrze, że chciałem spróbować czegoś nowego, czyli jedziemy z async/await. W tej sytuacji pozostają do wyboru AIOHTTP i Trio, więc zacząłem dobierać pozostałe elementy.

Z WebSocketami nie było żadnych problemów, AIOHTTP ma to wprost z pudełka, a do Trio jest dobrze działająca biblioteka. To teraz Redis no i tutaj zaczęły się schody.

Do AIOHTTP są 3 biblioteki: omc-oficjalna aioredis, aredis która kusi zgodnością interfejsu z najpopularniejszą biblioteką do Redisa redis-py oraz asyncio-redis o której tyle wiadomo, że działa. Kolorowy zawrót głowy, ale jak się bliżej przyjrzeć, to:

Z wielkiej trójki, została jedna, ledwo co udokumentowana i może jedna, bo w sumie nie wiadomo jak jej używać. Tragedią bym tego nie nazwał, ale do stanu może być to jednak trochę brakuje. Nota bene, benchmark jest trochę zniechęcający, nie umiem powiedzieć na ile jest wiarygodny.

Co zabawne, do Trio są aż cztery, choć tak naprawdę to też tylko trzy, bo ta najstarsza została zarchiwizowana (nie obsługiwała z resztą PubSub). Co zostało?

Ludzie, co jest z wami nie tak? No ale coś trzeba było wybrać, więc zdecydowałem się na kombo Trio + trio-websocket + RedIO, to w końcu projekt edukacyjny. Gównie dlatego, że AIOHTTP (i aioredis) wyglądają, jakby mało kto się nimi zajmował.

Implementacja

Implementacja poszła stosunkowo łatwo. Nie obyło się bez paru przeszkód natury mentalnej, ale po przestawieniu się na myślenie w async poszło już gładko. Miałem jedną zagwozdkę związaną z RedIO i teraz zastanawiam się, czy czasem to nie jest błąd w bibliotece, ale jeszcze nie jestem pewien co tam się dzieje. Otóż pobieranie wiadomości z topicu PubSub zdaje się blokować pętlę aplikacji dopóki wiadomość się nie pojawi, ale obszedłem to dodając await trio.sleep(0) w pętli zadania odczytującego z PubSub i na razie zdaje się działać. Do zaimplementowania zostało jeszcze kilka innych funkcji, a potem będę mógł się zająć pozostałymi dwiema częściami układanki.

O Trio mogę powiedzieć tyle, że dokumentację ma dobrą, przejrzystą i raczej kompletną (nie aż tak jak Falcon, ale blisko). Pisze się w tym całkiem przyjemnie, a dzięki wyczerpującemu wprowadzeniu przestawienie się na myślenie w async zajęło raptem jeden wieczór. W trackerze Trio dzieje się bardzo dużo, ale odbieram to głównie jako szum, bo samo tempo rozwoju nie jest zawrotne choć w miarę stałe. Kiedyś denerwowało mnie w Trio to, że statyczne analizatory kodu (np dostarczające usługi IntelliSense w edytorach) głupiały całkowicie, ale ta bolączka została już usunięta. Skończę ten projekt i zapewne kiedyś jeszcze do Trio wrócę, bo warto ten projekt śledzić, może się z niego urodzić coś równie dobrego, co Twisted.