2009-08-14 9 views
5

EDYTOWANIE:Jak sparametryzować moduł gen_server?

Nie zamierzam używać parametrów jako ogólnego celu budowania programów Erlang - wciąż uczę się tradycyjnych zasad projektowania. Nie zamierzam też naśladować OOP. Moim jedynym celem jest uczynienie moich połączeń gen_server spójnymi między instancjami serwera. Wydaje mi się, że przypomina to łamanie abstrakcji. Mogę sobie wyobrazić świat, w którym język lub OTP ułatwiał korzystanie z api dowolnego serwera instancji gen_server i jest to świat, w którym chcę żyć.

Dziękuję Zedowi za wykazanie, że mój główny cel jest możliwy.


Czy każdy może znaleźć sposób na sparametryzowanie modułów na serwerach gen_servers? W poniższym przykładzie załóżmy, że test_child jest serwerem gen_ z jednym parametrem. Gdy próbuję go uruchomić, wszystko pojawia się:

42> {test_child, "hello"}:start_link(). 
** exception exit: undef 
    in function test_child:init/1 
     called as test_child:init([]) 
    in call from gen_server:init_it/6 
    in call from proc_lib:init_p_do_apply/3 

Ostatecznie Próbuję wymyślić sposób korzystania z wielu nazwanych wystąpień gen_server. O ile mogę powiedzieć, jak tylko zaczniesz to robić, nie możesz już używać swojego pięknego interfejsu API i musisz wysyłać wiadomości do swoich instancji za pomocą komendy gen_server: call i gen_server: cast. Gdybym mógł podać instancji ich imiona, ten problem mógłby zostać złagodzony.

+0

Chcesz wielu serwerów nazw używając tego samego modułu wywołania zwrotnego, z których każda ma inną nazwę? Lub wiele serwerów nazw o tej samej nazwie? – Jacob

+0

Wiele serwerów gen_ przy użyciu tego samego modułu oddzwaniania. Możesz przekazać start_link nazwę, aby zarejestrować różne "instancje" za pomocą. Kiedy masz jedną instancję, wydaje się, że typowe jest nadanie jej tej samej nazwy co moduł, a twój publiczny interfejs API działa tak: some_module: some_function(). ... ale to wydaje się być tylko wygodą. Jeśli zarejestrujesz gen_server pod inną nazwą, to już nie działa. Chciałbym skończyć z czymś takim jak: 1> Mod: some_function(). 2> Mod1: some_function(). ... gdzie każda zmienna odnosi się do różnych wystąpień modułu gen_server – mwt

+0

Lub, alternatywnie, chciałbym wiedzieć, dlaczego nie powinienem się tym przejmować. Każde intro gen_server, które widziałem, tworzy interfejs API, a nie tylko używa rzutów/wywołań serwera gen_server.Będąc nowicjuszem w firmie Erlang, spodziewałem się, że będę mógł łatwo klonować wiele procesów i jestem zaskoczony, że funkcja API zrywa się z chwilą zmiany nazwy serwerów gen_servers. – mwt

Odpowiedz

-4
-module(zed, [Name]). 
-behavior(gen_server). 

-export([start_link/0, init/1, handle_cast/2]). 
-export([increment/0]). 

increment() -> 
    gen_server:cast(Name, increment). 

start_link() -> 
    gen_server:start_link({local, Name}, {?MODULE, Name}, [], []). 

init([]) -> 
    {ok, 0}. 

handle_cast(increment, Counter) -> 
    NewCounter = Counter + 1, 
    io:format("~p~n", [NewCounter]), 
    {noreply, NewCounter}. 

Moduł ten działa dobrze dla mnie:

Eshell V5.7.2 (abort with ^G) 
1> S1 = zed:new(s1). 
{zed,s1} 
2> S1:start_link(). 
{ok,<0.36.0>} 
3> S1:increment(). 
1 
ok 
4> S1:increment(). 
2 
ok 
+0

Dzięki. Zamierzam dodać to do mojego projektu i sprawdzić, czy korzyści przewyższają zastrzeżenia zgłoszone w tym wątku. – mwt

+1

Myślę, że negatywne głosy w tej odpowiedzi powinny wyjaśnić wszystko :) – gleber

+0

Sądzę, że już zorientowaliśmy się, że ty i archaelus nie jesteście zwolennikami sparametryzowanych modułów, ale dziękuję za liczenie :) – Zed

3

myślę, że nie należy korzystać z tej funkcji w ten sposób. Wygląda na to, że używasz interfejsu podobnego do OO do swoich serwerów gen_. Używasz w tym celu nazw zarejestrowanych lokalnie - to dodaje do wspólnego programu, czyli The Bad Thing. Tylko kluczowe i centralne serwery powinny być zarejestrowane pod register BIF - niech wszystkie inne będą bezimienne i zarządzane przez jakiegoś menedżera na nich (który prawdopodobnie powinien być zarejestrowany pod pewną nazwą).

+0

Jaka jest różnica między "managerem nad nimi" a wbudowanym rejestrem procesów pod względem funkcjonalności? – Zed

+1

Różnica polega na tym, że menedżer jest wyspecjalizowanym procesem, który wykonuje jedną rzecz na raz. Podczas gdy wbudowany rejestr procesów jest rejestrem ogólnego przeznaczenia dla serwerów centralnych, do którego należy uzyskać dostęp globalnie (na węźle). Szansa na zderzenia nazw jest znacznie większa podczas korzystania z rejestru procesów. Ponadto rejestr procesów AFAIK nie jest przeznaczony do przechowywania dużej liczby pozycji - tzn. Jest przeznaczony dla niewielkiej liczby procesów zarejestrowanych pod numerem – gleber

+0

Zgadzam się z Tobą i zobaczę podobieństwa z ramami zastrzyków zależności w świecie OO. Stosowany jest zastrzyk zależności zamiast globalnych metod statycznych, co zwiększa izolację i modułowość oraz ułatwia testowanie. Nawet krytyczne i centralne serwery nie powinny się odwoływać do nazw silnie zakodowanych atomów, zamiast tego te atomy powinny zostać przekazane do serwera genów jako parametr. Sparametryzowane moduły byłyby kiedyś dostępne, gdyby nie miały problemów. Dlatego zawsze używam zwykłego zwykłego państwa. Twarde nazwy kodowania są zachowaniami nadzorcy. – Christian

10

Istnieją dwie części tej odpowiedzi. Po pierwsze, prawdopodobnie nie chcesz używać sparametryzowanych modułów, dopóki nie będziesz biegły w Erlangu. Wszystko, co ci dają, to inny sposób przekazywania argumentów.

-module(test_module, [Param1]). 

some_method() -> Param1. 

jest równoważna

-module(test_non_paramatized_module). 

some_method(Param1) -> Param1. 

Były nie kupuje Ci dużo w ogóle, a bardzo mało istniejący kod Erlang wykorzystuje ten styl.

Bardziej zwykłe jest przekazywanie argumentu nazwy (zakładając, że tworzysz kilka podobnych gen_servers zarejestrowanych pod różnymi nazwami) do funkcji start_link.

start_link(Name) -> gen_server:start_link({local, Name}, ?MODULE, [Name], []). 

druga część odpowiedzi jest to, że gen_server jest kompatybilny z modułami paramatized:

-module(some_module, [Param1, Param2]). 

start_link() -> 
    PModule = ?MODULE:new(Param1, Param2), 
    gen_server:start_link(PModule, [], []). 

Param1 i Param2 będzie wtedy dostępna we wszystkich funkcjach gen_server zwrotnych.

Jak wspomina Zed, jak start_link należący do modułu paramatized, to musiałby wykonać następujące czynności w celu nazwać:

Instance = some_module:new(Param1, Param2), 
Instance:start_link(). 

Uważam, że jest to szczególnie brzydki styl - kod, który wywołuje some_module:new/n musi znać liczbę i kolejność parametrów modułu. Kod, który wywołuje some_module:new/n, również nie może żyć w samej some_module. To z kolei powoduje, że gorąca aktualizacja jest trudniejsza, jeśli zmieni się liczba lub kolejność parametrów modułu. Będziesz musiał skoordynować ładowanie dwóch modułów zamiast jednego (some_module i jego interfejs/moduł konstruktora), nawet jeśli uda ci się znaleźć sposób na uaktualnienie działającego kodu some_module. Nawiasem mówiąc, ten styl sprawia, że ​​nieco trudniej jest użyć bazy kodu dla zastosowań some_module:start_link.


Zalecany sposób przekazać parametry do gen_servers jest wyraźnie poprzez gen_server:start_link/3,4 argumentów funkcji i przechowywać je w wartości stanu powrocie z ?MODULE:init/1 callack.

-module(good_style). 

-record(state, {param1, param2}). 

start_link(Param1, Param2) -> 
    gen_server:start_link(?MODULE, [Param1, Param2], []). 

init([Param1, Param2]) -> 
    {ok, #state{param1=Param1,param2=Param2}}. 

Korzystanie z tego stylu oznacza, że ​​nie zostanie złapany przez różnych częściach OTP, które nie są jeszcze w pełni obsługują moduły paramatized (nowa i wciąż eksperymentalny funkcję). Ponadto wartość stanu można zmienić podczas działania instancji serwera gen_server, ale parametry modułu nie mogą.

Ten styl obsługuje również aktualizację za pomocą mechanizmu zmiany kodu. Po wywołaniu funkcji code_change/3 można zwrócić nową wartość stanu. Nie istnieje odpowiednia metoda zwracania nowej parametryzowanej instancji modułu do kodu gen_server.

+0

Nie widzę sensu twojej drugiej części. Nie ma żadnych "statycznych" funkcji w parametryzowanych modułach (też za nimi tęsknię). Nie będzie można wywołać metody start_link() bez wcześniejszego wywołania funkcji some_module: new/2. – Zed

+0

Oczywiście możesz mieć oddzielny moduł, np. Some_module_factory, który ma twój start_link() zwracający nowy sparametryzowany some_module, ale wtedy jesteśmy zbyt głęboko w kodowaniu stylu oo ... – Zed

+2

Przypuszczam, że możesz stworzyć moduł interfejsu (to było sparametryzowane), które komunikują się z nie-paramatyzowanym serwerem gen_downości przez serwer_grupy: wywołanie/2. Pozwoli to uniknąć problemu aktualizacji z serwerem gen_server i oznacza, że ​​inaczej przekazujesz odniesienie do serwera (jako parametr paramatyzujący, a nie jako parametr funkcji). Nadal będziesz mieć problem z koordynacją dwóch modułów i nie mogę tego polecić jako dobrego stylu. – archaelus

10

Chcę tylko powiedzieć dwie rzeczy:

  • archaelus wyjaśnia poprawnie. Jak mówi, ostateczny sposób, w jaki pokazuje, jest zalecanym sposobem robienia tego i robi to, czego się spodziewasz.

  • nigdy, NIGDY, NIGDY, NIGDY wykorzystać formularz starali! Pozostały po dawnych czasach, który nigdy nie oznaczał tego, co zamierzałeś, i jest teraz mocno przestarzały.

+0

@mwt, proszę wziąć pod uwagę punkty Roberta. Jest jednym z twórców Erlanga i na pewno powinien go słuchać każdy nowicjusz Erlangu! – gleber

+0

Cóż, dziękuję za poinformowanie mnie. Będę zwracać szczególną uwagę na wszelkie komentarze, które on robi.:) Co do pierwszego punktu, kolejność komentowania może mieć pomieszane rzeczy. Nie mogę powiedzieć, czy on odnosi się do ostatniej lub drugiej do ostatniej edycji archaelusa w tym momencie, ale myślę, że drugi do końca. Jeśli tak, to jest podobne do przykładu zed. Jeśli chodzi o punkt drugi, to i tak nigdy nie używałbym tego stylu. – mwt

+0

Miałem na myśli ostatni sposób, który pokazał, ten, który nie używa sparametryzowanych modułów, moduł good_style. Archaelus komentuje również, że przy użyciu modułów sparametryzowanych działa całkiem dobrze. Cóż, nawet jeśli nigdy nie zamierzałeś użyć stylu w swoim przykładzie, który jest stylem, którego użyłeś, musisz założyć, że jest to zamierzone. – rvirding

Powiązane problemy