2

我有一个包含 2 个命令行项目的解决方案,它创建了一个带有种子和客户端进程的 akka.net 集群。种子启动集群,然后实例化一个一致哈希集群路由器,该路由器对实现我的接口“IHasRouting”的任何消息执行哈希映射。因此,任何 IHasRouting 消息(来自种子或客户端)都应该以该消息的哈希值在 routee 上的种子处结束。

项目开始正常,集群形成没有错误。种子和客户端都实例化了一个路由器。来自种子和客户端的所有消息都具有相同的“VolumeId”,因此它们应该在种子处使用相同的路由。但是来自客户端节点的消息会为种子中的这些消息产生一个新的路由!

我对一致哈希集群路由器的理解是:

  • 一个表示它的 IActorRef 应该在该节点中的参与者打算向路由器发送消息的每个节点上退出。
  • 路由器的实现在每个节点上应该是相同的,并且具有相同的参与者名称。
  • 所有到路由器的消息都应该实现 IConsistentHash 或者路由器实例应该有一个“WithHashMapping()”
  • 具有相同哈希的所有消息将仅到达一个路由,并且始终是相同的路由
  • 一个路由可能需要多个哈希

我相信我了解一致哈希集群路由器的行为方式,并且许多 DEV 似乎都在正确使用路由器类型,所以我的实现一定是错误的......请帮助!如果有帮助,我可以提供完整的解决方案。

创建路由器的代码:

system.ActorOf(
   new ClusterRouterPool(
       local: new ConsistentHashingPool(nrOfInstances: 1)
          .WithHashMapping(m => (m as IHasRouting)?.Company?.VolumeId ?? throw new Exception("no routing!")),
                settings: new ClusterRouterPoolSettings(
                    100,
                    100,
                    allowLocalRoutees: allowLocalRoutees, //true if the node role is a Seed
                    useRole: "Seed"))
                    .Props(Props.Create(() => new CompanyDeliveryActor())), "company-router");

我有一个向路由器发送消息所需的“公司”类。此测试的所有 VolumeId 都相同。

public class Company
{
    public readonly Guid CompanyId;
    public readonly Guid VolumeId;
    public readonly string CompanyName;
    public Company(Guid companyId, Guid volumeId, string companyName)
    {
        this.CompanyId = companyId;
        this.VolumeId = volumeId;
        this.CompanyName = companyName;
    }
}

路由器映射使用的 IHasRouting 接口:

public interface IHasRouting
{
    Company Company { get; }
}

可以发送到路由器的示例消息类:

public class GetTripsMessage : IHasRouting
{
    public Company Company { get; private set; }
    public GetTripsMessage(Company company)
    {
        this.Company = company;
    }
}

最后是在路由器上为每个路由实例化的 CompanyDeliverActor:

public class CompanyDeliveryActor : ReceiveActor
{
    private readonly Dictionary<Guid, IActorRef> companyManagers = new Dictionary<Guid, IActorRef>();
    private readonly Guid instanceid = Guid.NewGuid();

    public CompanyDeliveryActor()
    {
        this.Receive<GetTripsMessage>(m => this.RouteCompanyMessage(m, m.Company));
        this.Receive<SetTripsMessage>(m => this.RouteCompanyMessage(m, m.Company));
    }

    private void RouteCompanyMessage(object m, Company company)
    {
        //placing a watch here shows that this.instanceid is different for messages from the client.

        if (!this.companyManagers.TryGetValue(company.CompanyId, out var manager))
        {
            manager = Context.ActorOf(Props.Create(() => new CompanyManagerActor()));
            this.companyManagers[company.CompanyId] = manager;                
        }

        manager.Tell(m, Context.Sender);
    }
}

感谢您的任何指导。

4

2 回答 2

0

当您打电话时ActorOf,您实际上是在当前集群节点上创建一个新的参与者实例。在池路由器的情况下,创建新路由器还将创建一个新的路由参与者池,可以在其他节点上分派。这也意味着,您调用ActorOf了两次(一次在客户端节点上,一次在种子节点上),结果您将收到两个单独的参与者池。

  1. 解决此问题的最简单方法是仅将路由器池实例化一次 - 让它跨集群节点调度路由池 - 并通过节点转发所有请求,节点作为集群的入口点。您可以将其他节点配置为故障转移以在入口节点失败时重新执行该任务。
  2. 另一件事是使用分布式 Pub/Sub集群分片,具体取决于您使用集群消息路由的目的。
于 2017-05-05T12:22:36.877 回答
0

经过调查,我同意我对 clustered-consistent-hash-router 的理解是错误的。我现在明白我需要将代表路由器的 IActorRef 传达给集群的其余部分。这可以通过 ClusterSinglton 或(可能)某种类型的分布式 pub sub 机制来完成。一种机制需要处理节点或 Actor 变得“丢失”的情况,一个新的 Actor(带有新的路由)被实例化,并且新的 Actor 被传达给集群中具有先前引用的所有那些。

我正在研究集群分片方法,并将发布另一个关于我在该方法中遇到的技术困难的问题。

于 2017-05-07T08:31:32.307 回答