Ayşegül Gürmeriç
CITS Tech
Published in
5 min readJan 5, 2023

--

Akka.NET ve aktör tasarı modeli

Yazımda daha önce bir buçuk milyondan fazla aktif ve anlık yüz binlere ulaşan kullanıcısı olan bir sistemin tasarım ve uygulama aşamalarında kullandığım ve performansından memnun kalınan bir model olan aktör kalıbını [1,5,7] anlatmaya çalışacağım. Konunun genişliği nedeniyle temel hatlarda kendi tecrübeme dayanarak ilerlemek istedim. Ek olarak, Akka.NET in C# ile temel kullanımı konusunda fikir verecek bilgiler paylaşarak temel bir C# örneğine de yer vereceğim. Dediğim gibi, ilerleyen yazılarda ihtiyaçlara göre daha karmaşık örnekler de oluşturmayı planlıyorum. Göreceği ilgiye göre ilerleyen yazılarda daha derin örnekler oluşturmaya çalışacağım.

Akka.NET, Mono ve .NET gibi ortamlarda eşzamanlı dağıtık işlem sistemleri geliştirmek için tasarlanmış bir frameworktür. Temelde Java için açık kaynak kodlu olarak geliştirilen ve popülerliği artan Akka sistemi, Petabridge [3,8] tarafından C# ve F# gibi dillerde de Akka.NET ile kullanılabilir hale getirilmiştir. Aktör tasarım kalıbının kullanılması gereken uygulamalarda tavsiye edebileceğim Akka.NET ile yüksek performanslı dağıtık işlem yapıları kurmanız güvenli, sıkıntısız ve hızlı geliştirme yapmanızı sağlayacaktır.

Bu sistem paralel hesaplamanın farklı kapasitede ve çeşitte cihaza dağıtılabilmesini sağlayan aktör modelinin C# üzerine geliştirilmesinin en pratik ve güvenilir yöntemlerinden biridir. Akka nın altyapısında bulunan aktör kalıbı, farklı ortamlarda bulunan, uygulamaların mesajlaşmasını ve ortak işlem yapabilmesini sağlayan asenkron bir mekanizmadır. Bu kalıpta olabildiğince basit kurgulanmış aktör adı verilen temel bir işlem ünitesi kodlanır. Birden fazla sayıdaki işlemci platformunun her birinde çok sayıda çalıştırılabilecek olan bu işleç genişleyebilirliği (scalability) [6] sayesinde karmaşık ve büyük problemleri dağıtık bir yapıda yüksek performans yaratarak çözümlemeye çalışır.

Aktörler ile tasarlanmış bir sistemin minimal yapı örneği [4]

Aktörler ile tasarlanmış bir sistemin minimal yapı örneği [4]

Akka.NET ile aktör modeli nasıl kullanılır?

Akka.NET ile gelen aktör modelinin kullanılabilmesi için ilk olarak elinizde bulunan ve dağıtık düzende çözülmek istenen problemin aktör ve mesaj sınıfları [8] ile tasarlanması gerekir. Akka.NET ile gelen ActorBase sınıfını kendi aktör sınıflarınızı tasarlamak için kullanarak aktörün hayat döngüsünü kapsayan işlemleri kodlamanız ilk adım olarak düşünülebilir. Sonrasında bu sınıflardan probleminizi çözecek sayıda nesneyi yaratmak ve doğru şekilde yönetecek algoritmalar oluşturmak aktör temelli uygulamalar geliştirmenin diğer temel işlemleridir. Tasarımlarınızın başarılı ve hızlı çözümler üretebilmesi için aşağıda belirttiğim bazı konulara dikkat etmenizi tavsiye ederim. Aktörlerin kullanılacağı projelerde, özellikle yazılım tasarımının oluşturulması sırasında, yazılım mimarının doğru kurgusu hem yazılımcıya hem de proje başarısına önemli katkı sağlayacaktır.

Uygulamanızda ve işlem dağıtımı yaptığınız diğer uygulamalarda birçok aktör nesnesi yaratarak hedeflediğiniz çözüme çok daha güçlü bir şekilde [6] ulaşmanız mümkün olur. Aktörleri kendi üzerlerine düşen işlemleri, diğer aktörlerin işlem ve durumlarından bağımsız olarak gerçekleştiren nesneler olarak düşünmemiz gerekir. Yani aynı işlemcide çalışan thread ler gibi paylaşımlı bir memory alanına sahip olmadıklarını varsayarak kullanmak, tecrübeme ve kullananların önerilerine göre daha doğru olur. Aktörler aynı zamanda kendi çocuk aktörlerini yaratabilme yetisine sahip olduklarından kurguladığınız sistemi genişletebilmeniz ve bunu yönetebilmeniz için önemli ve gelişmiş bir araçtır. Ayrıca, yazılım mimarisi ve uygulama geliştirme anlamında çok daha anlaşılır bir bakış açısı edinmenize de olanak sağlar. Sistem geliştirmeyi building blocks yapısına dönüştürmesi de büyük projelerin tasarımını kolaylaştırmaktadır.

C# Akka.NET temel uygulama örneği

Gerçek hayata dair problemlerin çözümü için kurgulanmış birçok örneği [6] internette bulmanız mümkün. Burada çok daha basit bir örneğin giriş için anlamlı olacağını düşünerek aşağıdaki kodu hazırladım. Dediğim gibi, ilerleyen yazılarda ihtiyaçlara göre daha karmaşık örnekler de oluşturmaya çalışacağım.

Aşağıdaki kodda iki aktörün yaratılması ve birbirleriyle haberleşmesi örneklendirilmiştir.

    public interface IMessage
{
int Id { get; }
MessageLevels Level { get; }
string Text { get; }
}

public enum MessageLevels
{
Low,
High,
Confidential
}

public sealed class MessageText : IMessage
{
public MessageText(int id, MessageLevels level, string text)
{
Id = id;
Level = level;
Text = text;
}

public int Id { get; }

public MessageLevels Level { get; }

public string Text { get; }
}

/// <summary>
/// Sınıfımızı ReceiveActor den türetiyoruz.
/// Bir aktörün içinde Receive methodunu kullanabilmek için aktörümüzü ReceiveActor den türetmeliyiz.
/// Constructor içinde Receive<T>(Action<T> handler) sayesinde her tip mesajı yakalayabiliriz.
/// </summary>
public class FirstActor : ReceiveActor
{
public FirstActor()
{
// FirstActor'un IMessage imzasına sahip mesajları dinlemesini sağlıyoruz.
Receive<IMessage>(s =>
{
Console.WriteLine($"Gelen mesaj : {s.Text} Önem Derecesi : {s.Level} Id : {s.Id}");

// Her mesaj geldiğinde Aktör Sistemimizdeki SecondActor'e mesaj atıyoruz.
Context.ActorSelection("/user/second-actor").Tell(new MessageText(Program.index++, MessageLevels.Low, Sender.Path.Name));
});
}
}

/// <summary>
/// Sınıfımızı ReceiveActor den türetiyoruz.
/// Bir aktörün içinde Receive methodunu kullanabilmek için aktörümüzü ReceiveActor den türetmeliyiz.
/// Constructor içinde Receive<T>(Action<T> handler) sayesinde her tip mesajı yakalayabiliriz.
/// </summary>
public class SecondActor : ReceiveActor
{
public SecondActor()
{
// SecondActor'un IMessage imzasına sahip mesajları dinlemesini sağlıyoruz.
Receive<IMessage>(s =>
{
Console.WriteLine($"Gelen mesaj : {s.Text} Önem Derecesi : {s.Level} Id : {s.Id}");

//Mesajlajma döngüsünü kırabilmek için eklediğim kontrol
if (Program.index <= 10)
{
// FirstActor'un mesajına cevap veriyoruz. Aslında burada örneğimizde mesajı sadece FirstActor yollayabileceği için açıklamayı bu şekilde yazıyorum.
//Fark ettiyseniz Sender.Tell ile gelen mesaja cevap veriyorum. Yukarıda FirstActor'un Receive<IMessage> uyarlamasında özellikle SecondActor'e mesaj atıyorum.
Sender.Tell(new MessageText(Program.index++, MessageLevels.Low, Sender.Path.Name));
}
});
}
}

class Program
{
public static ActorSystem MessageActorSystem;
public static int index = 0;

static void Main(string[] args)
{
Config config = ConfigurationFactory.ParseString(@"
akka {
stdout-loglevel = INFO
loglevel = INFO
log-config-on-start = on
}
");

MessageActorSystem = ActorSystem.Create("MessageActorSystem", config);

// Oluşturduğumuz Aktor Sisteminin içinde 'FirstActor' tipinde yeni bir aktör oluşturuyoruz
IActorRef firstActor = MessageActorSystem.ActorOf<FirstActor>("first-actor");

// Oluşturduğumuz Aktor Sisteminin içinde 'SecondActor' tipinde yeni bir aktör oluşturuyoruz
IActorRef secondActor = MessageActorSystem.ActorOf<SecondActor>("second-actor");

//first-actor ve second-actor ile mesajlaşmayı başlatıyoruz.
firstActor.Tell(new MessageText(index, MessageLevels.Low, "Start"));

Console.ReadLine();
}
}

Kullanımda dikkat edilecek konular:

Aktör modelinin kullanımında bilinmesi gereken ana konulardan biri aktör nesnelerinin birbirlerine gönderdiği mesajların FIFO (ilk giren ilk çıkar)’ya göre sırasıyla işlemesidir[7]. FIFO yapısına gelen mesajlardan ilkinin işlenmesi bitmeden bir diğerine geçiş yapılmaz yani aynı anda tek mesaj iletilir. Birden fazla mesajın veya işlecin aynı anda çalışması gerektiği durumlar için birden fazla aktör yaratılması gerekliliği unutulmamalıdır. Bu yapılarda hiyerarşik bir işleme ve kontrol kurgusu vardır. Her aktör kendisine bağlı çocuk aktörler yaratarak kendi üzerine düşen görevi tamamlar. Akka.NET uygulamalarında çocuk aktör nesnelerinin yönetimleri onları yaratan aktörlerin sorumluluğunda olduğundan, sistemin durmasına neden olacak hata durumlarının ve lokal hesaplama sonucu ortaya çıkacak veri kayıplarının hiyerarşik temelde ele alınması ve çözülmesi gerekmektedir. Ek olarak çok yüksek sayıda (örneğin milyonlarca) aktör nesnesi oluşabileceğinden lokalde kullanılan memory kapasitesinin tasarım esnasında özellikle ele alınmasını tavsiye ederim.

Tasarımlarınızda dikkat edilmesi gereken genel konuları kabaca yazmam gerekirse her aktör nesnesi için:

· Aktörün o anki durumu (state)

· İçerdiği algoritma

· Mesaj kutusuna binecek yük dengesi

· Yaratıcı aktörlerin yönetim işlemleri

· Hata durumlarının kontrolü

Konularına tasarım esnasında dikkat edilmeli ve implementasyon sırasında ciddi testler hazırlanmalı.

Akka.NET in sevdiğim yönleri:

· Her aktörü kolaylıkla adresleyip bunlara bir merkezden ulaşabilir veya birbirleri ile iletişimlerini sağlayabilen olay güdümlü (event driven) yapılar kurabilirsiniz.

· Akka ile oluşturduğunuz yapılarda hedeflediğiniz çözüme ulaşabilmek için böl ve yönet stratejisini [2] karşılayacak kadar ufak parçalarda (mikro servis) çözüm aktörü oluşturup bunları kolaylıkla yönetebilirsiniz.

· Probleminizin çözümü için kurgulanan aktör modelinde herhangi bir aktörün veya cluster yapılarda buna bağlı çocuk aktörlerin işleyişinde oluşan hatalar konusunda sistem tarafından bilgilendirilmeniz [9] güvenlik açısından en sevdiğim özelliklerden biri. Böyle bir durumda sadece o bölgedeki işleçleri tekrar başlatabilir veya çözümü için farklı stratejilere otomatik olarak gidebilirsiniz.

· Akka aynı zamanda bu aktörlerin birbirleri ile iletişimi açısından düşük gecikme (latency) konusunda performansı kanıtlanmış bir sistem olarak anılmaktadır.

· Her aktör node unun konfigürasyonu ve log [10] lanması konusunda da gerekli mekanizmalar Akka.NET içerisinden mevcuttur.

Referanslar

[1] https://en.wikipedia.org/wiki/Actor_model

[2] https://en.wikipedia.org/wiki/Divide-and-conquer_algorithm

[3] https://petabridge.com/

[4] https://cwiki.apache.org/confluence/display/FLINK/Akka+and+Actors

[5] https://getakka.net/articles/intro/what-are-actors.html

[6] https://www.etteplan.com/stories/how-virtual-actors-will-help-you-scale-your-applications-easy-way

[7] http://dist-prog-book.com/chapter/3/message-passing.html

[8] https://petabridge.com/cluster/lesson1.html

[9] https://getakka.net/articles/actors/fault-tolerance.html

[10] https://getakka.net/articles/utilities/logging.html

--

--