Pattern di attore in tessuto di servizio azzurro

4

Attualmente sto progettando un'applicazione e sono confuso su come suddividere la mia applicazione monolite. Dopo aver fatto qualche ricerca, gli attori del fabric di servizio sembrano essere la soluzione migliore per l'elaborazione semplice, ma sono confuso sull'approccio che dovrei adottare.

Come funziona la mia applicazione corrente nel mio monolite:

  1. Il messaggio IRC arriva da un utente attraverso un evento socket.
  2. Un processore analizza il messaggio e individua il comando (utilizzando MEF ) che dovrebbe in definitiva elaborare il messaggio. Ad esempio, se il comando fosse "! Help", individuerebbe il comando help e invierà il messaggio lì. Il processore cerca anche le impostazioni dell'utente (ad esempio, possono modificare le impostazioni! In a #, e il processore dovrebbe cercare "#help" invece di "! Help" e passa un enorme oggetto settings (come contesto fondamentalmente) al comando: utilizzo qualcosa di molto simile a ciò che JabbR fa qui .
  3. Se un comando non viene trovato (ex! asdf).:

    A. Il canale ha un set di comandi predefinito: il comando viene inviato a quello. (ad esempio! Asdf non è stato trovato, e hanno un aiuto come comando predefinito. Il messaggio è fondamentalmente elaborato come! help asdf).

    B. Il canale non ha un set predefinito: il messaggio viene ignorato.

  4. Il messaggio viene elaborato tramite quel comando.

Idealmente, vorrei che ogni comando avesse il proprio servizio (sono unità di lavoro separate). Alcuni pensieri su come potrei progettare questo tramite un microservizio:

  1. Il messaggio proviene da un utente e un canale tramite un argomento del bus di servizio.
  2. Un processore analizza il messaggio e spara un attore (con l'ID attore che è l'ID del canale associato a quel messaggio) che elaborerà il messaggio. Il processore dovrebbe gestire l'elenco di servizi associati a ciascun messaggio e questo processore dovrebbe essere aggiornato su ogni aggiunta successiva di un servizio di comando.
  3. Il comando attore gestirà internamente lo stato, il che significa che le impostazioni saranno interne a se stesso. Un comando "ignora lista", gestirà il proprio elenco di ignora e fornirà ad altri processi la possibilità di visualizzare l'elenco di ignora. (In questo caso, il processore potrebbe / userebbe l'elenco di ignora per vedere se un utente è ignorato).
  4. Alcuni attori di lunga durata diventerebbero servizi di stato e gestiranno le proprie impostazioni per ciascun canale che accederà a quel servizio.

Un'altra soluzione:

  1. Il messaggio arriva da un utente tramite un argomento del bus di servizio.
  2. Un processore analizza il messaggio e spara un attore (con l'ID attore che è l'ID utente associato a quel messaggio) che elaborerà il messaggio. Il processore dovrebbe gestire l'elenco di servizi associati a ciascun messaggio e questo processore dovrebbe essere aggiornato su ogni aggiunta successiva di un servizio di comando.
  3. L'attore chiamerà un utente attore per gestire e ricevere le impostazioni.
  4. Alcuni attori di lunga durata diventerebbero servizi di stato e gestiscono le proprie impostazioni per ogni utente che acceda a quel servizio.

Una terza soluzione:

  1. Il messaggio arriva da un utente tramite un argomento del bus di servizio.
  2. Un processore analizza il messaggio e inserisce un messaggio in una coda del bus di servizio. Utilizzando un filtro SQL, ogni comando sarebbe un servizio stateless che potrebbe quindi collegarsi alla coda ed elaborare i messaggi associati a quel comando. In questo caso, un comando predefinito non sarebbe possibile (O dovrei interrogare l'elenco degli abbonati in coda e ottenere un elenco di regole che stanno tutti ascoltando e confrontare ogni comando con quell'elenco. a tirare questa lista a intervalli regolari per assicurarmi che nuovi servizi vengano aggiunti a questa lista.)

Quindi, per riassumere, ho 2 problemi: dove gestire le impostazioni per un utente e come gestire il passaggio dei comandi a attori o servizi. Come devo procedere?

    
posta FrankerZ 13.04.2017 - 07:10
fonte

1 risposta

1

Penso che potresti complicarlo in qualche modo.

Pensiamo alle risposte della chat come all'interfaccia utente, ogni comando è la sua "pagina web", ma invece di renderizzare html, sta restituendo risposte IRC (e talvolta canzoni, ect).

Ora torniamo indietro e facciamo riferimento al pattern MVC, quasi sempre funziona bene per un'interfaccia utente e la tua "UI" non è una delle eccezioni a mio avviso. Pensa che ogni comando ha il proprio controller, in modo simile a come in un'app web tradizionale, ogni entità avrebbe il proprio controller.

Bus
 |->Dispatcher
        |-----> SongController
        |-----> HelpController
        |-----> PollController
        |-----> ect

Sospetto che alcuni dei problemi che potresti avere provengano dal tentativo di imporre una relazione tra i comandi, dove c'è molto poco.

Tutto ciò che fa il dispatcher sopra, è capire qual è il comando, quindi inoltrare la richiesta al controller appropriato.

Avrai un progetto separato per i tuoi modelli di dominio, con classi come Utente . Un terzo progetto conterrà le classi per il salvataggio / recupero dei dati.

Il Dispatcher può anche far sì che il messaggio provenga da un bus di servizio, se lo si desidera, tuttavia, potrebbe essere eccessivo, a seconda di come sono state implementate le cose

class MessageDispatcher
{
     public void HandleMessage()
     {
          User currentUser = UserRepository.GetUser(this.GetUsername());
          Channel channel = ChannelRepository.GetChannel(this.GetChannelId())
          string commandChar = currentUser.CommandCharSetting; //usually '!', but sometimes '#'
          string command = this.ParseCommand(commandChar);
          if (command == null) //Command not found
          { 
              command = channel.DefaultCommand;
          }

          if (command == "song")
          {
              SongController.HandleMessage(currentUser, requestData);
          }
          if (command == "help")
          {
              HelpController.HandleMessage(currentUser, requestData); //Note, the method signature here CAN be different, we don't want to try to force a relationship here, where none exists. Each controller is responsible for figuring out what to do with the command itself, it may or may not need a user, which we are only passing as an optimization
          }

Successivamente il controller si prenderà cura delle specifiche del comando, inclusa la ricerca dei dati necessari dai repository, o la gestione dei bus / cose asych come l'avvio di un'attività per inviare un brano all'indirizzo appropriato.

class HelpController
{
     public void HandleMessage(user, requestData)
     {
          HelpResponseModel model = new HelpResponseModel();
          model.NumberOfPeopleHelped = StatsRepo.GetTotalPeopleHelped();
          model.CommandChar = user.CommandCharSetting;
          StatsRepo.IncrementTotalPeopleHelped();

          new HelpView().Render(model);
          //The song controller would look up the song and send the file instead
     }
 }

class HelpView : IRCView
{
    public void Render(HelpModel model)
    {
        this.WriteLine("{0} have asked for help! :)", model.NumberOfPeopleHelped);
        this.WriteLine("{0}help - get help", model.CommandChar);
        this.WriteLine("{0}poll - get opinions, model.CommandChar);
        // ect
     }
}

Il tuo controller di brani sarà molto diverso:

class SongController
{
     public HandleMessage(user, requestData)
     {
          int currentSongId = this.GetCurrentlyPlayingSongId()

          ExecuteNewTask(() =>
          {
               Song song = SongRepo.GetSong(currentSongId);
               FTPSend(this.ParseOutAddress(requestData), Song.AudioData);
          });
          new SongView().Render(); //<---- Current Song Sent :)
      }
}

Ovviamente, questo codice esatto non funzionerà per te, ma spero che tu abbia l'idea, e non cercare di complicare troppo le cose. Questo potrebbe non aver risolto i problemi di prestazioni tanto quanto avresti desiderato, ma questo design renderà più facile misurare e sostituire i dettagli secondo necessità.

    
risposta data 20.04.2017 - 21:57
fonte

Leggi altre domande sui tag