Cześć.
Za nami ogrom wiedzy podzielonej na osiem wpisów. Dzisiejszy, dziewiąty, dotyczyć będzie pewnej bardzo przydatnej funkcjonalności Redisa. Pokaże jak skonfigurować i używać notyfikacja. Standardowo, jak w każdym poprzednim artykule, tak i w tym będę opierał się na wiedzy która już została przeze opublikowana. Zachęcam więc do zapoznania się z poprzednimi wpisami:
- https://mmazurek.dev/redis-i-python-dobrze-dobrana-para-8/
- https://mmazurek.dev/redis-i-python-dobrze-dobrana-para-7/
- https://mmazurek.dev/redis-i-python-dobrze-dobrana-para-6/
- https://mmazurek.dev/redis-i-python-dobrze-dobrana-para-5/
- https://mmazurek.dev/redis-i-python-dobrze-dobrana-para-4/
- https://mmazurek.dev/redis-i-python-dobrze-dobrana-para-3/
- https://mmazurek.dev/redis-i-python-dobrze-dobrana-para-2/
- https://mmazurek.dev/redis-i-python-dobrze-dobrana-para-1/
Czy są notyfikacje redisowe?
Notyfikacje to powiadomienia które wysyła Redis (via publish na konkretnym kanale) generowane w momencie pracy na innych kluczach. Brzmi zawile, więc może przykład: możemy tak skonfigurować Redisa by ten robił publish na pewny kanał z informacją o np. usunięciu jakiegoś klucza. Albo z dodaniem elementu do listy. Albo z dodaniem elementu do strumienia. Możliwości jest dużo, zbyt dużo by pokazać każdą z nich w tym wpisie, więc jest on bardziej przysłowiową wędką niż rybą.
Kanały
Subskrybować możemy się na kanały których nazwy wyglądają tak:
1 2 | __keyspace@0__:mykey __keyevent@0__:del |
pierwszy, zwany „key-space notification” po subskrypcji pozwoli na odbieranie wiadomości na temat konkretnego klucza (w przykładzie mykey). Drugi, zwany „key-event notification” będzie dostarczał informacje dla podanego typu operacji (w przykładzie dla del). Nie trudno się domyśleć że jeśli usuniemy klucz o nazwie „mykey” to na pierwszym kanale dostaniemy wiadomość o treści „del” a na drugim „mykey”.
Konfiguracja
Notyfikacje można włączyć na dwa sposoby. Jeden z nich jest trwały, tj ustawienie jest respektowane nawet po restarcie Redisa a drugi pozwala na konfigurację ulotną. Oczywiście tę drugą można zapisać i tym samym sprawić, że zostanie zapamiętana. Skonfigurowanie powiadomień sprowadza się do wyboru konkretnych literek z tych które odpowiadają za efekt jaki chcemy uzyskać:
1 2 3 4 5 6 7 8 9 10 11 12 13 | K Keyspace events, published with __keyspace@<db>__ prefix. E Keyevent events, published with __keyevent@<db>__ prefix. g Generic commands (non-type specific) like DEL, EXPIRE, RENAME, ... $ String commands l List commands s Set commands h Hash commands z Sorted set commands t Stream commands x Expired events (events generated every time a key expires) e Evicted events (events generated when a key is evicted for maxmemory) m Key miss events (events generated when a key that doesn't exist is accessed) A Alias for "g$lshztxe", so that the "AKE" string means all the events except "m". |
a wiec dla przykładu, jeśli chcemy aby Redis publikował na kanałach typu key-space informacje o komendach dotyczących list to konfiguracją będzie „Kl”. Jeśli chcemy by nie tylko o listach informował a o stringach też, to konfiguracją będzie „Kl$”. Przyznać muszę, że jest to słabo czytelne, ale idzie się przyzwyczaić.
Włączyć notyfikację można poprzez:
1 | redis-cli config set notify-keyspace-events K$ |
co sprawi, że Redis zacznie wysyłać notyfikację zgodnie ze wskazanym ustawieniem. Utrwalenie można osiągnąć wykonując polecenie:
1 | redis-cli config rewrite |
które po prostu zapisuje aktualną konfigurację do pliku redis.conf w którym również manualnie można zmienić ilość pożądanych notyfikacji.
Czekaj, stop!
Podoba Ci się to co tworzę? Jeśli tak to zapraszam Cię do zapisania się na newsletter:Jeśli to Cię interesuje to zapraszam również na swoje social media.
Jak i do ewentualnego postawienia mi kawy :)
Przykład
Stwórzmy program który będzie nasłuchiwał na każdą zmianę klucza wynikającą z komend dedykowanych stringowi:
1 2 3 4 5 6 7 8 9 10 11 | from redis import Redis redis_connection = Redis(decode_responses=True, db=0) redis_connection.config_set('notify-keyspace-events', 'K$') pub_sub = redis_connection.pubsub() pub_sub.subscribe('__keyspace@0__:test_key') for message in pub_sub.listen(): print(message) |
uruchom program i wykonaj z poziomu redis-cli komendę:
1 2 | 127.0.0.1:6379> set test_key 15 OK |
która momentalnie sprawi, że program wypisze:
1 | {'type': 'message', 'pattern': None, 'channel': '__keyspace@0__:test_key', 'data': 'set'} |
wykonaj teraz w redis-cli:
1 2 | 127.0.0.1:6379> append test_key 11 (integer) 4 |
a program zareaaguje nowym powiadomieniem:
1 | {'type': 'message', 'pattern': None, 'channel': '__keyspace@0__:test_key', 'data': 'append'} |
teraz usuń klucz i wykonaj:
1 2 | 127.0.0.1:6379> LPUSH test_key 1 (integer) 1 |
program na to nie zareagował. Wynika to wprost z konfiguracji, która pozwala na publikowanie tylko zdarzeń wygenerowanych w wyniku użycia komend dedykowanych stringom.
Jeśli uruchomisz taki program:
1 2 3 4 5 6 7 8 9 10 11 | from redis import Redis redis_connection = Redis(decode_responses=True, db=0) redis_connection.config_set('notify-keyspace-events', 'E$') pub_sub = redis_connection.pubsub() pub_sub.subscribe('__keyevent@0__:set') for message in pub_sub.listen(): print(message) |
to dostaniesz powiadomienia o każdym użyciu komendy SET a jako wiadomość przyjdzie nazwa klucza, na której komenda się wykonała, np.:
1 | {'type': 'message', 'pattern': None, 'channel': '__keyevent@0__:set', 'data': 'test_key'} |
Kończąc
To o czym należy pamiętać, to o tym że Redis pcha powiadomienia przez mechanizm kanałów pub-sub, co ma swoje zalety i wady. Ale nie ma sensu by je powtarzać – jeden z poprzednich artykułów jest poświęcony właśnie temu mechanizmowi. Ciekawostką jest, że te powiadomienia kiedyś bardzo pomogły mi, w takim trybie „turbo-hot-fix”, uszczelnić pewien proces. Ustawienie klucza jednoznacznie określało że powinna się po nim zadziać pewna akcja. Akcja z wyniku błędu nie zawsze się wykonywała a notyfikacje pomogły mi ekspresowo złapać ten moment i obsłużyć niefortunne przypadki. Tak czy siak, warto wiedzieć że takie sztuczki istnieją.
Mateusz Mazurek