akka :某个阶层的演员能成为另一个阶层的演员吗?

rvpgvaaj  于 2022-11-05  发布在  其他
关注(0)|答案(2)|浏览(144)

作为一个课程项目,我试图实现一个(模拟)的筏协议。在这篇文章中,我将不会使用筏术语在所有;相反,我将使用一个简化方法。
该协议由多个服务器运行(例如,5),可以处于三种不同的状态(A,B,C)。服务器从“基本”类型继承了一些状态变量和行为,但它们也都有许多独特的状态变量和方法,并响应不同的消息。在协议的某个点上,处于某种状态的服务器(例如,A)才能变成另一个状态(例如,B)。换句话说,服务器应该:
1.丢失状态A的状态变量和方法,获取状态B的变量和方法,但保留“基本”类型的变量。
1.停止响应发往状态A的消息,开始响应发往状态B的消息。
在Akka中,点1可以使用Receives实现,并变成()。
第2点是需要的,因为,例如,类B的参与者不应该访问类A的参与者的状态变量和方法。这旨在分离关注点,并实现更好的代码组织。
我在实施第2点时面临的问题如下:

  • 现在,我的实现只有一个参与者,它包含A和B状态变量和方法。
  • 我尝试实现的协议要求每个服务器都必须保持对其他服务器的引用(即其他服务器的ActorRef)。
  • 我不能简单地在状态B中生成一个参与者,将“基本”类型的状态变量的值传递给它,然后停止旧的参与者,因为新生成的参与者有一个新的ActorRef,而其他服务器对此一无所知,它们将继续使用旧的ActorRef发送消息(因此,新的参与者将不会收到任何东西,双方都超时)。

规避该问题的一种方式是,新产生的行动者通过向包括其旧的ActorRef的其他行动者发送消息来“通告”其自身。然而,再次由于协议,其他服务器可能暂时不可用(即,它们崩溃),因此它们可能不接收和处理通告。
在这个项目中,我必须使用AbstractActor的扩展,而不是FSM(最终状态机),并且必须使用Java。
是否有任何Akka模式或功能可以解决这个用例?感谢您的见解。下面是一个简化的示例。

public abstract class BaseActor extends AbstractActor {
    protected int x = 0;
    // some state variables and methods that make sense for both A and B

    @Override
    public Receive createReceive() {
        return new ReceiveBuilder()
                .matchEquals("x", msg -> {
                    System.out.println(x);
                    x++;
                })
                .build();
    }
}

public class A extends BaseActor {
    protected int a = 10;
    // many other state variables and methods that are own of A and do NOT make sense to B

    @Override
    public Receive createReceive() {
        return new ReceiveBuilder()
                .matchEquals("a", msg -> {
                    System.out.println(a);
                })
                .matchEquals("change", msg -> {
                    // here I want A to become B, but maintain value of x
                })
                .build()
                .orElse(super.createReceive());
    }
}

public class B extends BaseActor {
    protected int b = 20;
    // many other state variables and methods that are own of B and do NOT make sense to A

    @Override
    public AbstractActor.Receive createReceive() {
        return new ReceiveBuilder()
                .matchEquals("b", msg -> {
                    System.out.println(b);
                })
                .matchEquals("change", msg -> {
                    // here I want B to become A, but maintain value of x
                })
                .build()
                .orElse(super.createReceive());
    }
}

public class Example {
    public static void main(String[] args) {
        var system = ActorSystem.create("example");

        // actor has class A
        var actor = system.actorOf(Props.create(A.class));
        actor.tell("x", ActorRef.noSender()); // prints "0"
        actor.tell("a", ActorRef.noSender()); // prints "10"

        // here, the actor should become of class B,
        // preserving the value of x, a variable of the "base" kind
        actor.tell("change", ActorRef.noSender());

        // actor has class B
        actor.tell("x", ActorRef.noSender()); // should print "1"
        actor.tell("b", ActorRef.noSender()); // should print "20"
    }
}
58wvjzkj

58wvjzkj1#

这是一个实现的草图。
1.您可以将每个状态建模为单独的类:
第一个
1.然后,在Actor中,您可以为A和B定义接收函数,并将其初始化为A或B,具体取决于哪一个是初始值

private static class MyActor extends AbstractActor
  {
    private AbstractActor.Receive receive4StateA(StateA stateA)
    {
      return new ReceiveBuilder()
        .matchEquals("a", msg -> stateA.setSomeProperty(msg))
        .matchEquals("changeToB", msg -> getContext().become(
          receive4StateB(StateB.fromStateA(stateA))))
        .build();
    }

    private AbstractActor.Receive receive4StateB(StateB stateB)
    {
      return new ReceiveBuilder()
        .matchEquals("b", msg -> stateB.setSomeProperty(msg))
        .matchEquals("changeToA", msg -> getContext().become(
          receive4StateA(StateA.fromStateB(stateB))))
        .build();
    }

    //assuming stateA is the initial state
    @Override
    public AbstractActor.Receive createReceive()
    {
      return receive4StateA(StateA.fromBase(new BaseState()));
    }
  }
ruarlubt

ruarlubt2#

诚然,我的Java是生疏的,但举个例子,这个演员(或者非常类似的东西......)将接收字符串,直到它接收到一个Lock消息,之后可以查询它在被锁定之前接收到了多少个不同的字符串。它跟踪接收到的字符串的Set以执行重复数据删除。在Lock上,它转换到不包含Set的第二个Receive(仅Integer字段),并忽略StringLock消息。

import akka.japi.JavaPartialFunction;
import java.util.HashSet;
import scala.runtime.BoxedUnit;

public class StringCounter extends AbstractActor {
  public StringCounter() {}

  public static class Lock {
    private Lock() {}
    public static final Lock INSTANCE = new Lock();
  }

  public static class Query {
    private Query() {}
    public static final Query INSTANCE = new Query();
  }

  /**The taking in Strings state */
  public class AcceptingStrings extends JavaPartialFunction<Object, BoxedUnit> {
    private HashSet<String> strings;

    public AcceptingStrings() {
      strings = new HashSet<String>();
    }

    public BoxedUnit apply(Object msg, boolean isCheck) {
      if (msg instanceof String) {
        if (!isCheck) {
          strings.add(msg);
        }
      } else if (msg instanceof Lock) {
        if (!isCheck) {
          context().become(new Queryable(strings.size()), true);
        }
      } else {
        // not handling any other message
        throw noMatch();
      }

      return BoxedUnit.UNIT;
    }
  }

  /**The responding to queries state */
  public class Queryable extends JavaPartialFunction<Object, BoxedUnit> {
    private Integer ans;

    public Queryable(int answer) {
      ans = Integer.valueOf(answer);
    }

    public BoxedUnit apply(Object msg, boolean isCheck) {
      if (msg instanceof Query) {
        if (!isCheck) {
          getSender().tell(ans, getSelf());
        }
      } else {
        // not handling any other message
        throw noMatch();
      }

      return BoxedUnit.UNIT;
    }
  }

  @Override
  public Receive createReceive() {
    return new Receive(new AcceptingStrings());
  }
}

注意,在Queryable中,set已经消失了。需要注意的一点是,JavaPartialFunction通常会在isCheck设置为true时调用apply一次,如果该调用没有抛出noMatch()返回的异常,它将被再次“for真实的”调用,并且isCheck被设置为false。因此,您需要注意,在isCheck为true的情况下,除了throw noMatch()或返回之外,不要执行任何操作。
这种模式与Akka Typed(尤其是函数API)中的情况非常相似。
希望这能阐明这种方法,当然,你的老师也有可能不接受这一点,尽管在这种情况下,可能值得用下面的论点来反驳:

  • 在参与者模型中,状态和行为实际上是同一件事
  • 所有功能 * 都 * 包含在AbstractActor

我也不一定推荐在JavaAkka代码中使用这种方法(字段中带有state的AbstractActor更像是Java-y)。

相关问题