Własny EventLoop w Springu

0

Cześć, piszę sobie hobbysticznie gierkę przeglądarkową w standardowym stacku Spring Boot 2/SQL i nie mam pomysłu w jaki dobry sposób zaimplementować funkcjonalność obsługiwania wydarzeń po określonym czasie.

Mamy następujący Use Case - użytkownik chce sobie wybudować budynek. Czas budowy wynosi 1 h, po tym czasie następuje wybudowanie budynku, aktualizacja db, itp. Ponadto użytkownik może zawsze cofnąć rozkaz i odzyskać wydane surowce.

W prosty sposób można to zrobić używając ScheduledExecutorService - dać po prostu odpowiedni delay do zadania. Jednakże widzę tutaj dwa problemy:

  • nie widzę prostego sposobu w którym użytkownik może anulować rozkaz
  • dla każdego rozkazu przez długi okres czasu będzie utrzymywany jeden wątek, których w założeniu może być miliony

Więc zamiast tego myślałem o stworzeniu czegoś podobnego do js'owego EventLoopa (przynajmniej jeśli dobrze go rozumiem). W pseudo codzie:

public final class CommandsExecutor{
    PriorityQueue<AppEvent> queue;
 
   public CommandsExecutor(){
      this.run();
   }

   public <T extends AppEvent> void add(T event){
      queue.add(event);
   }

   public <T extends AppEvent> void remove(T event){
      queue.remove(event);
   }


   public void run(){
      while(true){
         if (LocalDateTime.now() == queue.peek().getExecutionTime()){
              CompletableFuture.supplyAsync(() -> queue.peek().proccess())
              queue.pop();
         }

      }
   }

}

Założenia:

  1. Klasa byłaby singletonem.
  2. Zadania w kolejce są priorytetowane ze względu na czas (im krótszy, tym event jest wyżej w kolejce)
  3. Zadania pewnie musiałyby być synchroniczne by nie było sytuacji, że event mający wydarzyć się później został zakończony przed eventem który wydarzył się wcześniej (bo np. transakcja się wcześniej skommitowała)

Nawet nie próbowałem czegoś takiego implementować więc nie mam pojęcia jak trudne by to było w implementacji (nie wiem jakie Spring może mieć zdanie o takich pomysłach). Mechanizm który zostanie stworzony będzie de facto podstawą systemu więc musi być szybki i niezawodny. I pytania - takie założenie ma sens? Jest jakiś wzorzec projektowy który pomaga rozwiązywać podobne problemy? Jest jakiś mechanizm w Springu który to zrobi za mnie? Jestem otwarty na pomysły.

1

Primo

dla każdego rozkazu przez długi okres czasu będzie utrzymywany jeden wątek, których w założeniu może być miliony

Nie będzie. To tak nie działa. zlecasz zadanie i wątek z puli zostaje przydzielony dopiero gdy zadanie ma się wykonać na czas wykonania zadania.
Kryje się natomiast o wiele bardziej poważny problem. Co gdy apka z jakiegoś powodu padnie? Kto wznowi zadania i na podstawie czego? Po drugie jesteś pewny czasu na JVM? Co gdy ktoś na serwerze cofnie strefę czasową?

Secundo
Twój event loop jest jeszcze gorszy i nie przynosi rozwiązań żadnych problemów. Ponadto w Javie mamy do tego lepsze biblioteki. Między innymi Reactor. Jednak event loop nie daje wielkiej przewagi przy scheduled jobach. Najczęściej eventloop jest przydatny by optymalnie wykorzystać CPU gdy mamy dużo zadań IO bound.

Tertio
Na twoim miejscu użyłbym czegoś w stylu event sourcing. Zapisywałbym decyzje userów jako eventy. Z pewną granulacją dajmy na to co 2 min przeliczałbym aktualny stan gry i zapisywał w bazie do odczytu. Czyli jedna bazka z eventami. Druga bazka z aktualnym stanem. Jeżeli potrzebowałbym bardzo aktualny stan to brałbym obliczony ostatni snapshot świata i aplikował nieuwzględnione eventy od tego czasu.

Dodatkowo JavaScript by mógł pokazywać logikę serwera bardziej real time, by dać wrażenie że wszystko żyje. Warto by też przekazywać do klientów czas serwera, aby wszyscy operowali tym samym czasem z dokładnością do kilku sekund.

0

Dzięki za odpowiedź.

  1. Właśnie nie wiedziałem jak to działa, bo nie potrafiłem znaleźć bardziej teoretycznych informacji na ten temat. Jeśli to działa tak jak mówisz to ok, w takim razie nie ma tego problemu. Problem padnięcia apki jest - myślałem o tym, że mogę persystować zadania w tabeli w bazie danych co jakiś czas, na przykład 30 min. i przy starcie aplikacja by sobie zaciągała na nowo zadania i je interpretowała. W zależności od wydajności mogę też zapisywać wszystkie. Czas to oczywiście inny problem, ale na razie go nie rozwiązuje.

  2. Znam Reactora, ale kojarzę go bardziej z WebFluxem a nie zwykłymi blokującymi aplikacjami webowy. Event Sourcing to jest chyba to czego potrzebuje, pomysł z dwoma bazami jest spoko, ale w czym jest lepszy od posiadania po prostu jednej? Wydajność jest sporo lepsza?

Co do przeliczania stanu gry co jakiś czas (te 2 minuty na przykład) to mi się to nie podoba ze względu na komfort gry. Użytkownik który czeka aż wróci jego wojsko, nie chce czekać dodatkowych dwóch minut na to aż stan gry się zaktualizuje na serwerze, dlatego wolałbym by eventy były procesowe na bieżąco. Jednakże na razie nie wiem jak zaimplementować fakt, by event wykonywał się w po określonym czasie - używać ***ScheduledExecutorService *** czy szukać czegoś w Reactorze? +

Font end - tak, właśnie w taki sposób to jest robione, a czas serwera to jest dobry pomysł.

0

Zrobiłem prosty prototyp z użyciem ThreadPoolTaskScheduler i z ConcurrentHashMapy, w testach wszystko działa bardzo dobrze. Zacznę implementować jakiś "rzeczywisty problem" i zobaczymy jak to pójdzie, w międzyczasie popatrzę się na Redisa (Jedisa), wydaje się być idealny do persystencji eventów.

Jeśli ktoś ma jakieś doświadczenia i chciałby się nimi podzielić to chętnie posłucham.

1

Możesz to co naklepałeś wystawić na githubie? Bo mnie zaciekawiło.

0

JDK 21 + Project Loom - słyszałeś?

0

@marian pazdzioch:

Proszę, kod pisany w javie 2,5 roku temu. Od tego czasu nie utrzymywany, za jakość nie odpowiadam ( ͡° ͜ʖ ͡°)

https://gitlab.com/warmaker/war-maker/-/blob/develop/src/main/java/com/graphzero/warmaker/application/events/schedulable/ScheduledEventsService.kt

EDIT: Wątek założony 2 lata przed napisaniem kodu więc w sumie nie wiem jak mi się wtedy założenia zmieniły w międzyczasie.

0

Marcin dobrze Ci radzi walisz jdk 21 i virtualne wątki używasz
1 mln wątków łyka na śniadanie

1

To był wątek założony 5 lat temu ale dzięki za radę, jak będę miał znowu ten problem to zastosuje xD

1 użytkowników online, w tym zalogowanych: 0, gości: 1