İçeriğe geç →

Orleans ile Loosely Coupled ve Scalable RESTful Service Oluşturma

Merhaba arkadaşlar.

Daha önceki Orleans’a Giriş makalem içerisinde, bu aralar Orleans ve Actor-based sistemler üzerinde durduğumdan bahsetmiştim. Bu makale konusu altında ise Orleans’ı middle-tier olarak kullanıp, loosely coupled ve scalable RESTful service’ler nasıl oluştururuz konusuna değinmeye çalışacağım.

Orleans’ın bize kazandırdığı pratikliğin yanı sıra, mimari boyutta da yeni bir yaklaşım getiriyor aslında. Data neredeyse business logic’in orada execute ediliyor olduğundan tutun, Reentrancy ve Concurrency gibi problemlerden de kaçınılmış bir şekilde her şeyin Scalable Grain‘ler (Virtual Actor) tarafından handle ediliyor olması kulağa ne kadar çok harika geliyor, değil mi?

Her neyse, actor-based sistemler her ne kadar fazlasıyla ilgimi çekiyor olsa da, Microsoft’un Orleans project’i ile neredeyse 10 yıllık yazılım hayatıma farklı bir bakış açısı geldi diyebilirim.

Orleans’ı Middle-Tier Olarak Kullanmak

Orleans’ın bize sunmuş olduğu gelişmiş actor modeli ile, distributed ve high-scale uygulamaları herhangi bir reliability, distributed resource management veya scalability bottleneck’leri gibi concern’ler ve complexity’ler olmadan geliştirebilmemize olanak sağlıyor. Peki, bu kadar güçlü bir framework’ü RESTful service’lerimizin arkasında bir middle-tier olarak kullanmak nasıl olurdu?

Dilerseniz konunun devamına bir örnek ile devam edelim. Kullanıcılar için bir araç takip sistemi geliştirdiğimizi düşünelim. İhtiyacımız olan araç verisini ise, sürücülerin aracı çalıştırdıkları an topluyor olacağız. Bu verilerin sonucunda ise örneğimiz gereği sisteme subscribe olan client’lara, aracın nerede olduğu bilgisini notification olarak göndereceğiz.

Uygulayacak olduğumuz mimari tasarım, neredeyse yukarıdaki diyagram gibi olacaktır. Bir adet REST endpoint’i ve arkasında çalışan bir Silo.

Siloyu Oluşturmak

İlk olarak “VehicleTracking.Common” isminde bir class library oluşturalım. Bu library içerisinde Grainler arası aktaracak olduğumuz message’ları tanımlayacağız.

“VehicleInfo” message’ını, aşağıdaki gibi tanımlayalım.

Yukarıda yer alan “[Immutable]” attribute’ünü, serialization işlemleri sırasında performans kazandırabilmek için kullandık. Orleans içerisinde farklı Silo’larda bulunan object’ler, Grain’ler arasında binary serializer ile serialize edilip, tekrar deserialize edilerek gönderilmektedir.

[Immutable] Messages

Serialization işlemi, object’lerin farklı Silo’larda bulunan Grain’lere ulaşabilmesi için gerçekleştirilir. Bir diğer yandan ise aynı Silo üzerinde bulunan Grain’lerin, aynı object’e erişmemeleri ve internal state’lerini değiştirememeleri için deep-copy işlemi gerçekleştirilir. Fakat bu işlem aynı Silo üzerinde bulunan Grain’ler için, biraz daha performanslı bir hale getirilebilinir. Performans optimizasyonu için Orleans içerisinde ise bu serialization işlemini, object’in immutable olup olmadığına karar verebilerek, bypass edebilmek mümkündür.

“VehicleTracking.GrainInterfaces” isminde bir class library daha oluşturalım ve içerisine “IVehicleGrain” isminde bir interface tanımlayalım.

Tanımlamış olduğumuz “IVehicleGrain” interface’i, sürücülerin konumlarını takip edebilmemiz için ihtiyaç duyduğumuz function’ı içermektedir. Function adına dikkat ederse eğer, daha çok bir RPC tanımlasına benzediğini görebiliriz. Çünkü Orleans client’ları ve Grain’leri, birbirleri ile RPC üzerinden haberleşmektedir.

“IVehicleTrackingGrain” isminde yeni bir interface daha tanımlayalım.

“IVehicleTrackingGrain” interface’i ile aracın hareket ettiği durumlarda, “IVehicleGrain” üzerinden aracın bulunduğu konum bilgisini “SetVehicleTrackingInfo” method’u ile aktarıyor olacağız. Sonrasında ise sisteme subscribe olan client’lara, aracın hareket ettiği durumlarda tracking bilgisini notification olarak göndereceğiz.

Notification işlemini gerçekleştirebilmemiz için, bir observer tanımlayacağız. Bunun için “IVehicleTrackingObserver” isminde bir interface daha tanımlayalım.

Artık Grain implementasyonlarına başlayabiliriz.

Öncelikle observe işlemlerini gerçekleştireceğimiz, “IVehicleTrackingGrain” interface’ini implemente edelim.

Observe işlemlerini Orleans içerisinde bulunan “ObserverSubscriptionManager” helper’ı ile gerçekleştireceğiz. Subscribing ve notification gönderme gibi işlemleri kolay bir şekilde handle etmektedir. Override ettiğimiz “OnActivateAsync” method’u ise, Grain’in aktive edilme işleminin en son kısmında call edilen bir method’dur. Burada ise “RegisterTimer” method’unu kullanarak, periodic olarak Grain’ler üzerinde callback işlemlerini gerçekleştirebilmeyi sağladık. Callback method’una baktığımızda ise “_vehicleInfo” field’ı null değilse, subscribe olmuş tüm client’lara “ReportToVehicle” method’u üzerinden bir notification göndereceğiz.

[Reentrant] Attribute

Yukarıda decorate etimiş olduğumuz “[Reentrant]” attribute’ünü de, network’de oluşabilecek bottleneck’lere karşı ve performans optimizasyonunu arttırabilmek için kullandık. Carl Hewitt‘in dediği gibi, kavramsal olarak actor model içerisinde message’lar birer birer işlenmektedir. Orleans içerisinde ise bazı maliyetli işlerin olduğu durumlarda Grain’i block’lamamak için ihtiyaç duyulabilecek noktalarda, “[Reentrant]” attribute’ü gibi tekniklerle concurrent processing sağlanabilmektedir. Fakat, kullanmamız gereken noktalarda dikkatli olmamız öneriliyor, aksi halde race-conditions durumları ile karşı karşıya gelebiliriz.

Artık “IVehicleGrain” interface’ini aşağıdaki gibi implemente edebiliriz.

Araçlardan gelecek olan konum bilgisini “SetVehicleInfo” method’u ile alıp, sonrasında bazı business logic’ler doğrultusunda işlediğimizi düşünelim. Business logic’lerin işlenmesinden sonra ise message’ı, örneğimiz gereği notification gönderebilmek için “VehicleTrackingGrain” e aktarıyoruz.

Artık implementasyonlarını tamamladık ve şimdi aşağıdaki gibi “VehicleTracking.TestSilo” isminde bir Orleans Dev/Test Host’u oluşturalım.

Sonrasında ise aşağıdaki gibi “VehicleTrackingObserver” isminde bir class tanımlayalım.

Burada ise daha önce tanımlamış olduğumuz “IVehicleTrackingObserver” interface’ini implemente ettik. Aracın hareket haline geçmesi ile birlikte gelen notification’ları, console üzerine yazdıracağız.

“Program.cs” içerisini ise, aşağıdaki gibi değiştirelim.

Burada ise “IVehicleTrackingGrain” tipinde bir Grain instance’ı alarak, “IVehicleTrackingObserver” üzerinden subscribe işlemini gerçekleştirdik. Bu proje üzerinde test amaçlı hem Orleans Silo’sunu ayağa kaldıracağız, hem de observer üzerinden gelen notification’ları da console üzerine yazdıracağız.

REST Endpoint’ini Tanımlamak

Artık REST endpoint’ini kodlamaya başlayabiliriz. Bunun için “VehicleTracking.Api” isminde empty bir Web API projesi oluşturalım ve ardından aşağıdaki gibi NuGet Package Manager üzerinden, “Microsoft.Orleans.Core” paketini dahil edelim.

Bu işlemin ardından, Silo ile iletişim kurabilmemiz için “Global.asax” içerisinde test Silo’sunu aşağıdaki gibi initialize etmemiz gerekiyor.

Artık Silo ile iletişim kurabiliriz. Hemen “VehicleTracking” isminde bir controller oluşturalım ve aşağıdaki gibi kodlayalım.

Artık POST işlemini gerçekleştirebileceğimiz bir endpoint’e sahibiniz. Burada “deviceId” ile bir “VehicleGrain” initialize edip, araç bilgilerini “SetVehicleInfo” method’u ile ilgili Grain’e aktarıyoruz.

Implementasyon işlemleri bu kadar ve artık test işlemi için hazırız. Test işlemini gerçekleştirebilmemiz için önce Silo’yu initialize etmemiz gerekiyor. Bunun için “VehicleTracking.TestSilo” projesini start etmemiz yeterli olacaktır ve sonrasında ise “VehicleTracking.Api” projesini start edeceğiz.

Bu işlemin ardından “VehicleTracking.Api” projesini de start edelim ve Postman üzerinden aşağıdaki gibi “/api/vehicle-trackings?deviceId=1&location=Taksim Square&direction=Bagdat Street” endpoint’ine, bir POST isteğinde bulunalım.

Sonuç olarak REST endpoint’i üzerinden göndermiş olduğumuz message’ın notification işleminin, yukarıda observer aracılığı ile console üzerine yazdırıldığını görebiliriz.

Sonuç Olarak

Oluşturmuş olduğumuz REST endpoint’i ve middle-tier olarak çalışan Orleans Silo’su ile, herhangi bir thread locking ve concurrency concern’leri olmadan loosely coupled ve scalable çalışan bir sistem inşa etmiş olduk.

Umarım faydalı bir blog yazısı olmuştur. Bu aralar Orleans’ın Docker Swarm ile beraber çalışması üzerine araştırmalarımı sürdürüyorum ve bu süreç içerisinde edinebildiğim veya karşılaşacağım tecrübeleri sizlerle başka bir blog konusu altında aktarmaya çalışacağım.

İlgili örneğe buradan erişebilirsiniz: https://github.com/GokGokalp/orleans-vehicletracking-sample

Kaynaklar:

https://dotnet.github.io/orleans/Tutorials/Front-Ends-for-Orleans-Services.html

https://dotnet.github.io/orleans/Tutorials/Concurrency.html

Bu makale toplam (1063) kez okunmuştur.

18
2



Kategori: .NET Actor Programming Model Architectural

Yorumlar

Bir Cevap Yazın

E-posta hesabınız yayımlanmayacak. Gerekli alanlar * ile işaretlenmişlerdir

*