Async w Pythonie - wciąż daleko do ideału
Opublikowano: 11.03.2021
Ostatnia modyfikacja: 07.02.2024
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.

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:
- aioredis jest w trakcie totalnej przebudowy interfejsu API tak, by można nią było zastąpić redis-py; nie mnie oceniać czy to dobry czy słaby pomysł, ale tymczasem nie wiadomo czy działa z Redisem 6, nie działa z Pythonem 3.10 i ma problemy z Pythonem 3.9, w dodatku niektórzy z oryginalnych autorów biblioteki już się obrazili za tę zmianę API
- aredis ma dokumentację skopiowaną z redis-py, w szczególności w interesującym mnie zakresie, czyli PubSub, co czyni ją (dokumentację) bezużyteczną
- asyncio-redis co prawda działa ponad wszelką wątpliwość (przekonałem się o tym naocznie), ale dokumentacja jest wyjątkowo skąpa
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?
- Redtrio bez dokumentacji, API z wyglądu nie przypomina niczego co bym znał, ale obsługuje najnowszy protokół Redisa
- trio-redis bez dokumentacji, za to z ostrzeżeniem żeby nie używać bez potrzeby i adnotacją, że jest niekompletna
- RedIO, która też zasadniczo nie ma dokumentacji, chociaż ma wyczerpujące readme - również z ostrzeżeniem, żeby nie używać bez potrzeby
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.