Podczas 21 odcinka stworzyć kilka instancji PingPong.Server
w elixir. Wtedy też wykorzystaliśmy jeden sposób, którego nie opisałem i obiecałem, że kiedyś opiszę. A więc to kiedyś właśnie nastąpiło :) Dla niewtajemniczonych w jednej z aplikacji chcieliśmy stworzyć dwa razy ten sam GenServer
, przy czym wciąż chcieliśmy się do niego odwoływać po nazwie zamiast posiadać gdzieś zapisany PID
. Czyli by za pomocą jak najprostszego kodu móc wywołać ping i uzyskać pong.
Nasza domyślna implementacja PingPong.Server
na start_link/0
wykonywała kod:
GenServer.start_link(__MODULE__, 0, name: :pingpong)
Który bindował GenServer
do aktualnego modułu (__MODULE__
), ustawiał stan początkowy na 0
oraz nadawał nazwę naszego serwera jako :pingpong
. To ostatnie pozwalało nam na wykonywanie kodu:
GenServer.call(:pingpong, {:ping, "ping"})
Który wysyłał wiadomość do serwera :pingpong
o treści ping. Gdybyśmy nie nadali mu nazwy :pingpong
, musielibyśmy przechować wynik naszego start_link/0
, lub pozbyć się abstrakcji PingPong.start_link/3
:
pid = GenServer.start_link(__MODULE__, 0)
Dzięki temu pid'owi
jesteśmy wstanie wywołać nasz serwer:
GenServer.call(pid, {:ping, "ping"})
Oczywiście, mogliśmy to zrobić wcześniej posiadając nazwany serwer, jednak, mielibyśmy kłopoty, gdybyśmy chcieli takich serwerów stworzyć dwa lub nawet trzy. A to wszystko przez to, że nazwa został już zarejestrowana/wykorzystywana. Opcja z PID
pozwala nam mieć wiele serwerów PingPong.Server
, jednak utrudnia ona nam ich zarządzanie.
Dla przykładu, jak mieliśmy nadzorcę, to nasz PingPong.Server
był tworzony z poziomu inicjalizacji nadzorcy. Co powoduje, że wiedza o PID
po prostu zniknie. Metoda ping w PingPong.Server
zaś przestanie mieć sens, bo nie jesteśmy wstanie tego wysłać – nie znamy ani pidu ani nie mamy nazwy.
Właśnie dla takich sytuacji dostępne są trzy rozwiązania z którch możemy skorzystać. Dwa rozwiązania możemy wykorzystać zarówno w iex
jak i w mix
, zaś trzecie i ostatnie jedynie w mix
. Jedną opcję już znamy i jest to rejestrowanie serwera po atom’ie. Czyli dosłownie tak jak to zrobiliśmy z :pingpong
.
Dziś zajmiemy się drugą opcją – modułem :global
. Wiedza ta przyda się bardzo przy tworzeniu Supervisors
i GenServers
w BitTorrent gdzie dosłownie o taki sam problem zahaczymy. A za tydzień sięgniemy do opcji trzeciej i ostatniej.
:global
O :global
jest cicho – albo ja nie potrafiłem szukać – w postach o elixir, jako, że jest to moduł typowo erlangowy. A dokładniej jest to globalny rejestr nazw. Serwer ten jest startowany od razu z nodem i umożliwia on komunikacje cross nodową – rozproszoną. Czyli pozwala nam na znalezienie serwera który może być na zupełnie innym komputerze lub w innym procesie terminala.
To jak on działa i co on daje, pozostawiam waszej dociekliwości :) dla nas jedyne ważne jest to w tym przypadku, że on istnieje i implementuje on metody które GenServer
potrzebuje by móc rejestrować nazwy bardziej globalnie niż nasz PingPong.Server
. Tracimy w tym momencie metodę ping/0
w takiej postaci jak aktualnie ją mamy, ale też możemy ją ciutkę przerobić – zaraz pokaże jak.
Ogólnie, rozchodzi się wszystko o to, że przy start_link/3
ostatni parametr wygląda następująco:
{:global, name}
To spowoduje, że nasz serwer zostanie zarejestrowany w globalnym module i będzie miał nazwę name
. Teraz przy słaniu wiadomości wystarczy podać:
GenServer.call({:global, name}, {:ping, "ping"})
A więc nasz ping trzeba zamienić na:
def ping(name) do GenServer.call({:global, name}, {:ping, "ping"}) end
CIEKAWOSTKA: jak nadawaliśmy swoją nazwę za pomocą atomu, to elixir zamieniał to na {:local, name}
, a więc prawie na to samo co :global
, tylko, ze lokalnie (na danym komputerze, na danym node).
Podsumowanie
W ogóle pominąłem kwestię co to jest node i jak on wygląda i jakie ma właściwości. Wrócę do tego. Ale chciałem tylko pokazać, że mamy kilka sposobów rejestrowania GenServer w aplikacji. Każdy ma swoje plusy i minusy każdy też ma innych zasięg.
Dziś była opcja z :global
rejestracją nazw tak by każdy w sieci mógł z niej skorzystać. Co prawda to nie rozwiązuje problemu z :pingpong a wręcz przeciwnie powoduje, że nie będziemy mogli użyć :pingpong nigdzie w sieci, jednak jest to też krok do tego by opisać ostatnie rozwiązanie które ten problem już rozwiązuje.
Do przeczytania jak zwykle jutro, a o elixirze za tydzień! :)
[…] Elixir #36 – GenServer i rejestr instancji (część 1) […]
Comments are closed.