在停止策略失败后调用Prestart

时间:2016-03-02 14:07:11

标签: tdd akka.net

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

我正在使用TestKit来改进功能。我遇到的问题是,当actor抛出超时异常时,即使我有一个停止策略,也会在之后调用prestart。

我想知道演员的生命周期吗?如果战略要在失败后停止,似乎很奇怪调用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));
    }
}

杰夫回答后的解决方案

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的一部分,可以为测试的监护人设定监督策略

1 个答案:

答案 0 :(得分:1)

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

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

Sys.ActorOf(props);

这是在/user监护人下创建MyActor,默认情况下具有OneForOne Restart指令。这就是你的演员重新开始的原因。

要获得所需内容,您需要使用自定义主管策略创建父actor,并在其下创建MyActor