Warning: strpos() [function.strpos]: Empty needle in /home/dealer/kasat/pkg/cahir/web/html/lib/plugins/translation2/action.php on line 53

Warning: Cannot modify header information - headers already sent by (output started at /home/dealer/kasat/pkg/cahir/web/html/lib/plugins/translation2/action.php:53) in /home/dealer/kasat/pkg/cahir/web/html/inc/actions.php on line 163
erlang:distributed [Krystian Bacławski Wiki]
 

Programowanie rozproszone

Motywacja

Pożądane cechy systemu rozproszonego

  1. Przezroczystość: Z punktu widzenia programisty zdalny węzeł powinien być obsługiwany tak samo jak lokalny węzeł. W rzeczywistości narzut na ilość połączeń i komunikatów oraz zależności czasowe są niepomijalne.
  2. Skalowalność: Dołożenie zasobów do działającego systemu powoduje liniowe zwiększenie jego wydajności. Idealne skalowanie nie jest możliwe – prawo Amdahla.
  3. Tolerowanie awarii: Jeśli to możliwe system nie posiada SPoF (ang. single point of failure). Ciągłość pracy systemu zapewnia się przez replikację zasobów. Implementuje się systemy monitorujące, które w przypadku awarii podejmują akcje naprawcze.
  4. Bezpieczeństwo: Jeśli system jest w strefie, do której mają dostęp potencjalnie niebezpiecznie użytkownicy, musimy zapewnić autoryzację i system uprawnień do zasobów.

Wzorce projektowe dla systemów rozproszonych

Wzorce architektoniczne

Potrzebujemy łatwo móc implementować aplikacje na bazie znanych architektur:

  • Klient-serwer – ustalona odpowiedzialność;
  • Warstwowa – usługi są zorganizowane warstwowo, rozwinięcie w/w architektury.
  • Klaster – wszystkie komputery ściśle współpracują – klika; z reguły wszystkie nad ustalonym zadaniem,
  • Peer-to-peer – luźno powiązane; nie ma ustalonych odpowiedzialności,
  • Blackboard – centralna baza danych będąca źródłem wiedzy; procesy przetwarzają wiedzę; arbiter ustala kto co ma robić,
  • Zunifikowana przestrzeń – wszystkie komputery widzą i utrzymują jeden zasób; np. rozproszona baza danych.

Wzorce organizacyjne:

  • Składowanie wyników (ang. cache servers).
  • Serwery pośredniczące (ang. proxy servers).
  • Rozkładanie obciążenia (ang. load balancing).
  • Uwzględnianie położenia geograficznego.

Wymagania wobec języka

Wymarzony język

Czego moglibyśmy chcieć od języka (i ew. maszyny wirtualnej), żeby łatwiej pisało się aplikacje rozproszone.

  • Efektywna komunikacja przez przekazywanie komunikatów. Narzut na komunikację między węzłami musi być możliwie jak najmniejszy.
  • Narzut na zarządzanie zasobami systemowymi musi być akceptowalnie mały. Po dołożeniu zasobów do pojedynczej maszyny, albo dołożeniu maszyn do sieci, nasza aplikacja powinna odczuwać wzrost wydajności.
  • Musi być możliwe sprawne monitorowanie systemu. Wbudowane sygnalizowanie błędów i mechanizmy obsługi awarii.
  • Programista w łatwy sposób powinien radzić sobie ze znajdowaniem błędów lub problemów z wydajnością (wąskich gardeł).
  • Bardzo przydatne może być wprowadzanie poprawek lub dodawanie funkcjonalności do działającego systemu.
  • Powinny być dostępne biblioteki, które automatyzują często wykorzystywane elementy systemów rozproszonych.
  • Komunikacja z niezależnymi komponentami systemu musi odbywać się za pomocą standardowych protokołów sieciowych.

Wymarzona biblioteka standardowa...

Czyli wynajdowanie koła od nowa nie jest pożytecznym zajęciem…

Jakich funkcjonalności chcemy od biblioteki standardowej, żeby było wygodniej?

  • Łatwej komunikacji z węzłami, plus globalna przestrzeń nazw.
  • Łatwego deklarowania, wersjonowania i wymiany komponentów.
  • Szablony wzorców projektowych dla pojedynczych procesów i grup.
  • Mechanizmy logowania i zbierania statystyk.
  • Monitorowanie – wykrywanie awarii, mierzenie wydajności.
  • Rozproszona baza danych – wydajna z replikacją.
  • Obsługa protokołów sieciowych – TCP, UDP, SCTP, SSL.
  • Obsługa protokołów wymiany danych – XML i ASN.1.
  • Interoperatywności z systemami:
    • rozproszonymi – standard CORBA,
    • monitorującymi – protokół SNMP,
    • bazodanowymi – biblioteka ODBC.
  • Możliwość dodania konsoli administratora – Telnet, SSH.
  • Komunikacji z popularnymi usługami – protokoły HTTP,FTP, SMTP, LDAP, etc.

Podstawy - Programowanie rozproszone w Erlangu

Węzły

Nazwy węzłów

Czym jest węzeł Erlanga?

Instancja maszyny wirtualnej Erlanga. Posiada unikalny identyfikator. Opcjonalnie może mieć klucz do autoryzacji. Można odpalić wiele węzłów na tej samej maszynie.

Jak wystartować węzeł Erlanga:

Należy wystartować specjalny proces – jądro umożliwiające rozproszenie w sieci TCP/IP z modułu net_kernel. Może to nastąpić niejawnie w wyniku wywołania jakiejś funkcji np. z modułu net_adm. Zanim jednak to nastąpi trzeba nadać nazwę.

Czym są nazwy?

Nazwa węzła składa się dwóch części, z nazwy:

  • węzła – właściwie tutaj jest pełna dowolność,
  • nazwy domenowej (DNS) komputera, na którym jest odpalona maszyna wirtualna. Niech nasza maszyna ma adres komputer.ii.uni.wroc.pl. Nazwa domenowa:
  • krótka to komputer,
  • długa, czyli FQDN (ang. Fully Qualified Domain Name) to komputer.ii.uni.wroc.pl.

Jak nadawać nazwy?

Nadawanie nazwy:

  • Z linii poleceń:
erl -sname supelek
(supelek@komputer)>
erl -name supelek
(supelek@komputer.ii.uni.wroc.pl)>
  • Przy pomocy modułu net_kernel:
net_kernel:start([supelek,shortnames]).
(supelek@komputer)>
net_kernel:start([supelek,longnames]).
(supelek@komputer.ii.uni.wroc.pl)>

Uwaga: Wersja z krótką nazwą, nie wymaga interakcji z DNS-em.

Jak węzły się odnajdują?

Na każdym komputerze, na którym wystartujemy węzeł Erlangowy odpalany jest proces epmd. Zajmuje się on mapowaniem z nazw węzłów na pary (NumerIP,Port).

Połączenia

Połączenia między węzłami

Jak nawiązać połączenie?

Po nadaniu nazwy i wystartowaniu jądra sieciowego, funkcja is_alive powinna zwracać prawdę. Od tej chwili możemy się łączyć.

Połączenie można nawiązać jawnie: net_adm:connect_node lub niejawnie przy każdej próbie wykorzystania zdalnego węzła. Połączenia są dwukierunkowe. Można jawnie się rozłączyć: erlang:disconnect(Node).

Ilość połączeń…

Po podłączeniu do zdalnego węzła automatycznie zostają nawiązane połączenia do wszystkich widzianych przez niego węzłów. Dość szybko tworzą duże kliki. Powstaje spory narzut na ilość połączeń i komunikację. Takie niejawne łączenie można wyłączyć:

erl -connect_all false

Widoczność połączeń

Funkcje odpytujące system nazw:

  • node/0 – nazwa bieżącego węzła.
  • node/1 – na jakim węźle znajduje się proces, referencja, etc.
  • nodes/0 – lista widocznych węzłów,
  • nodes/1 – lista węzłów połączonych, ukrytych, znanych, etc.

Po co są węzły ukryte?

  • Nie są kluczowe do działania systemu i nie potrzebują być monitorowane.
  • Niejawne bramki między klikami. Luźno połączone komponenty większego systemu.
  • Elementy nadzorujące i śledzące.

Monitorowanie węzłów

Ogólnie informacje…

Oczywiście na własne potrzeby maszyny wirtualne monitorują siebie nawzajem. Mechanizm net-tick. Połączenie każdy z każdym.

Monitorowanie…

Należy włączyć system monitorowania. Domyślnie monitorowane są tylko widoczne węzły.

net_kernel:monitor_nodes(Flag)
net_kernel:monitor_nodes(Flag,[Option])

W skrzynce procesu będą się pojawiać komunikaty w następującym formacie:

{nodeup,Node} | {nodeup,Node,[{Tag,Val}] }
{nodedown,Node} | {nodedown,Node,[{Tag,Val}]}

Autoryzacja i uwierzytelnianie

Autoryzacja…

Wbudowany mechanizm jest bardzo prostu i ogranicza się do tego, kto może się podłączyć do bieżącego węzła:

net_kernel:allow([Nodes])

Uwierzytelnianie…

Używany prymitywny mechanizm ciasteczek. Ciasteczko może być ustawiane:

  • z pliku $HOME/.erlang.cookie z prawami odczytu dla właściciela,
  • z linii poleceń erl -setcookie, ale każdy może podejrzeć przy pomocy polecenia ps,
  • przy pomocy wbudowanej funkcji erlang:set_cookie/2
erlang:set_cookie(node(),Cookie)
erlang:set_cookie(SomeNode(), DifferentCookie)

Procesy

Procesy na zdalnych węzłach

Lista różnic w obsłudze procesów… Nie jest tego zbyt wiele:

  • Mamy dodatkowe funkcje do tworzenia procesów:
spawn(Node,Module,Fun,Args)
spawn_link(Node,Module,Fun,Args)
spawn_opt(Node,Module,Fun,Args,[Option])
  • Nazywanie procesów działa lokalnie na pojedynczym węźle. Jeśli chcemy globalnie nazywać procesu musimy użyć modułu global.
  • Musimy pamiętać, że teraz zbiór łącz używany do przesyłania sygnałów między procesami może być rozproszony.
  • Pojawiają się nowe przyczyny zakończenia procesu.

Cała reszta działa tak samo – oczywiście poza wydajnością.

Nie ma spawn_monitor/4. Zostało zastąpione uniwersalniejszą funkcją spawn_opt/5.

Limity przezroczystości

Czego nie da się zignorować?

  • awarii węzłów,
  • dużych opóźnień.

Pojedyncza maszyna, wiele węzłów:

Jeśli odpalamy z jednego konta użytkownika to poza zmianami w wynikającymi z potrzeby stosowania węzłów nic nie powinno nas zaskoczyć.

Wiele maszyn, wiele węzłów:

Co z dostępem do kodu z pozostałych maszyn. Opóźnienia mogą być większe. Większe prawdopodobieństwo gubienia pakietów. Potencjalnie problemy ze współpracą z DNS.

Pozostałe wyzwania:

Problemy z zaporami ogniowymi, trasowaniem. Trzeba się martwić bezpieczeństwem – wbudowany protokół jest przystosowany do komunikacji w bezpiecznych sieciach.

Kod programu

Wymagania:

Moduły nie są automatycznie przesyłane między maszynami. Na docelowej maszynie musi być skompilowana wersja modułu, inaczej spawn/4 zawiedzie.

Rozwiązanie:

  1. Ręczne kopiowanie plików między maszynami.
  2. Dzielony system plików – NFS, glusterfs, etc.
  3. Serwer kodu – na żądanie pobiera moduły z centralnego repozytorium.
  4. Z poziomu powłoki można wywoływać polecenie nl(Module). Załaduje moduł na wszystkie połączone węzły.

Moduły - Programowanie rozproszone i bibliotek standardowa

Administracja węzłami - moduł net_adm

Sprawdzanie łączności, nazwy komputerów

Plik .hosts.erlang przechowuje wyrażenia erlangowe, które są nazwami komputerów, na których mogą być odpalone maszyny Erlangowe. Przydatne w środowiskach z dzielonym systemem plików, a bez DNS-a.

'hera.stud.ii'.
'zeus.stud.ii'.
'mekong.stud.ii'.
(nowa linia)
dns_hostname(Host) -> {ok, Name} | {error, Host}
host_file() -> Hosts | {error, Reason}
localhost() -> Name
names() -> {ok, [{Name, Port}]} | {error, Reason}
net_adm:ping(Node) -> pong | pang
world() -> [node()]
world_list(Hosts) -> [node()]

Zdalne wywołania funkcji - moduł rpc

Współpraca z pojedynczymi węzłami

Wywołania synchroniczne z wynikiem:

rpc:call(Node, Module, Function, Args, Timeout) -> Res | {badrpc, Reason}
rpc:block_call(Node, Module, Function, Args, Timeout) -> Res | {badrpc, Reason}

Wywołania asynchroniczne:

rpc:async_call(Node, Module, Function, Args) -> Key
rpc:yield(Key) -> Res | {badrpc, Reason}
rpc:yield(Key) -> Res | {badrpc, Reason}
rpc:nb_yield(Key) -> {value, Val} | timeout
rpc:cast(Node, Module, Function, Args) -> void()

Współpraca z grupami węzłów

Rozgłaszanie:

rpc:multicall(Nodes, Module, Function, Args, Timeout) -> {ResL, BadNodes}
rpc:eval_everywhere(Nodes, Module, Function, Args) -> void()
rpc:abcast(Nodes, Name, Msg) -> void()
rpc:sbcast(Nodes, Name, Msg) -> {GoodNodes, BadNodes}

Zarządzanie globalnymi nazwami - moduł global

Rejestracja globalnych nazw

Wiemy, że odwoływanie się do procesów po nazwach jest wygodne. Dobrze jest mieć rozproszony mechanizm przydzielania nazw procesom.

global:register_name(Name, Pid, Resolve) -> yes | no
global:registered_names() -> [Name]
global:unregister_name(Name) -> void()
global:whereis_name(Name) -> pid() | undefined
global:send(Name, Msg) -> pid()

Warto zwrócić uwagę, że register_name działa atomowo.

Jeśli zarejestrowany proces zostanie zakończony, nazwa zostaje globalnie wyrejestrowana.

Konflikty nazw

Problem występuje kiedy w klastrze istnieje już proces, który został w ten sposób nazwany. Wtedy funkcja register_name/3 wywołuje funkcję zwracającą poprawny identyfikator procesu. Jeśli funkcja Resolve nie zwróci poprawnego identyfikatora, nazwa zostaje wyrejestrowana.

1: global:random_exit_name(Name, Pid1, Pid2) -> Pid1 | Pid2''
2: global:random_notify_name(Name, Pid1, Pid2) -> Pid1 | Pid2
3: global:random_notify_name(Name, Pid1, Pid2) -> Pid1 | Pid2
  1. Wybiera losowo prawidłowy proces, a drugi z nich zabija:
  2. Wybiera losowo prawidłowy proces, a drugi informuje o konflikcie przy pomocy komunikatu: {global_name_conflict,Name}
  3. Uznaje oba procesy za konfliktujące i wysyła im komunikaty: {global_name_conflict, Name, OtherPid}

Zdalne uruchamianie węzłów - moduł slave

Uruchamianie maszyn Erlanga na żądanie

Posiadając konto uniksowe na zdalnej maszynie możemy wystartować tam jakiś proces, a gdyby to była maszyna Erlanga? Działa pod warunkiem, że po drugiej stronie jest ta sama wersja i podobne ułożenie katalogów.

slave:start(Host, Name, Args) -> {ok, Node} | {error, Reason}
slave:start_link(Host, Name, Args) -> {ok, Node} | {error, Reason}
slave:stop(Node) -> ok

O czym nie opowiedziałem

Inne ciekawe moduły:

  • global_group – partycjonowanie systemu, grupy z oddzielnymi przestrzeniami nazw,
  • pg2 – nazwane rozproszone grupy procesów,
  • pool – rozkładanie obciążenia miedzy grupę węzłów.

Inne mniej ważne rzeczy:

  1. Monitorowanie zasobów.
  2. Przywódca grupy.
 
erlang/distributed.txt · Last modified: 2010/05/07 23:12 by Krystian Bacławski
 
Except where otherwise noted, content on this wiki is licensed under the following license:CC Attribution-Noncommercial-No Derivative Works 3.0 Unported
Recent changes RSS feed Donate Powered by PHP Valid XHTML 1.0 Valid CSS Driven by DokuWiki