2017-07-27 2 views
0

J'ai besoin de conseils pour utiliser une machine à ressort pour des processus de longue durée. Je veux concevoir un flux. Disons que j'ai les prochains états: Démarrer-> step1-> step2-> step3-> finish. J'ai un contrôleur qui peut envoyer des événements à la machine d'état pour gérer les transitions entre les états. J'ai un StateMachinePersister. J'ai un convertisseur de StateMachineContext à byte [] et retour. Ça semble bon pour mon objectif d'affaires. Donc tout devrait bien fonctionner.machine d'état à ressort - gérer des processus de longue durée

Mais j'ai un problème? Je ne peux pas comprendre comment gérer les cas quand je vais décider de changer le flux. Je veux dire que si j'ai un environnement de production où certains processus ont persisté dans l'état "step2". Mais je suis obligé de changer le flux. disons que je veux ajouter une étape ou supprimer une étape dans le flux. Je pense que j'aurai des problèmes lors de la désérialisation de la machine d'état.

Donc la question est: peut-être la machine d'état de ressort ne me convient pas, ou il y a quelques recettes comment je peux gérer de tels cas?


J'ai une entité que je veux gérer les états, transitions, etc.

@Entity 
@Access(AccessType.FIELD) 
@Table(name = "processes", indexes = @Index(columnList = "currentState")) 
public class Process extends AbstractPersistable<Long> implements ContextEntity<ProcessState, ProcessEvent, Long> { // NOSONAR 

    private static final long serialVersionUID = 8848887579564649636L; 

    @JsonIgnore 
    StateMachineContext<ProcessState, ProcessEvent> stateMachineContext; // NOSONAR 

    @Enumerated(EnumType.STRING) 
    ProcessState currentState; 


    @Override 
    public void setStateMachineContext(StateMachineContext<ProcessState, ProcessEvent> stateMachineContext) { 
     if (stateMachineContext == null) { 
      throw new IllegalStateException("stateMachineContext can't be null"); 
     } 
     this.currentState = stateMachineContext.getState(); 
     this.stateMachineContext = stateMachineContext; 
    } 


    @Override 
    public StateMachineContext<ProcessState, ProcessEvent> getStateMachineContext() { 
     return stateMachineContext; 
    } 

... 
} 

Je haricot StateMachinePersist qui est responsable de l'initialisation stateMachineContext pour le processus particulier.

@Bean StateMachinePersist publique> persistent() { return new StateMachinePersist>() {

@Override 
    public StateMachineContext<ProcessState, ProcessEvent> read(
      ContextEntity<ProcessState, ProcessEvent, Serializable> process) throws Exception { 
     return process.getStateMachineContext(); 
    } 

    @Override 
    public void write(StateMachineContext<ProcessState, ProcessEvent> context, 
      ContextEntity<ProcessState, ProcessEvent, Serializable> process) throws Exception { 
     process.setStateMachineContext(context); 
    } 
}; 

}

Je StateMachineAdapter qui est responsable de la persistance et la restauration de la machine d'état

public class DefaultStateMachineAdapter<S, E, T> { 

    final StateMachineFactory<S, E> stateMachineFactory; 

    final StateMachinePersister<S, E, T> persister; 

    public DefaultStateMachineAdapter(StateMachineFactory<S, E> stateMachineFactory, StateMachinePersister<S, E, T> persister) { 
     this.stateMachineFactory = stateMachineFactory; 
     this.persister = persister; 
    } 

    public StateMachine<S, E> restore(T contextObject) throws Exception { 
     StateMachine<S, E> stateMachine = stateMachineFactory.getStateMachine(); 
     return persister.restore(stateMachine, contextObject); 
    } 

    public void persist(StateMachine<S, E> stateMachine, T order) throws Exception { 
     persister.persist(stateMachine, order); 
    } 

    public StateMachine<S, E> create() { 
     StateMachine<S, E> stateMachine = stateMachineFactory.getStateMachine(); 
     stateMachine.start(); 
     return stateMachine; 
    } 

} 

J'ai StateMachineContextConverter qui est responsable de la sérialisation/deseriali zation de StateMachineContext. J'ai utilisé Kryo pour ces opérations.

public class StateMachineContextConverter implements AttributeConverter<StateMachineContext, byte[]> { 

    @Override 
    public byte[] convertToDatabaseColumn(StateMachineContext attribute) { 
     return serialize(attribute); 
    } 

    @Override 
    public StateMachineContext convertToEntityAttribute(byte[] dbData) { 
     return deserialize(dbData); 
    } 


} 

Je contrôleur qui est responsable de changer d'état

public class ProcessEventController { 


    final DefaultStateMachineAdapter<ProcessState, ProcessEvent, ContextEntity<ProcessState, ProcessEvent, ? extends Serializable>> processStateMachineAdapter; 

    public ProcessEventController(DefaultStateMachineAdapter<ProcessState, ProcessEvent, ContextEntity<ProcessState, ProcessEvent, ? extends Serializable>> processStateMachineAdapter) { 
     this.processStateMachineAdapter = processStateMachineAdapter; 
    } 

    @RequestMapping(path = "/processes/{id}/{event}", method = RequestMethod.POST) 
    @Transactional 
    public HttpEntity<Void> receiveEvent(@PathVariable("id") Process process, @PathVariable("event") ProcessEvent event) throws Exception { 
     StateMachine<ProcessState, ProcessEvent> stateMachine = processStateMachineAdapter.restore(process); 
     if (stateMachine.sendEvent(event)) { 
      processStateMachineAdapter.persist(stateMachine, process); 
      return ResponseEntity.accepted().build(); 
     } else { 
      return ResponseEntity.unprocessableEntity().build(); 
     } 
    } 
} 

Répondre

0

Il est évident que vous ne pouvez pas modifier la machine existante en cours d'exécution, mais que vous travaillez déjà avec la persistance Je suppose que vous êtes au moins des machines d'arrêt .

Regardez statemachine-examples-datajpa qui utilise une machine existante pour stocker la configuration dans un DB. Nous avons StateMachineModelFactory qui vous permet de stocker vos affaires n'importe où, si une implémentation intégrée ne vous convient pas. Cet exemple créera de nouvelles instances de machine chaque fois que de nouveaux événements seront envoyés. En d'autres termes, vous pouvez accéder à une base de données à l'aide de l'éditeur intégré et ajouter de nouvelles transitions sans redémarrer un processus Java principal. Cela vous donne une certaine flexibilité, mais si vous avez besoin de changer votre code de fonctionnement alors les choses deviendront impossibles.

+0

Merci pour votre réponse. Je veux ajouter quelques détails de ma mise en œuvre. Et demande encore. J'ai lu une fois de plus la documentation et j'ai compris que ce n'est pas vraiment une bonne façon de créer une machine d'état à partir de l'usine à chaque fois. Donc, la première chose que je veux retravailler est DefaultStateMachineAdapter. Il est préférable de récupérer la machine d'état du pool et de la réinitialiser à un état particulier. Correct? –

+0

La deuxième chose, qui est plus importante pour moi que l'overhead lors de l'instanciation de la machine d'état, est les changements futurs dans l'état et les transitions. Supposons que je veux gérer le processus avec les états et les événements suivants: step1 - event1 -> step2 - event2 -> step3. Mon exigence est que chaque utilisateur de mon application peut exécuter plusieurs processus. Par exemple, user1 fait une demande pour mon contrôleur et exécute process1 dans state2. –

+0

Supposons que l'application exécute une tâche de longue durée lorsque state2 a été activé. Lorsque cette tâche sera terminée, l'application devrait changer de state2 en state3. Supposons qu'à ce moment nous voulons changer la définition du processus. Je veux dire que je devrais retravailler step1 - event1 - step2 -> event2 -> step3 sur step1 - event1 -> step2 -> event2 -> newstep-newevent-> step3 Donc après avoir redéployé mon application vouloir restaurer mon état et passer par une nouvelle définition. Comme je l'ai mentionné plus tôt, j'ai échoué ici. –