Actor model of communication mode

In the Actor model has one and only one mode of communication between all of the Actor, and that is tell the way, is fire and forget approach. But in the actual development process engineers we have evolved a number of commonly used mode of communication. Paper akka-typed (2.6.0-M8) frame an example, in the presence of the basic communication mode of some actor model to discuss how messages are flowing between the actor.

Fire and Forget

We first saw the most basic mode that is sent fire and forget forgotten. This model is intuitively try to send a message to the actor a stop after the communication concerned. Here are sending a message is sent only to ensure that fire, that message is issued, but may be lost due to network reasons. This communication mode uses the so-called at most once delivery semantics.

Before giving an example of the code can be run, we briefly explain the specific implementation akka-typed framework of the actor model. In akka-typed in by transmitting a message ActorRef<T>to a reference, ActorReffor a reference position of the actor's transparent, Tshowing the actor message types can be received. This is somewhat different from the typical actor model. This is because the typical non-deterministic actor model, as implemented as akka untyped frame, i.e., any actor can accept any type of information, and thus by become/ unbecometo change their behavior (behavior). In akka-typed by reference with the type of label, guaranteed to be called actorRef.tell(message)when the unexpected does not send the wrong message, and the type of information helps inference code, rather than a bunch of any reference in any type of ActorRef and type Object analysis of message laborious. One disadvantage is the inability to achieve any correspondence flexibility of become/ unbecomeif needed to achieve a similar function state machines, you need to add additional fields and Behaviorsdiscussed separately for the state, but all can accept messages must be Tof type.

import akka.actor.typed.ActorRef;
import akka.actor.typed.ActorSystem;
import akka.actor.typed.Behavior;
import akka.actor.typed.javadsl.Behaviors;
import java.util.concurrent.CountDownLatch;

public class FireAndForget {
    public static final CountDownLatch LATCH = new CountDownLatch(2);

    public static class PrintMe {
        public final String message;
        public PrintMe(String message) {
            this.message = message;
        }
    }

    public static final Behavior<PrintMe> printerBehavior =
        Behaviors.receive((ctx, printMe) -> {
           ctx.getLog().info(printMe.message);
           LATCH.countDown();
           return Behaviors.same();
        });

    public static void main(String[] args) throws Exception {
        ActorSystem<Void> system = ActorSystem.create(Behaviors.setup(ctx -> {
            ActorRef<PrintMe> printer = ctx.spawn(printerBehavior, "printer");
            printer.tell(new PrintMe("message 1"));
            printer.tell(new PrintMe("message 2"));
            return Behaviors.ignore();
        }), "fire-and-forget");

        LATCH.await();

        system.terminate();
    }
}

It should be used CountDownLatchto synchronize ensure the message is delivered and then turn off ActorSystem, or because the execution order is not guaranteed, most likely due to the printing log before seeing ActorSystemoff printer actor is stopped, so that follow-up is scheduled delivery failure messages. This you can see fire and forget the only way to ensure that tellthe point of the call issued a message, as the message can not be delivered, it will be how to deal with the sender on the matter.

Request-Response

Request - response mode is another very common mode of communication, i.e., actor-1 sends a message to the actor-2, actor-2 after receiving the message with a corresponding message to the actor-1. Note that we only mention sending a message, both directions is fire and forget, and any other information a message is received by actor-1 actor-2 is received it is saying there is no difference. Unless the actor-2 sends back a response message carries with relevant information, otherwise the actor-1 does not know this message is a response to a message just sent. In addition, the specialized akka-typed in, or even longer since the message sender information includes natural, actor-1 does not know this message is sent, actor-2, must be explicitly contain all the required information in the message .

Since the request and response are loosely coupled, and this mode uses we ask pattern different be discussed below, can not be projected as object-oriented programming model actor.

import akka.actor.typed.ActorRef;
import akka.actor.typed.ActorSystem;
import akka.actor.typed.Behavior;
import akka.actor.typed.Terminated;
import akka.actor.typed.javadsl.Behaviors;
import scala.runtime.Nothing$;

@SuppressWarnings("WeakerAccess")
public class RequestResponseTypedActors {

    public static final class Request {
        public final String message;
        public final ActorRef<Response> replyTo;

        public Request(String message, ActorRef<Response> replyTo) {
            this.message = message;
            this.replyTo = replyTo;
        }
    }

    public static final class Response {
        public final String message;

        public Response(String message) {
            this.message = message;
        }
    }

    private static final Behavior<Request> responder = Behaviors
            .receiveMessage(request -> {
                System.out.println("got request: " + request.message);
                request.replyTo.tell(new Response("got it!"));
                return Behaviors.same();
            });

    private static Behavior<Response> requester(ActorRef<Request> responder) {
        return Behaviors
                .setup(ctx -> {
                    responder.tell(new Request("hello", ctx.getSelf()));
                    return Behaviors.receiveMessage(response -> {
                        System.out.println("got message " + response.message);
                        return Behaviors.stopped();
                    });
                });
    }

    public static void main(String[] args) {
        ActorSystem.create(Behaviors.setup(ctx -> {
            ActorRef<Request> res = ctx.spawn(responder, "responder");
            ActorRef<Response> req = ctx.spawn(requester(res), "requester");
            ctx.watch(req);

            return Behaviors.receiveSignal((context, sig) -> {
                if (sig instanceof Terminated) {
                    return Behaviors.stopped();
                } else {
                    return Behaviors.unhandled();
                }
            });
        }), "ReqResTyped");
    }
}

We can see, responder requester received message contains the address of the requester, so you can send a return message. The reason why the requester know responder address by the developer to tell it directly in the creation of the requester. Obviously such a question is requester is how innately know the address of the responder of it? In a distributed system, which can have a fixed address or name service by pre to achieve this objective. In akka-typed by ActorSelectionusing the actor address addressing mode has been hard to do (unless you use untyped forcibly acquiring sub back, very complex and hacky), mainly through the Receptionistname of the service to achieve. This mode requires service in the cluster must use the akka-cluster-typed, it is quite bloated.

Query

Query mode mentioned here, i.e. a so-called ask mode, i.e. after sending a message is waiting on a response to the bind message. Due to network lag and uncertainty, waiting for blocking this response will not only cause performance problems, are more likely to target machine downtime due to missing or respond to messages so that the current system is not available. To do this, we need to decouple the request and response process, the type of the return from the local value of the result becomes a value directly wrapped in the Future. Then, the requesting party to improve business processes Future domain transform using a combination of word combinations to the subsequent step. Since Future box returns immediately, the current thread can continue with other work, performed according to respective logical combination of the previous step after Future values ​​are filled. Here Incidentally, an example of Future also known functional programming concepts Monad enhance the above and combinations of such fields is a common way to deal with functional programming state change.

Since the return of a Future, we need to discuss conditions of the Future done carefully. First, the abstract, the Future will be completed in time to receive a reply. But the reply of the request if it is between two parties to complete the business actor, then you need to have a business actor table to cache all of the Future, and in both the request and reply with Future's unique identifier to reply received when auto-completion Future. Because Query mode is not inherently associated with the actor in the model, but a summary of the latter design patterns, doing so will cause all actor have had to bear the unnecessary overhead.

Thus in the method which uses akka, a request for a reply cycle, using a dedicated PromiseActorresponsible interaction actor and a distal end, i.e. the request message PromiseActorsent, also receives a reply from the PromiseActorFuture wherein only dedicated to complete. In akka-typed, the additionally added createRequestand the applyToResponseparameters for the former PromiseActorreference an incoming message sent, the message type which is adapted to accept an upper actor after receiving a reply.

import akka.actor.typed.ActorRef;
import akka.actor.typed.ActorSystem;
import akka.actor.typed.Behavior;
import akka.actor.typed.javadsl.AskPattern;
import akka.actor.typed.javadsl.Behaviors;
import java.time.Duration;
import java.util.concurrent.CompletableFuture;

public class Ask {
    public static class Pong { }
    public static class Ping {
        public final ActorRef<Pong> replyTo;
        public Ping(ActorRef<Pong> replyTo) {
            this.replyTo = replyTo;
        }
    }

    public static final Behavior<Ping> pongBehavior =
        Behaviors.receiveMessage(ping -> {
           ping.replyTo.tell(new Pong());
           return Behaviors.stopped();
        });

    public static void main(String[] args) {
        ActorSystem system = ActorSystem.create(Behaviors.setup(ctx -> {
            ActorRef<Ping> ref = ctx.spawn(pongBehavior, "pong");
            CompletableFuture<Pong> future = AskPattern.ask(
                ref,
                Ping::new,
                Duration.ofMillis(2000L),
                ctx.getSystem().scheduler()).toCompletableFuture();
            future.whenComplete((pong, throwable) -> {
                ctx.getLog().info("pong={}, throwable={}", pong, throwable);
                ctx.getSystem().terminate();
            });
            return Behaviors.ignore();
        }), "ask");
    }
}

Ask a little bit mode on a particular note, that if you use AskSupport in the actor ask, ask this fact there will be a spawn out PromiseActor. It has already been mentioned above, but definitely worth say it again. Because when you use and actor-2 AskSupport communication applications ask mode in actor-1, the actual communication is PromiseActor. Therefore, ordering on messages between the two actor akka provided for this third actor PromiseActor is not established. This may lead to very subtle concurrency competition.

Pipe 和 Aggregate

Finally, a brief mention of two message flow patterns Pipe and Aggregate, with two modes often ask mode used.

Pipe Future a transmission mode upon completion of the Actor to another, the basic form pipe(future, replyTo)is often used to actor-1 actor 2-requests, requests to further actor-2 actor-3, then the actor-2 will be in actor-3 request future pipe actor-1 to the reply. For example, actor-1 is a client, actor-2 is a database front-end, actor-3 backend database.

Aggregate mode can be seen as a natural extension Ask mode. Ask mode can only process one request and its response, and the simple extension mode Aggregate sub-actor to create an explicit request in response to a plurality of process, very intuitive.

Guess you like

Origin www.cnblogs.com/tisonkun/p/11622304.html