Kiedy NIE jest dobrze używać aktorów w akka / erlang?

58

Codziennie pracuję z akką od 7-8 miesięcy. Kiedy zaczynałem, pracowałem nad aplikacjami i zauważyłem, że aktorzy będą wykorzystywani praktycznie w dowolnym miejscu w systemie aktorów do komunikacji między większością obiektów. Więc zrobiłem to samo - podkręć innego aktora dla x / y / z.

Wydaje mi się, że może to być zbyt niedyskryminujące, co zwiększa złożoność tam, gdzie nie jest potrzebne - ale nie mogę znaleźć dyskusji na temat tego, gdzie należy zastosować aktorów kontra zwykłą synchronizację, a nawet asynchroniczną logikę poprzez futures. Zacząłem się zastanawiać, kiedy mój współpracownik wspomniał o czymś podobnym. Niedawno zdałem sobie sprawę z kilku przypadków, w których zastanawiałem się nad zadaniem, a następnie uniknąłem tworzenia innego aktora, ponieważ mogłem bezpiecznie osiągnąć ten sam wynik w niezmiennej implementacji - np. Coś takiego jak uzyskiwanie wartości konfiguracyjnych z bazy danych lub pliku w miejscu, do którego bardzo rzadko i gdzie uzyskujesz dostęp oczekiwanie na wynik to rzeczywisty przypadek użycia.

W szczególności wydaje mi się, że w każdym przypadku, gdy grasz z niezmiennym stanem, aktorzy tworzą złożoność i ograniczają przepustowość - na przykład czystą funkcję w obiekcie można wywoływać jednocześnie bez żadnego ryzyka przy dowolnym poziomie współbieżności, jednak aktor może przetwarzać tylko jedną wiadomość na raz. Alternatywnym rozwiązaniem jest zaparkowanie wątku, jeśli musisz poczekać na wynik, chyba że zaczniesz używać kontraktów futures, ale w przypadkach, w których nie musisz się martwić o asynchroniczne przesyłanie wiadomości lub skalowanie, wydaje się, że zatrudnienie aktora może być przesadą.

Więc moje pytanie brzmi - czy jest zły czas na wykorzystanie aktorów? Jestem ciekawy, jak wygląda Erlang i naprawdę chciałbym wglądu innych ludzi. Lub jeśli istnieją jakieś zasady dotyczące korzystania z aktora.

JasonG
źródło
Jaka praca daje możliwość codziennej pracy z Akką przez wiele miesięcy?
Den
3
Dobre. :) Lub sam dokonaj zmiany.
JasonG
Chciałbym wiedzieć dokładnie, jakie są kompromisy między askwcieleniem się w rolę aktora a zwykłym użyciem równiny Future.
Max Heiber,
2
Skończyłem pisać książkę o Akce kilka lat później i myślę, że można to podsumować w następujący sposób: Jeśli masz jakiś stan - np. Licznik - wiele wątków czytających i zapisujących z / do tej wartości skończy się tupaniem bez synchronizacji i blokowania. Jeśli umieścisz ten stan w aktorze, nagle możesz mieć pewność, że jest on dostępny bezpiecznie i nie upuszczasz zapisów ani nieczytelnych odczytów. Aktorzy w erlang i akka również zakładają, że stan może się zepsuć, a aktor może rzucać błędy, aby mieć pewne cechy systemu samoleczenia. Futures są prostsze, jeśli nie musisz mutować
JasonG
wiele lat później jestem programistą erlang / eliksir i wracam do tego z nowymi spostrzeżeniami :) Eliksir jest zupełnie inny, ponieważ nie ma żadnych obiektów jako alternatywy dla procesów, więc procesy są zawsze budowane, gdy potrzebujesz stanu . To się po prostu dzieje równolegle. To wciąż najlepsza heurystyka.
JasonG

Odpowiedzi:

23

To pytanie mnie interesuje i prowadziłem badania. Aby zobaczyć inne punkty widzenia, zobacz ten post na blogu autorstwa Noela Walsha lub to pytanie dotyczące przepełnienia stosu. Mam kilka opinii, które chciałbym przedstawić:

  • Myślę, że Akka, ponieważ współpracuje z wiadomościami, zachęca do „pchnięcia”. Często w przypadku współbieżności twierdziłbym, że nie tego chcesz. Pull jest znacznie bezpieczniejszy. Na przykład wspólnym wzorcem dla systemów rozproszonych jest zestaw pracowników przetwarzających informacje w kolejce . Oczywiście jest to możliwe w Akka, ale niekoniecznie jest to pierwsze podejście, jakie ludzie próbują . Akka oferuje również trwałe skrzynki pocztowe, ale znowu zależy to od sposobu ich użycia - pojedyncza kolejka współdzielona jest znacznie bardziej elastyczna niż kolejki na pracownika w celu równoważenia / ponownego przypisywania pracy.
  • Łatwo jest wejść w sposób myślenia o zastępowaniu lekcji aktorami. W rzeczywistości niektórzy nawet opowiadają się za tym, mówiąc, że aktorzy powinni robić tylko jedną rzecz . Doszedł do logicznego wniosku, że zwiększa złożoność kodu, jak opisuje Jason, ponieważ jeśli każda klasa jest aktorem, to jest wiele dodatkowych komunikatów i bloków odbierania / wysyłania. Utrudnia także zrozumienie i przetestowanie kodu, ponieważ tracisz formalność interfejsów - i nie jestem przekonany, że komentarze są rozwiązaniem tego problemu . Również pomimo legendarnej skuteczności Akki , podejrzewam, że proliferacja aktorów nie jest dobrym pomysłem pod względem wydajności - kiedy używamy wątków Java, wiemy, że są cenne i odpowiednio je chronią.
  • Jest to związane z poprzednim punktem, ale inną irytacją jest utrata informacji o typie, którą podkreślają Noel i Pino, ponieważ dla wielu z nas właśnie dlatego używamy Scali, a nie innych języków, takich jak Python. Jest na to kilka sposobów, ale są one niestandardowe , niezalecane lub eksperymentalne .
  • W końcu współbieżność, nawet jeśli masz sposób myślenia „niech to się zawiesi” , jest trudna. Alternatywne modele programowania mogą pomóc, ale nie usuwają problemów - zmieniają je - dlatego dobrze jest o nich myśleć formalnie . Właśnie dlatego programista Joe Average sięga po gotowe narzędzia, takie jak RabbitMQ, Storm, Hadoop, Spark, Kafka lub NoSQL. Akka ma kilka gotowych narzędzi i komponentów, co jest fajne, ale wydaje się również dość niski poziom, więc lepiej przygotowane wspólne elementy systemów rozproszonych pomogłyby programistom i zapewnić prawidłowe zbudowanie systemów.

Podobnie jak Jason, chętnie słyszę wgląd innych ludzi tutaj. Jak mogę rozwiązać niektóre z powyższych problemów i lepiej korzystać z Akka?

Mark Butler
źródło
1
Znalazłem ten post na temat testowania w spot Akka timgilbert.wordpress.com/2013/07/07/...
Mark Butler
„Kompozycja nad dziedziczeniem” jest nadal przydatna do wstrzykiwania zależności. Np. Przekaż zależność jako jako rekwizyty (używa parametrów konstruktora). Problemem byłby wówczas nadzór - stworzony aktor byłby nadzorowany w innym miejscu niż aktor. Router może być wykorzystany do owijania strategii nadzoru wokół aktorów stworzonych na najwyższym poziomie, ale to już dość skomplikowane! Wydaje mi się, że ciasto byłoby lepszym rozwiązaniem - na przykład cechę tworzenia aktora dla aktora usługi db. Następnie wystarczy użyć testowej cechy usługi db mock / stub db w kontekście tekstowym.
JasonG
1
Tak, dokładnie - nadzór nie działa dobrze z zastrzykiem zależności. Miałem też przypadki, w których musiałem wstrzyknąć fabrykę, a nie klasę, w przeciwnym razie wystąpiłyby dziwne problemy z zamykaniem - zgaduję z powodu traktowania wątków przez Akkę.
Mark Butler,
Myślę, że to nie jest zły pomysł - dzięki dzięki - mógłbym spróbować o tym trochę napisać i nawiązać z nim kontakt towarzyski. Możesz wstrzyknąć coś, co daje Rekwizyty? Fabryka aktorów, w której konsument może utworzyć aktora. np. metoda def (arg) = zwróć rekwizyty (nowy ActorWithArgs (arg)) z tej fabryki (psuedo?), abyś mógł ustawić aktora we właściwym kontekście. To wydaje się być dobrym pomysłem i dobrym celem dla rozwiązań ciastek dla di.
JasonG
Właśnie zacząłem śledzić ten kurs: coursera.org/course/posa Chociaż jest on skierowany przede wszystkim do osób programujących Androida, jest to również dobry przegląd współbieżności w Javie. Zastanawiam się więc nad tym: „Czy Akka nie jest tylko fantazyjną formą pętli zdarzeń z dzwonkami i gwizdkami (ponieważ można umieszczać pętle zdarzeń na różnych wątkach)?”
Mark Butler
21

Warto zastanowić się, do czego służy model aktora: taki jest model aktora

  1. model współbieżności
  2. co pozwala uniknąć jednoczesnego dostępu do stanu zmiennego
  3. za pomocą mechanizmów komunikacji asynchronicznej w celu zapewnienia współbieżności.

Jest to cenne, ponieważ korzystanie ze stanu wspólnego z wielu wątków staje się naprawdę trudne, szczególnie gdy istnieją relacje między różnymi składnikami stanu wspólnego, które muszą być zsynchronizowane. Jeśli jednak masz składniki domeny, w których:

  • Nie zezwalasz na współbieżność LUB
  • Nie zezwalasz na stan zmienny (jak w programowaniu funkcjonalnym), LUB
  • Musisz polegać na mechanizmie komunikacji synchronicznej,

wtedy model aktora nie zapewni wielu (jeśli w ogóle) korzyści.

Mam nadzieję, że to pomaga.

Aidan Cully
źródło
Dzięki - myślę, że naprawdę musisz wtedy ocenić dwie rzeczy - stan zmienny i współbieżność? Nie ma problemu do rozwiązania, jeśli klasa jest bezstanowa LUB nie jest współbieżna. Jeszcze jedna uwaga - myślę, że tolerancja na uszkodzenia to kolejna kwestia? np. jeśli masz niezmiennego klienta redis, ale może on zawieść podczas działania - czy nie byłby to dobry przypadek użycia? że ponowne uruchomienie tego aktora może być konieczne? Ponieważ - chociaż klasa może być całkowicie niezmienna - bardzo możliwe jest, że istnieje stan skorumpowany zewnętrzny względem niezmiennego aktora, który może spowodować jego awarię.
JasonG
Myślę, że aktor, który jest odpowiedzialny za komunikację z redis, otrzyma wyjątek i zrestartuje się.
JasonG
@JasonG Według mnie „odporność na awarie” nie jest ogólnie aspektem modelu aktora - jest to coś, co pochodzi z implementacji modelu aktora w Erlang (a może Akka?). Nie mogę rozmawiać z redis, chociaż muszę przyznać, że „niezmienny klient” brzmi dla mnie dziwnie ... Jeśli jakiś element oprogramowania może zawieść, nie rozumiem, jak można go uznać za niezmienny.
Aidan Cully
Tolerancja błędów i nadzór istnieją w akka i są bardzo podobne do erlang. Rozumiem, co mówisz o niezmiennym kliencie, ale niezmienny oznacza, że ​​stan kodu nigdzie się nie zmienia. Jeśli zainicjuję połączenie podczas uruchamiania, możliwe jest, że kod klienta będzie niezmienny dzięki strategii restartu zastosowanej dla dowolnego rodzaju błędu, po prostu ponownie inicjując aktora po napotkaniu problemu.
JasonG
12

Twoja intuicja jest prawidłowa, IMHO. Używanie aktorów wszędzie jest jak przysłowiowy młotek i widzenie tylko gwoździ.

Najlepszą praktyką Erlang jest stosowanie procesów / aktorów do wszystkich działań, które odbywają się jednocześnie. To jest tak jak w prawdziwym życiu. Czasami trudno jest znaleźć właściwą ziarnistość, ale w większości przypadków po prostu wiesz, patrząc na modelowaną domenę i zachowując zdrowy rozsądek. Obawiam się, że nie mam lepszej metody, ale mam nadzieję, że to pomoże.

Vlad Dumitrescu
źródło
Fajne dzięki. Naprawdę interesuje mnie, co się stanie w tej dyskusji.
JasonG
0

W celu przesyłania komunikatów wejścia / wyjścia:

Niedawno spotkałem się z aplikacją opartą na akce, w której model aktora faktycznie powodował problemy z współbieżnością, prostszy model byłby lepszy przy obciążeniu.

Problem polegał na tym, że przychodzące wiadomości poruszały się po różnych „liniach” (różnymi ścieżkami aktorów), ale kod zakładał, że wiadomości dotrą do miejsca docelowego w tej samej kolejności, w jakiej przybyły. Tak długo, jak dane przychodziły w odpowiednio dużych odstępach czasu, działało to, ponieważ do miejsca docelowego byłby tylko jeden konflikt. Kiedy interwały zmniejszały się, zaczęli schodzić z porządku i powodować dziwne zachowania.

Problem mógł zostać rozwiązany poprawnie przy nieco mniejszej liczbie aktorów, ale łatwo jest popełnić błąd, gdy nadużywają ich.

DHa
źródło
Jest to fundamentalne i szeroko stosowane. Możesz zagwarantować tylko zamówienie między dwoma aktorami. doc.akka.io/docs/akka/current/scala/general/… Nie możesz zagwarantować dostawy zamówień w żadnym systemie, w którym masz ochotę / chcesz. andreasohlund.net/2012/01/18/dont-assume-message-ordering
JasonG
-1

Moim zdaniem istnieją dwa przypadki użycia aktorów. Zasoby współdzielone, takie jak porty i tym podobne, oraz stan duży. Pierwsza była dobrze omówiona do tej pory w dyskusji, ale ważny jest również duży stan.

Duża struktura przekazywana przy każdym wywołaniu procedury może zużywać dużo stosów. Ten stan można umieścić w osobnym procesie, strukturę zastąpić identyfikatorem procesu, a proces ten jest sprawdzany w razie potrzeby.

Bazy danych, takie jak mnesia, można traktować jako zewnętrzne zapisywanie stanu w procesie zapytania.

Tony Wallace
źródło
1
Możesz wyjaśnić? Masz na myśli duży stan w sieci? W ramach procesu lokalnego przekazanie odwołania do niezmiennej struktury nie ma prawie żadnych kosztów i nie można przekazać struktur zmiennych. Zakładając, że mówisz o dużej zmiennej strukturze, którą należy skopiować, aby wysłać? Więc to w zasadzie to samo - stan wspólny. Rozmiar tak naprawdę nic nie zmienia. Daj mi znać, jeśli się mylę.
JasonG
Jak przechodzisz przez odniesienie? Z pewnością sposobem na to jest wprowadzenie struktury do procesu i przekazanie jej do procesu. Połączenia lokalne umieszczają dane na stosie, a każde połączenie rekurencyjne zrobi to ponownie (z wyjątkiem rekurencji ogona). Rekurencyjne przetwarzanie struktury może być wykonane przy użyciu list do przenoszenia stanu z jednego wywołania do drugiego, ten stan może następnie odwoływać się do struktury w innym procesie.
Tony Wallace