6. Akka state switching

In Akka, actors are often in a state where they cannot process certain messages. For example, if the database client is offline, it will not be able to process any messages until it is back online. We can choose to continue to re-establish the client connection until the connection is successful. In this case, the client will discard all received messages before successfully connecting. Another approach is to put aside messages that the client cannot process, and wait for the client to restore the connection state before processing it.

Akka provides a mechanism called stash , which is used to switch back and forth between message states in Akka:

  • stash: Temporarily store messages in an independent queue that stores messages that cannot be processed currently:
  • unstash: take the message out of the temporary storage queue and put it back in the mailbox queue, the actor can continue to process these messages

It should be noted that although stash() and unstash() are very convenient to use when you want to quickly change the state, the state of the stash message must be bound to a certain time limit, otherwise the mailbox may fill up. You can schedule the execution of this message in the actor's constructor or preStart method:

System.scheduler().scheduleOnce(
Duration.create(1000,TimeUnit.MILLISECONDS),self(),CheckConnected,system.dispacher(),null);

Conditional statements

The most intuitive way is to store the state in the Actor, and then use a conditional statement to determine what the Actor should do.

public PartialFunction receive() {
    
    
    return RecieveBuilder
            .match(GetRequest.class, x -> {
    
    
                if (online) {
    
    
                    processMessage(x);
                } else {
    
    
                    stash();
                }
            })
            .match(Tcp.Connected.class, x -> {
    
    
                online = true;
                unstash();
            )
            .match(Disconnected.class, x -> online = false).build();
    }
}

Stash/unstash is used, so once the Actor is online, all messages of stash will be processed.

Many times, Actors store state, and then have different behaviors based on the value of this state. Using conditional statements is a very procedural method for handling behavior and state.

Hotswap: Become/Unbecome

  • become(PartialFunction behavior): This method modifies the behavior defined in the receive block to a new PartialFunction.
  • unbecome(): This method changes the behavior of the Actor back to the default behavior.
public PartialFunction receive() {
    
    
    return RecieveBuilder
            .match(
                GetRequest.class, x -> stash())
            .match(Connected.class, x -> {
    
    
                context().become(online); // become
                unstash();
            }).build();
}

final private PartialFunction<Object, BoxedUnit> online(final ActorRef another) {
    
    
    return RecieveBuilder.match(
            GetRequest.class, x -> processMessage(x)
    ).build();
}

Compared with conditional statements, the behavior of each state is defined in its own independent PartialFunction.

The actor is offline at first, and it cannot respond to GetRequest at this time, so it stashes the message. Before receiving the Connected message, these messages will be temporarily stored and put aside. Once the Connected message is received, the actor calls become to change the status to online. At this time, the Actor will also call unstash to retrieve all temporary messages back to the work queue. In this way, you can use the behavior defined in the online status method to process all messages. If the Actor receives a Disconnected message, unbecome is called to restore the Actor's behavior to the default settings.

Finite State Machine (FSM)

From "Akka Introduction to Practice", the code is for reference only. Pay attention to the public account : Data PorterReply to Akkareceive the book "Akka Introduction and Practice"

There are also state and state-based behavior changes in FSM. Compared with hot swapping, FSM is a heavier abstract concept that requires more codes and types to be able to implement and run. So generally speaking, heat exchange is a simpler and more readable option.

The type in FSM has two parameters: state and container.

Define the state

  • Disconnected: offline, there is no message in the queue;
  • Disconnected and Pending: Offline, the queue contains messages;
  • Connected: online, no messages in the queue;
  • Connected and Pending: online, the queue contains messages
enum State{
    
      
	DISCONNECTED,   
	CONNECTED,   
	CONNECTED_AND_PENDING,  
}

Define the state container

The state container is where the messages are stored. FSM allows us to define the state container and modify the state container when switching states.

public class EventQueue extends LinkedList<Request> {}

  1. Inherit akka.actor.AbstractFSM<S, D>

    public class BunchingAkkademyClient extends AbstractFSM<State, RequestQueue>{
          
              {
          
          //init block  } } 
    
  2. Define the behavior in the initialization block

    {
          
            
    	startWith(DISCONNECTED, null); //init block 
    }  
    
  3. Define how different states respond to different messages and how to switch states based on the received message.

    when(DISCONNECTED,
         matchEvent(FlushMsg.class, (msg, container) -> stay())
            .event(GetRequest.class, (msg, container) -> {
          
          
                container.add(msg);
                return stay();
            })
            .event(Tcp.Connected.class, (msg, container) -> {
          
          
                if(container.getFirst() == null) {
          
          
                    return goTo(CONNECTED);
                } else {
          
          
                    return goTo(CONNECTED_AND_PENDING);
                }
            }));
    when(CONNECTED,
         matchEvent(FlushMsg.class, (msg, container) -> stay()) {
          
          
            .event(GetRequest.class, (msg, container) -> {
          
          
                container.add(msg);
                return goTo(CONNECTED_AND_PENDING);
        }));
    when(CONNECTED_AND_PENDING,
         matchEvent(FlushMsg.class, (msg, container) -> {
          
          
                container = new EventQueue();
                return stay();
         })
         .event(GetRequest.class, (msg, container) -> {
          
          
                container.add(msg);
                return goTo(CONNECTED_AND_PENDING);
         }));
    
    scala.PartialFunction pf = ReceiveBuilder.match(String.class,
                x -> System.out.println(x)).build(); 
    when(CONNECTED, pf);
    

    Some messages are ignored in some states, and they are processed in other states. The actor must return a description of the state, either staying in a certain state in the FSM or transitioning to another state.

  4. initialize()

    The last thing to do in this code block is to call initialize();

references

  • "Akka Introduction and Practice"

Follow the public Akkaaccount and reply to receive the book "Akka Introduction and Practice"

Pay attention to the official account, 数据工匠记and focus on the offline and real-time technical dry goods in the big data field to share regularly! Personal website www.lllpan.top
Insert picture description here

Guess you like

Origin blog.csdn.net/lp284558195/article/details/112596242