2011-11-30 13 views
5

Próbuję zbudować wysokowydajny system rozproszony z Akka i Scala.Obsługa ramek Akka do wyszukiwania duplikatów wiadomości

Jeśli pojawi się komunikat żądający kosztownej (i nie wymagającej efektów ubocznych) obliczeń, a dokładnie to samo obliczenie zostało już wcześniej zlecone, chcę uniknąć ponownego obliczania wyniku. Jeśli obliczone wcześniej żądanie zostało już zakończone, a wynik jest dostępny, mogę go buforować i ponownie użyć.

Jednak okno czasowe, w którym można żądać duplikowania, może być dowolnie małe. na przykład Mogłem dostać tysiąc lub milion wiadomości z prośbą o tak samo kosztowne obliczenia w tej samej chwili dla wszystkich praktycznych zastosowań.

Istnieje komercyjny produkt o nazwie Gigaspaces, który rzekomo obsługuje tę sytuację.

Wydaje się jednak, że obecnie w Akka nie ma wsparcia dla obsługi duplikatów zleceń pracy. Biorąc pod uwagę, że struktura Akka ma już dostęp do wszystkich komunikatów przesyłanych za pośrednictwem frameworka, wydaje się, że rozwiązanie ramowe może mieć wiele sensu.

Oto, co proponuję do struktury Akka: 1. Utwórz cechę, która wskaże typ wiadomości (np. "ExpensiveComputation" lub coś podobnego), które mają podlegać poniższemu podejściu do buforowania. 2. Inteligentnie (hashowanie itp.) Identyfikuje identyczne wiadomości odebrane przez (tych samych lub różnych) aktorów w konfigurowalnym oknie czasowym. Inne opcje: wybierz maksymalny rozmiar bufora pamięci do użycia w tym celu, z zastrzeżeniem wymiany (np. LRU) itp. Akka może również wybrać buforowanie tylko wyników wiadomości, które były kosztowne w przetwarzaniu; wiadomości, które zajęły bardzo mało czasu na przetwarzanie, mogą być ponownie przetworzone w razie potrzeby; nie ma potrzeby marnowania cennego miejsca buforowego na buforowanie ich i ich wyników. 3. Gdy identyczne wiadomości (odebrane w tym oknie czasowym, prawdopodobnie "w tym samym momencie") zostaną zidentyfikowane, unikaj niepotrzebnych duplikatów obliczeń. Ramy robią to automatycznie, a zasadniczo duplikaty wiadomości nigdy nie zostaną odebrane przez nowego aktora do przetwarzania; zanikłyby one w milczeniu, a wynik przetworzenia go raz (czy to obliczenie było już zrobione w przeszłości, czy też trwające właśnie wtedy) został wysłany do wszystkich odpowiednich odbiorców (natychmiast, jeśli jest już dostępny, a po zakończeniu obliczeń, jeśli nie). Zauważ, że wiadomości powinny być uważane za identyczne, nawet jeśli pola "odpowiedzi" są różne, o ile semantyka/obliczenia, które reprezentują są identyczne pod każdym innym względem. Należy również pamiętać, że obliczenia powinny być czysto funkcjonalne, tzn. Wolne od skutków ubocznych, aby optymalizacja buforowania sugerowała działanie i nie zmieniać w ogóle semantyki programu.

Jeśli to, co sugeruję, nie jest zgodne ze sposobem postępowania w Akka i/lub jeśli widzisz kilka silnych powodów, dla których jest to bardzo zły pomysł, proszę dać mi znać.

Dzięki jest niesamowite, Scala

Odpowiedz

10

co prosicie jest nie zależy od ram Akka ale raczej to jak architekt aktorów i wiadomości. Najpierw upewniając się, że twoje wiadomości są niezmienne i mają odpowiednio zdefiniowane tożsamości za pomocą metod equals/hashCode. Klasy przypadków dają ci oba za darmo, ale jeśli masz actorRefs osadzone w wiadomości dla celów odpowiedzi, będziesz musiał nadpisać metody tożsamości. Parametry klasy sprawy powinny mieć te same właściwości rekursywnie (niezmienna i właściwa tożsamość).

Po drugie, musisz dowiedzieć się, w jaki sposób aktorzy zajmą się przechowywaniem i identyfikowaniem bieżących/przeszłych obliczeń.Najłatwiej jest unikalnie mapować wnioski do aktorów. W ten sposób ten aktor i tylko ten aktor kiedykolwiek przetworzy to konkretne żądanie. Można to zrobić łatwo, biorąc pod uwagę ustalony zestaw aktorów i kod haszujący żądania. Punkty premiowe, jeśli zestaw aktorów jest nadzorowany, gdzie kierownik zarządza równoważeniem obciążenia/mapowaniem i zastępowaniem nieudanych aktorów (Akka sprawia, że ​​ta część jest łatwa).

Wreszcie sam aktor może zachować zachowanie buforowania odpowiedzi na podstawie opisanych kryteriów. Wszystko jest bezpieczne dla wątków w kontekście aktora, więc pamięć podręczna LRU kluczowana przez samo żądanie (dobre właściwości tożsamości pamiętają) jest łatwa w przypadku każdego pożądanego zachowania.

+0

Istnieje odmiana tego, która zależy od tego, czy ostatnia wiadomość jest przetwarzana, a nie wcześniejsza w kolejce. tj. nie chcę rozpoczynać kosztownych obliczeń, dopóki nie upewnię się, że wszystkie wiadomości zostały odebrane. Myślę, że powyższe podejście można zmodyfikować za pomocą FSM, aby osiągnąć ten sam rezultat. – dres

5

Jak mówi Neil, nie jest to tak naprawdę funkcjonalność szkieletowa, to raczej trywialne, aby to zaimplementować, a nawet przekształcić ją w swoją własną cechę.

trait CachingExpensiveThings { self: Actor => 
    val cache = ... 
    def receive: Actor.Receive = { 
    case s: ExpensiveThing => cachedOrCache(s) 
    } 

    def cacheOrCached(s: ExpensiveThing) = cache.get(s) match { 
    case null => val result = compute(s) 
       cache.put(result) 
       self.reply_?)(result) 
    case cached => self.reply_?)(cached) 
    } 
    def compute(s: ExpensiveThing): Any 
} 


class MyExpensiveThingCalculator extends Actor with CachingExpensiveThings { 
    def compute(s: ExpensiveThing) = { 
    case l: LastDigitOfPi => ... 
    case ts: TravellingSalesman => ... 
    } 
} 
+0

Ja również obliczyłem ostatnią cyfrę Pi, jak to się stało? ; p –

+1

Ostatni to π –

0

Nie wiem, czy wszystkie te obowiązki powinny być obsługiwane tylko przez Akka. Jak zwykle wszystko zależy od skali, a w szczególności od liczby atrybutów, które określają wyjątkowość komunikatu.

W przypadku mechanizmu pamięci podręcznej, wspomniane wcześniej podejście z unikatowym odwzorowaniem żądań na aktorów jest rozwiązaniem, zwłaszcza że może być wspierane przez utrwalanie.

W przypadku tożsamości, zamiast prostego sprawdzania równości (który może być wąskim gardłem) będzie raczej używać algorytmu opartego wykres jaksignal-collect.