Грамотное завершение узла кластера Akka.NET
Фон
У меня есть кластер Akka.NET, содержащий начальный узел Lighthouse и два других узла, использующих системы акторов. Когда я пытаюсь сделать постепенное завершение работы на одном из узлов кластера, я хочу видеть, что по крайней мере один из других узлов видит сообщение об уходе узла и что все узлы кластера в конечном итоге исключают уходящий узел из списка узлов.
После того, как об этом позаботятся, я ожидаю, что смогу отключить узел без двух других узлов, сходящих с ума из-за невозможности подключения к узлу, который выключился.
Что я пробовал
Сейчас у меня есть консольное приложение, обернутое в приложение TopShelf:
class ActorService : ServiceControl
{
private ActorSystem _actorSystem;
public bool Start(HostControl hostControl)
{
_actorSystem = ActorSystem.Create("myActorSystem");
var cluster = Cluster.Get(_actorSystem);
cluster.RegisterOnMemberRemoved(_Terminate);
return true;
}
public bool Stop(HostControl hostControl)
{
var cluster = Cluster.Get(_actorSystem);
cluster.Leave(cluster.SelfAddress);
return true;
}
private void _Terminate()
{
_actorSystem.Terminate();
}
}
Вот мой главный:
class Program
{
static int Main(string[] args)
{
return (int) HostFactory.Run(x =>
{
x.UseAssemblyInfoForServiceInfo();
x.RunAsLocalSystem();
x.StartAutomatically();
x.Service<ActorService>();
x.EnableServiceRecovery(r => r.RestartService(1));
});
}
}
При переходе через функцию Stop я не вижу ни одного полученного сообщения об уходе узла на другие узлы. Однако когда функция возвращается, другие узлы начинают выдавать исключения.
Пользователь канала Akka.NET Gitter сказал:
Я наблюдал то же самое даже без TopShelf, я должен сказать, с чистым проектом ASP.NET Core после прекращения работы веб-хостинга.
Вопрос
Что можно добавить, чтобы другие узлы получали сообщение об уходе узла?
2 ответа
Я думаю, что проблема в том, что Stop()
Метод завершается до завершения ухода. Вам следует дождаться события MemberRemoved.
это Stop()
Метод будет ждать, пока не будет вызван обратный вызов MemberRemoved и сообщен о том, что он даже завершил систему субъекта.
class Worker
{
private static readonly ManualResetEvent asTerminatedEvent = new ManualResetEvent(false);
private ActorSystem actorSystem;
public void Start()
{
this.actorSystem = ActorSystem.Create("sample");
}
public void Stop()
{
var cluster = Akka.Cluster.Cluster.Get(actorSystem);
cluster.RegisterOnMemberRemoved(() => MemberRemoved(actorSystem));
cluster.Leave(cluster.SelfAddress);
asTerminatedEvent.WaitOne();
//log.Info("Actor system terminated, exiting");
}
private async void MemberRemoved(ActorSystem actorSystem)
{
await actorSystem.Terminate();
asTerminatedEvent.Set();
}
}
Примечание: я проверил три типа приложений, как оставить кластер без проблем. Я размещал это на GitHub. Есть все еще некоторые исключения и несколько мертвых букв при выходе, но другие узлы больше не пытаются непрерывно переподключаться к вышедшему узлу.
Я хотел опубликовать обновление этой темы здесь, так как мы добавили новую функцию в Akka.NET, так как этот ответ был первоначально принят: CoordinatedShutdown
Он делает то, что ответ @ZoolWay делает под капотом и многое другое, но чтобы использовать его, все, что вам нужно сделать, это следующее:
class Worker
{
private ActorSystem actorSystem;
public void Start()
{
this.actorSystem = ActorSystem.Create("sample");
}
public void Stop()
{
Task<Done> shutdownTask = CoordinatedShutdown.Get(actorSystem).Run(CoordinatedShutdown.ClrExitReason.Instance);
shutdownTask.Wait();
}
}
Это проще и может обрабатывать более сложные сценарии очистки, такие как выключение Akka.Cluster.Sharding до завершения самого кластера. Я считаю, что это рекомендуемый способ работы с Akka.NET 1.3.2.