Кластерный непротиворечивый хеш-пул, создающий новый маршрут для того же отображения
У меня есть решение с 2 проектами командной строки, которое создает кластер akka.net с начальным и клиентским процессами. Начальное число запускает кластер, а затем создает экземпляр-hash-cluster-router, который выполняет отображение хеша для любого сообщения, которое реализует мой интерфейс "IHasRouting". Таким образом, любое сообщение IHasRouting (от семени или клиента) должно заканчиваться на семени в маршрутизаторе для хэша этого сообщения.
Проекты запускаются нормально и кластер создается без ошибок. И seed, и клиент создают экземпляр маршрутизатора. Все сообщения из семени и клиента имеют одинаковый "VolumeId", поэтому они должны идти по тому же маршруту в семени. НО Сообщения от клиентского узла приводят к новому маршруту для этих сообщений в начальном порядке!
Мое понимание согласованного хеш-кластера-маршрутизатора таково:
- IActorRef, представляющий его, должен завершиться на каждом узле, где участники этого узла намереваются отправлять сообщения на маршрутизатор.
- Внедрение маршрутизатора должно быть идентичным на каждом узле и иметь одинаковое имя актера.
- Все сообщения к маршрутизатору должны реализовывать IConsistentHash или экземпляр маршрутизатора должен иметь "WithHashMapping()"
- Все сообщения с одним и тем же хешем будут поступать только на один адрес, и он всегда будет одним и тем же.
- Рут может занять более одного хэша
Я полагаю, что понимаю, как должен вести себя кластер-маршрутизатор с последовательным хешем, и многие разработчики, по-видимому, правильно используют тип маршрутизатора, поэтому моя реализация должна быть неправильной... Пожалуйста, помогите! Я могу предоставить полное решение, если это поможет.
Код, который создает маршрутизатор:
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");
У меня есть класс "Компания", который требуется для сообщений на маршрутизатор. Все VolumeIds одинаковы для этого теста.
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);
}
}
Спасибо за любое руководство.
2 ответа
Когда ты звонишь ActorOf
вы на самом деле создаете новый экземпляр субъекта на текущем узле кластера. В случае маршрутизаторов пула, создание нового маршрутизатора также создаст новый пул участников-маршрутизаторов, которые могут быть отправлены через другие узлы. Это также означает, что вы звоните ActorOf
два раза (один на клиентском узле, один на начальном узле), в результате вы получите два отдельных пула актеров.
- Самый простой способ решить эту проблему - просто создать экземпляр пула маршрутизаторов только один раз - позволить ему отправлять пул маршрутов по узлам кластера - и пересылать все ваши запросы через узел, который служит точкой входа в ваш кластер. Вы можете настроить другие узлы как аварийное переключение для повторного выполнения этой задачи в узле входа.
- Еще одна вещь, которую нужно сделать, - это использовать распределенный паб / суб или сегментирование кластера в зависимости от того, для чего вы используете маршрутизацию сообщений кластера.
После расследования я согласен с тем, что мое понимание кластерного согласованного хеш-маршрутизатора неверно. Теперь я понимаю, что мне нужно сообщить IActorRef, который представляет маршрутизатор, остальной части кластера. Это может быть достигнуто через ClusterSinglton или (возможно) распределенный паб-механизм некоторого типа. Механизм должен справиться с ситуацией, когда узел или субъект становятся "потерянными", создается новый актер (с новыми маршрутами), и новый субъект сообщается всем тем в кластере, на которые была указана предыдущая ссылка.
Я исследую подход "Кластерное разделение" и опубликую еще один вопрос, касающийся технических трудностей, которые возникают у меня с этим подходом.