geri

Akka ya da Java ExecutorService ve Hazelcast ile kullanıcı bildirim kuyruğu

03/04/2012

Son zamanlarda Hazelcast'i dağıtık bir bildirim kuyruğu olarak kullanmak üzerine kafa yoruyorum. Amacım, farklı node'ların sistem çapındaki herhangi bir kullanıcı için üreteceği bildirimleri ortak bir kuyrukta tutmak ve bu bildirimleri gerekli zamanlarda işleyerek ilgili kullanıcıya ulaştırmak. Burada gerekli zaman ile bir kullanıcı için bildirim kuyruğu uzunluğunun belli bir sayıyı geçmesi ya da bu kullanıcı için kuyruktaki en eski bildirimin belli bir zamandan önce oluşturulmuş olmasını kastetmekteyim. Bu doğrultuda bir yandan hangi Hazelcast veri yapısını kullanacağımı düşünürken bir yandan da yazma işlemini ne şekilde yapabileceğimi araştırıyorum.

Önceleri bildirimleri saklamak için Queue kullanmayı düşündüm. Fakat yukarıda belirttiğim ihtiyaçları değerlendirince her kullanıcı için ayrı bir kuyruğa sahip olmam gerektiğini fark ettim. Her kullanıcı için ayrı bir Queue oluşturmak ve bunları yönetmek çok maliyetli olacağından ihtiyacıma en uygun veri yapısının MultiMap olduğuna kanaat getirdim. MultiMap içinde kullanacağım anahtarlar kullanıcı adına(username), değerler de bildirimlere(Notification) karşılık gelecek. Bir kullanıcı için birden fazla bildirim olabilecek.

Sonraki adımda bildirim kuyruğundaki bildirim sayısı belli bir değerin üstünde ve en eski bildirimin oluşturulma tarihi belli bir zamandan önce olan kullanıcıları en performanslı şekilde nasıl elde edebileceğimi düşündüm. Hazelcast, Map içindeki değerleri sorgulayabilmemiz için SQL benzeri bir DSL sağlıyor. Yalnız bu özellik MultiMap için geçerli değil. Bu sebeple veriyi denormalize etmeye ve kullanıcı kuyruklarına ait bilgileri(her bir kullanıcı kuyruğu için eleman sayısı ve en eski bildirim zamanı) ayrı bir Map içinde tutmaya karar verdim.

MultiMap içine yazacağım bildirimler için bir Notification sınıfı oluşturdum. Her bir kullanıcı için kuyruk bilgilerini saklamak amacıyla oluşturduğum QueueMetaData sınıfı ise şöyle:

Daha sonra, bir ExecutorService kullanarak 6 farklı kullanıcı için 16384 adet rastgele bildirim oluşturan bir kod yazdım. Buradaki belki en önemli problem Map ve MultiMap için senkronizasyonu sağlamak idi. Hazelcast'in sağladığı anahtar bazlı lock mekanizması yaptığım testlerde sorunsuz çalıştı. Yazma işleminin ayrıntısı için aşağıdaki Producer koduna bakabilirsiniz.

Her bir Producer opCount kadar Notification oluşturmakta. Serialization için JSON kullanmaya karar verdim. Bu sebeple google-gson kütüphanesinden faydalandım. Farklı sayıda Producer ile testler yaptım. Sonuçları yazının sonunda paylaşacağım. Producer sınıfının çalışabilmesi için gerekli olan LatchWorker ve HazelcastManager sınıflarının kodları aşağıda.

Son olarak yazma işlemini Akka ile de denemeye karar verdim. Bundaki amacım hem Akka'yı öğrenmek, hem de performansını ExecutorService ile karşılaştırmaktı. Producer sınıfına benzer şekilde bir Producer aktörü yazdım. Producer aktörlerini yönetmek için yazdığım Master aktörü aşağıda. Bu aktörün görevi belirtilen sayıda Producer oluşturarak bunların opCount kadar Notification oluşturmalarını sağlamak. 16384 adet bildirim için farklı thread/actor sayıları ile Akka ve ExecutorService yöntemlerini karşılaştırdım ve işlem sürelerini ölçtüm. Süreler µs(1/1000ms) cinsindendir. Sonuçlar aşağıdaki tabloda yer almakta.
ExecutorService Akka
1 thread/actor 21173751 16442245
2 thread/actor 15759791 12487179
4 thread/actor 13337575 9462002
8 thread/actor 11633355 8432141
16 thread/actor 11394124 8362550

Eğer bariz bir hata yapmıyorsam Akka yöntemi hem yönetim hem de performans açısından ExecutorService yöntemine göre daha önde. Hata bildirimi/öneri için yorumlarınızı beklemekteyim.

Follow me on Twitter