Obecnie pracuję nad serwerem mediów na żywo, który pozwoli zwykłym konsumentom przesłać nam wideo na żywo. W naszym obecnym środowisku widzieliśmy transmisje wysyłane do nas w ciągu kilku dni, dlatego pomysł, aby naprawić błąd (lub dodać funkcję) bez odłączania użytkowników, jest niezwykle ważny.Jak działa wymienianie poprawek Erlanga w trakcie działania?
Jednak podczas pisania kodu zdałam sobie sprawę, że wymienianie się gorącym kodem nie ma sensu, chyba że piszę każdy proces tak, że cały stan jest zawsze wykonywany wewnątrz serwera gen_, a wszystkie zewnętrzne moduły, które wywołania gen_server muszą być tak proste jak możliwy.
Weźmy następujący przykład:
-module(server_template).
-behaviour(gen_server).
-export([start/1, stop/0]).
-export([init/1, handle_call/3, handle_cast/2, handle_info/2, terminate/2, code_change/3]).
start() -> gen_server:start_link({local, ?MODULE}, ?MODULE, [], []).
init([]) -> {ok, {module1:new(), module2:new()}}.
handle_call(Message, From, State) -> {reply, ok, State}.
handle_cast(any_message, {state1, state2}) ->
new_state1 = module1:do_something(state1),
new_state2 = module2:do_something(state2),
{noreply, {new_state1, new_state2}}.
handle_info(_Message, _Server) -> {noreply, _Server}.
terminate(_Reason, _Server) -> ok.
code_change(_OldVersion, {state1, state2}, _Extra) ->
new_state1 = module1:code_change(state1),
new_state2 = module2:code_change(state2)
{ok, {new_state1, new_state2}}
Według tego, co udało mi się znaleźć, gdy nowa wersja kodu jest ładowany do aktualnie działającej starcie bez użycia systemu OTP, można uaktualnić do bieżącego kodu wersji poprzez wywołanie twojego modułu jako zewnętrznego wywołania funkcji, czyli my_module:loop(state)
.
To co widzę, to to, że gdy wykonywana jest funkcja hot swap, funkcja code_change/3
jest wywoływana i aktualizuje stan, więc mogę go użyć, aby upewnić się, że każdy z moich modułów zależnych migruje ostatni stan, w którym dostał mnie do stanu aktualna wersja kodu. Robi to, ponieważ nadzorca wie o uruchomionym procesie, który pozwala na zawieszenie procesu, aby mógł wywołać funkcję zmiany kodu. Wszystko dobrze.
Jednakże, jeśli wywołanie modułu zewnętrznego zawsze wywołuje bieżącą wersję tego modułu, może się to wydawać zerwane, jeśli w połowie funkcji wykonano hot swap. Na przykład mój serwer gen_ jest obecnie w trakcie obsługi obsady any_message
, na przykład pomiędzy uruchomieniem module1:do_something()
i module2:do_something()
.
Jeśli jestem zrozumienia rzeczy poprawnie, module2:do_something()
będzie teraz nazwać nowo aktualną wersję funkcji do_something
, co może potencjalnie oznaczać Jestem przekazując dane nie migrujące ostrożnie do nowej wersji module2:do_something()
. Mogłoby to łatwo spowodować problemy, jeśli jest to rekord, który się zmienił, tablica z nieoczekiwaną liczbą elementów lub nawet jeśli na mapie brakuje wartości oczekiwanej przez kod.
Czy nie rozumiem, jak działa ta sytuacja? Jeśli tak jest, wydaje się to wskazywać, że muszę śledzić pewne szczegóły wersji dla dowolnej struktury danych, która może przesuwać granice modułów, a każda funkcja publiczna musi sprawdzić numer wersji i przeprowadzić migrację na żądanie, jeśli to konieczne.
Wydaje się to być bardzo wysokim zleceniem, które wydaje się być podatne na błędy, więc zastanawiam się, czy czegoś brakuje.
Masz rację. OTP sprawia, że aktualizacja kodu jest bardziej kontrolowana. Zawiesza wykonanie kodu zgodnego z OTP, ładuje nową wersję, wywołuje 'code_change/3', a następnie kontynuuje pracę. Jest to o wiele bardziej kontrolowane ulepszanie kodu. Inną rzeczą jest to, że jeśli coś się zawiesza, OTP pozwala na ponowne uruchomienie bardzo szybko, więc jeśli twoje 'module2: do_something/1' ulegnie awarii z nowym formatem danych, może odzyskać.Te rzeczy idą w parze i są wciąż o rząd wielkości prostsze i bardziej niezawodne niż w jakimkolwiek innym środowisku uruchomieniowym. –
W rzeczywistości zawieszenie procesu OTP nie jest dla mnie jasne. Zakładam, że to zawiesza proces po tym, jak trwają jakiekolwiek wywołania 'handle_call/handle_cast', w przeciwnym razie nie byłoby w stanie użyć stanu migrowanego, prawda? – KallDrexx
Zostało to wyjaśnione tutaj (w podsekcji aktualizacji): http://erlang.org/doc/design_principles/release_handling.html#id78465 Release Handler używa 'sys: suspend/1,2',' sys: change_code/4,5 ', i' sys: resume/1,2', aby zawiesić, uaktualnić, a następnie wznowić proces. – Amiramix