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();
}
}
}
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? –
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. –
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. –