使用停止策略失败后调用预启动

Prestart being called after failure with stopping strategy

我正在为远程服务上的参与者创建 retry/reconnect 功能。 actor 应该在启动前调用选择引用来订阅来自远程服务上的 actor 的消息。如果由于某种原因演员不在那里,我希望我的演员在报告连接失败之前重试几次。

我正在使用 TestKit 来测试功能。我 运行 遇到的问题是,当演员抛出超时异常时,即使我有停止策略,也会在之后调用预启动。

我是不是对actor的生命周期理解有误?如果策略是在失败后停止,那么调用 PreStart 似乎很奇怪。

public class MyActor : ReceiveActor
{
    private readonly ActorPath path;
    private ActorSelection selection;
    private int retry = 0;
    protected override void PreStart()
    {
        selection = Context.ActorSelection(path);
        selection.Tell("prestart");
        SetReceiveTimeout(TimeSpan.FromSeconds(1));
    }

    public MyActor(ActorPath path)
    {
        this.path = path;

        Receive<ReceiveTimeout>(timeout =>
        {
            if (retry < 2)
            {
                retry++;
                selection.Tell("retry");
                return;
            }

            throw new Exception("timeout");
        });
    }
}

public class Test : TestKit
{
    [Fact]
    public void FactMethodName()
    {
        var probe = CreateTestProbe();

        var props = Props.Create(() => new MyActor(probe.Ref.Path))
                                            .WithSupervisorStrategy(new OneForOneStrategy(exception => Directive.Stop));

        Sys.ActorOf(props);

        //Initial
        probe.ExpectMsg<string>(s => s=="prestart",TimeSpan.FromSeconds(2));
        //Retries
        probe.ExpectMsg<string>(s => s == "retry", TimeSpan.FromSeconds(2));
        probe.ExpectMsg<string>(s => s == "retry", TimeSpan.FromSeconds(2));
        //No more
        probe.ExpectNoMsg( TimeSpan.FromSeconds(10));
    }
}

Jeff 回答后我的解决方案

public class ParentActor : UntypedActor
{
    private readonly Func<IUntypedActorContext, IActorRef> creation;
    private IActorRef actorRef;

    public ParentActor(Func<IUntypedActorContext, IActorRef> creation)
    {
        this.creation = creation;
    }

    protected override void PreStart()
    {
        actorRef = creation(Context);
    }

    protected override void OnReceive(object message)
    {
        actorRef.Tell(message);
    }
}

public class MyActor : ReceiveActor
{
    private readonly ActorPath path;
    private int retry;
    private ActorSelection selection;

    public MyActor(ActorPath path)
    {
        this.path = path;

        Receive<ReceiveTimeout>(timeout =>
        {
            if (retry < 2)
            {
                retry++;
                selection.Tell("retry");
                return;
            }

            throw new Exception("timeout");
        });
    }

    protected override void PreStart()
    {
        selection = Context.ActorSelection(path);
        selection.Tell("prestart");
        SetReceiveTimeout(TimeSpan.FromSeconds(1));
    }
}

public class Test : TestKit
{
    [Fact]
    public void FactMethodName()
    {
        var probe = CreateTestProbe();

        var props = Props.Create(
            () => new ParentActor(context => context.ActorOf(Props.Create(
                () => new MyActor(probe.Ref.Path), null), "myactor")))
                         .WithSupervisorStrategy(new OneForOneStrategy(exception => Directive.Stop));

        Sys.ActorOf(props);

        //Initial
        probe.ExpectMsg<string>(s => s == "prestart", TimeSpan.FromSeconds(2));
        //Retries
        probe.ExpectMsg<string>(s => s == "retry", TimeSpan.FromSeconds(2));
        probe.ExpectMsg<string>(s => s == "retry", TimeSpan.FromSeconds(2));
        //No more
        probe.ExpectNoMsg(TimeSpan.FromSeconds(10));
    }
}

有效,但感觉它可能是 TestKit 的一部分,可以为测试的监护人设置主管策略

var props = Props.Create(() => new MyActor(probe.Ref.Path))
    .WithSupervisorStrategy(new OneForOneStrategy(exception => Directive.Stop));

这为 MyActor 设置了主管策略,它将用于 MyActor 的子级而不是 MyActor 本身。

Sys.ActorOf(props);

这是在默认情况下具有 OneForOne Restart 指令的 /user 监护人下创建 MyActor。这就是您的 actor 重新启动的原因。

要得到你想要的,你需要用自定义监管者策略创建一个父actor,并在其下创建MyActor