第二条消息在我的Akka.Net演员中变为Unhandled,然后它似乎停止了

时间:2017-07-17 23:10:50

标签: c# akka.net

免责声明:我是Akka的新手:)

我试图在Akka中实现路由器,基本上是

  1. 收到消息
  2. 在字典中查找处理消息类型的IActorRef
  3. 如果未找到匹配项,请使用Akka.DI作为子actor创建一个并添加到词典
  4. 向该演员发送消息
  5. 这很好 - 第一次,但如果我尝试Tell()或Ask()路由器两次,第二条消息总是在流中以未处理的方式结束

    我尝试在子actor中重写Unhandled()并在那里放置一个断点,实际上是在第二条消息上被击中。

    路由器:

    public class CommandRouter : UntypedActor
    {
        protected readonly IActorResolver _resolver;
        private static readonly Dictionary<Type, IActorRef> _routees = new Dictionary<Type, IActorRef>();
        private ILoggingAdapter _log = Context.GetLogger(new SerilogLogMessageFormatter());
    
        public CommandRouter(IActorResolver resolver)
        {
            _resolver = resolver;
        }
    
        protected override void OnReceive(object message)
        {
            _log.Info("Routing command {cmd}", message);
            var typeKey = message.GetType();
    
            if (!_routees.ContainsKey(typeKey))
            {
                var props = CreateActorProps(typeKey);
    
                if (!props.Any())
                {
                    Sender?.Tell(Response.WithException(
                        new RoutingException(
                            $"Could not route message to routee. No routees found for message type {typeKey.FullName}")));
                    return;
                }
    
                if (props.Count() > 1)
                {
                    Sender?.Tell(Response.WithException(
                        new RoutingException(
                            $"Multiple routees registered for message {typeKey.FullName}, which is not supported by this router. Did you want to publish stuff instead?")));
                    return;
                }
    
                var prop = props.First();
                var routee = Context.ActorOf(prop, prop.Type.Name);
                _routees.Add(typeKey, routee);
            }
    
            _routees[typeKey].Forward(message);
    
        }
    
        private IEnumerable<Props> CreateActorProps(Type messageType)
        {
            return _resolver.TryCreateActorProps(typeof(IHandleCommand<>).MakeGenericType(messageType)).ToList();
        }
    
        protected override SupervisorStrategy SupervisorStrategy()
        {
            return new OneForOneStrategy(x => Directive.Restart);
        }
    }
    

    ActorResolver方法,它使用来自Akka.DI.StructureMap的DependencyResolver:

    public IEnumerable<Props> TryCreateActorProps(Type actorType)
    {
        foreach (var type in _container.GetAllInstances(actorType))
        {
            yield return _resolver.Create(type.GetType());
        }
    }
    

    实际的儿童演员非常紧张:

    public class ProductSubscriptionHandler : ReceiveActor, IHandleCommand<AddProductSubscription>
    {
        public ProductSubscriptionHandler()
        {
            Receive<AddProductSubscription>(Handle);
        }
    
        protected bool Handle(AddProductSubscription command)
        {
            Sender?.Tell(Response.Empty);
            return true;
        }
    }
    

    在actor系统初始化之后调用整个事件,如下所示:

    var router = Sys.ActorOf(resolver.Create<CommandRouter>(), ActorNames.CommandRouter);
    
    router.Ask(new AddProductSubscription());
    router.Ask(new AddProductSubscription());
    

    我一直在第二个(或任何后续)消息上收到此错误:&#34;来自...的未处理消息&#34;:

    [INFO][17-07-2017 23:05:39][Thread 0003][[akka://pos-system/user/CommandRouter#676182398]] Routing command Commands.AddProductSubscription
    [DEBUG][17-07-2017 23:05:39][Thread 0003][akka://pos-system/user/CommandRouter] now supervising akka://pos-system/user/CommandRouter/ProductSubscriptionHandler
    [DEBUG][17-07-2017 23:05:39][Thread 0003][akka://pos-system/user/CommandRouter] *Unhandled message from akka://pos-system/temp/d* : Documents.Commands.AddProductSubscription
    [DEBUG][17-07-2017 23:05:39][Thread 0007][akka://pos-system/user/CommandRouter/ProductSubscriptionHandler] Started (Consumers.Service.Commands.ProductSubscriptionHandler)
    

1 个答案:

答案 0 :(得分:0)

所以,事实证明我的问题有一个更简单(和工作)的解决方案:只需在CommandRouter构造函数中注册并启动所有routee actor,而不是per-receive。

所以现在我的代码看起来也更简单了:

<强> CommandRouter:

public class CommandRouterActor : UntypedActor
{
    public Dictionary<Type, IActorRef> RoutingTable { get; }
    private ILoggingAdapter _log = Context.GetLogger(new SerilogLogMessageFormatter());

    public CommandRouterActor(IActorResolver resolver)
    {
        var props = resolver.CreateCommandHandlerProps();
        RoutingTable = props.ToDictionary(k => k.Item1, v => Context.ActorOf(v.Item2, $"CommandHandler-{v.Item1.Name}"));
    }

    protected override void OnReceive(object message)
    {
        _log.Info("Routing command {cmd}", message);
        var typeKey = message.GetType();

        if (!RoutingTable.ContainsKey(typeKey))
        {
                Sender?.Tell(Response.WithException(
                    new RoutingException(
                        $"Could not route message to routee. No routees found for message type {typeKey.FullName}")));

                _log.Info("Could not route command {cmd}, no routes found", message);
        }

        RoutingTable[typeKey].Forward(message);
    }

    protected override SupervisorStrategy SupervisorStrategy()
    {
        return new OneForOneStrategy(x => Directive.Restart);
    }
}

我的 ActorResolver (在上面的ctor中使用)只是IHandleCommand<>的{​​{3}}:

    public IEnumerable<Tuple<Type, Props>> CreateCommandHandlerProps()
    {
        var handlerTypes =
            _container.Model.AllInstances.Where(
                    i =>
                        i.PluginType.IsGenericType && i.PluginType.GetGenericTypeDefinition() ==
                        typeof(IHandleCommand<>))
                .Select(m => m.PluginType);

        foreach (var handler in handlerTypes)
        {
            yield return new Tuple<Type, Props>(handler.GenericTypeArguments.First(), _resolver.Create(handler));
        }
    }