Akka nedir? Java ile Akka Kullanımı
Merhaba arkadaşlar,
Akka kısacası bize dinamik ,dağıtık , yüksek performanslı, ölçeklenebilir gerçek zamanlı işler yapmamıza olanak sağlar. Akka scala ile yazılmış bir api’dir . Scala jvm üzerinde çalıştığı için java’ya entegre etmek çok basittir.
Akka bize uygulamamız çalılırken eş zamanlı n tane objenin birbirinden bağımsız şekilde çalışmasına olanak sağlar. Eş zamanlı yaşayan objelerin herbirisine aktör (actor) denir.
Aktörlerin bize sağladığı avantajları sıralarsak ;
– Yüksek Performans
– Asenkron yapısı
– Non-Blocking (aktörlerin bağımsız olması birbirini etkilememesi)
– Mesaj odaklı olması
Aktör nedir (actor) ?
Akka eş zamanlı işlemleri basit ve güçlü bir şekilde yerine getirebilmek için aktör modelini benimsemiştir.
Aktörler ;
– Kendilerine ait belleğe sahiplerdir.
– Sadece asenkron mesajlarla iletişim kurarlar
– Her aktörün kendisine ait bir mesaj kutusu vardır
– Davranışlarının bir parçası olarak yeni mesajlar yaratabilir ve diğer aktörlere gönderebilir.
Aktör tabanlı modelleme daha çok bilgisayar bilimleri üzerine teorik araştırmalar,sosyal medya ,veri analizi, otomobil ve trafik sistemleri , bankacılık işlemleri vb. açıkçası yani veri trafiğinin yoğun olduğu sistemlerde kullanılır. IOT dünyasında da kullanımı son zamanlarda artış göstermiştir.
Aktör Modeli – Özellikleri
– Asenkron Model
– Gönderim ve alma birbirlerinden ayrılmıştır
– Mesajlar kaybolabilir
– Aktif elemanlar başarısız olamaz.
– Mesaj iletimi
-Mesajların iletimi modelin dışındadır gönderilen mesajlar herhangi bir zamanda ve herhangi bir sırada iletilebilirler – ya da iletilmezler.
Tek kısıtlama: Sadece gönderilen mesajlar iletilebilir
– Mesajların gelmesiyle davranışları değişebilir
– Mesajlar – 1- Değişemez değerler (Aktör modeli sürüm A) 2- Aktörler (Aktör modeli sürüm B)
– Topoloji
– Aktör ağları dinamiktir
– Aktörlere kanallar / portlar vb. mesajlar gönderilemez.
– Herşey bir aktördür, mesajlarda dahil.,
Şimdi ise akka ile javada örnek yapalım . Projemiz maven bu yüzden kütüphanemizi pom.xml‘e ekliyoruz.
1 2 3 4 5 |
<dependency> <groupId>com.typesafe.akka</groupId> <artifactId>akka-actor_2.11</artifactId> <version>2.4.4</version> </dependency> |
Diyelimki bir mail sunucusunu akka ile çalıştırıyoruz. Birtane MailObject isminde bir sınıfımız olsun , bu sınıfımız gönderilen mailleri temsil edecek.
MailObject.java
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 |
package com.turkishh.akka; import java.io.Serializable; /** * www.turkishh.com * * Mehmet KILIÇ */ public class MailObject implements Serializable { /** * */ private static final long serialVersionUID = 1L; private String message; public MailObject(String message) { this.message = message; } public String getMessage() { return message; } public void setMessage(String message) { this.message = message; } } |
Sonrasında bir tane SendMail isminde aktör (actor) sınıfımız olsun . Arkadaşlar açıklamaları kodda yaptığımdan comment düştüğüm yerleri iyi takip ediniz.
SendMail.java
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 |
package com.turkishh.akka; import java.util.Random; import java.util.concurrent.TimeUnit; import akka.actor.UntypedActor; /** * www.turkishh.com * * Mehmet KILIÇ */ // Bir sınıfın aktör olabilmesi için // UntypedActor isimli sınıftan türemesi lazım // Bu sınıftan miras aldığımızda onReceive() methodunu // ezmemizi zorunlu kılıyor. public class SendMail extends UntypedActor { private static int i=1; //Bir aktör çağırıldığında // ilk çalışan methodumuz // bu methodu kaç tane aktör ayağa kaldırdığımızı // daha net bir biçimde görebilmemiz için // oluşturdum. @Override public void preStart() { System.out.println(i+". Mail Gönderici Başlatıldı."); i++; } // Bir aktör çağırıldığında bu methoda her defasında düşer // preStart() metodu aktör oluşturulduğunda yani yeni create edildiğinde çalışır. // onReceive() metodunda ise farketmez ayakta olan veya yeni create edilmiş // aktör her defasında bu metodu çağırır. // @Override public void onReceive(Object msg) throws Exception { if(msg instanceof MailObject) { MailObject o = (MailObject) msg; System.out.println("<<< START : " + o.getMessage()+ " isimli işlem başladı " + " aktör adresi : " +getSelf().path()); Random r=new Random(); int a=r.nextInt(5); TimeUnit.SECONDS.sleep(a); System.out.println("<<< END : " + o.getMessage()+ " isimli işlem bitirdi. " + " aktör adresi : " +getSelf().path()); } } } |
Şimdi ise aktörlerimizi yöneteceğimiz bir sınıf yazıyoruz . Burada kaçtane aktör ayağa kaldıracağımızı , aktörlerimiz hangi mantık ile çalışacağına Router sınıfı ile karar veriyoruz. Ayrıyeten aktörlerimiz herhangi bir nedenden dolayı düştüğünde otomatik olarak onun yerine bir aktör create edilmesini yazacağız.
ActorManager.java
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 |
package com.turkishh.akka; import java.util.ArrayList; import java.util.List; import akka.actor.ActorRef; import akka.actor.Props; import akka.actor.Terminated; import akka.actor.UntypedActor; import akka.routing.ActorRefRoutee; import akka.routing.RandomRoutingLogic; import akka.routing.Routee; import akka.routing.Router; /** * www.turkishh.com * * Mehmet KILIÇ */ // Aktör yöneticimizde aslında bir aktördür // Sadece daha anlaşılır olması için ayağa kaldıracağımız // aktörleri buradan yönetelim istedim. public class ActorManager extends UntypedActor { @Override public void preStart() { System.out.println("Aktör yöneticisi çalıştı."); } Router router; { List<Routee> routees = new ArrayList<Routee>(); // Burada 3 tane aktör'ün çalışmasını istiyoruz. // işlemleri aktörlerimize gönderdiğimizde 3 tane aktör // iş yaptığını konsol çıktısında daha net göreceğiz. for (int i = 0; i < 3; i++) { ActorRef r = getContext().actorOf(Props.create(SendMail.class)); getContext().watch(r); routees.add(new ActorRefRoutee(r)); } // Router algoritmamızı burada veriyoruz. router = new Router(new RandomRoutingLogic(), routees); } @Override public void onReceive(Object msg) throws Exception { // Burada ise işi atayacağımız aktöre göndereceğimiz // sınıfın istediğimiz türdenmi diye kontrol ediyoruz // eğer istediğimiz tipten bir mesaj değil ise aktöre iş atamıyoruz // Burada else kısmında aktörlerimizden birisi hata verip düşerse tekrar // yeni bir aktör create ediliyor. if (msg instanceof MailObject) { router.route(msg, getSender()); } else if (msg instanceof Terminated) { router = router.removeRoutee(((Terminated) msg).actor()); ActorRef r = getContext().actorOf(Props.create(SendMail.class)); getContext().watch(r); router = router.addRoutee(new ActorRefRoutee(r)); } } } |
Router sınıfını kullanmamızdaki amaç aktörlerimizi runtime zamanı nasıl çalışması gerektiğine karar vermek . RandomRoutingLogic sınıfı ismindende anlaşılacağı gibi boş olan aktörlere iş parçacıklarını rastgele dağıtıyor.
– RoundRobinRoutingLogic :
İlk başlatıldığında aktör sayısı kadar işlemi dağıtır .Sonrasında ise ilk başlatılan aktör boşa çıkana kadar yeni iş parçacığı atamaz ta ki ilk başlatılan aktör boşa ne zaman çıkarsa sırada bekleyen iş parçacıklarını aktöre işlemesi için verir.
– RandomRoutingLogic :
İsminden de anlaşılacağı gibi ilk çalıştığında tüm işlemleri aktör sayısına böler ve eşit şekilde rastgele dağıtır.
– SmallestMailboxRoutingLogic :
Bu stratejide davranış şekli ilk başta aktör sayısı kadar işlemi dağıtır , Sonrasında ise iş yükü en az olan aktöre iş parçacıkları gönderir.
– BroadcastRoutingLogic
– ScatterGatherFirstCompletedRoutingLogic
– TailChoppingRoutingLogic
– ConsistentHashingRoutingLogic
Diğer stratejileri deneme ve okuma fırsatım olmadı ama akka’ın dökümantasyonun da olduğunu sanıyorum.
http://doc.akka.io/docs/akka/2.4.4/java/routing.html
Şimdi ise artık test sınıfımızı yazalım.
App.java
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 |
package com.turkishh.akka; import akka.actor.ActorRef; import akka.actor.ActorSystem; import akka.actor.Props; /** * www.turkishh.com * * Mehmet KILIÇ */ public class App { public static void main( String[] args ) { // Aktör oluşturabilmek için ActorSystem oluşturuyoruz // 'mail-sender-test' verdiğimiz bu isim her aktörün // unique bir adresi olacak adresimiz bu isim ile // başlayacaktır. ActorSystem system = ActorSystem.create("mail-sender-test"); // Burada ilk olarak aktör yöneticimizi çalıştırıyoruz. ActorRef sendMail = system.actorOf(Props.create(ActorManager.class)); MailObject mail1 = new MailObject("mail mesaj 1"); sendMail.tell(mail1, ActorRef.noSender()); System.out.println("> mail mesaj 1 gönderildi"); MailObject mail2 = new MailObject("mail mesaj 2"); sendMail.tell(mail2, ActorRef.noSender()); System.out.println("> mail mesaj 2 gönderildi"); MailObject mail3 = new MailObject("mail mesaj 3"); sendMail.tell(mail3, ActorRef.noSender()); System.out.println("> mail mesaj 3 gönderildi"); MailObject mail4 = new MailObject("mail mesaj 4"); sendMail.tell(mail4, ActorRef.noSender()); System.out.println("> mail mesaj 4 gönderildi"); MailObject mail5 = new MailObject("mail mesaj 5"); sendMail.tell(mail5, ActorRef.noSender()); System.out.println("> mail mesaj 5 gönderildi"); } } |
Artık çalıştıralım ve çıktısına bakalım. Arkadaşlar çıktısını iyi incelemenizi tavsiye ediyorum.
Burada gördüğünüz üzere arkadaşlar akka://mail-sender-test/user/$a/$a aktörü mail mesaj 2 isimli işini bitirdikten hemen sonra
mail mesaj 3 isimli iş parçacığını alıp işlem yapmak üzere devam ediyor.
Alttaki çıktıda ise 5 tane iş parçacığı ‘mail mesaj n+1’ oluşturmuşuz yine ve dikkat ediniz iki durumdada 3 tane aktör ayağa kaldırdı ve bunlara
router stratejimiz ne ise ona göre iş parçacıkları dağıtaya başladı.
Umarım yararlı olmuştur 🙂
İyi Çalışmalar