Azure Functions — Nedir Bunlar?
Eğer yazılım geliştiriyorsanız mutlaka Mikro Servisleri duymuşsunuzdur. Projelerimizi mikro boyutlara ayırarak, hepsinin belli bir düzen içerisinde çalışmasını sağlamaktır buradaki asıl amaç. Bunun sebebi ise her servisin sorumluluğunu ve diğerlerine bağımlılığını ayırarak hem ölçeklendirmeyi kolaylaştırmak hem de bakım ve yayınlama maliyetlerini düşürmek oluyor genelde. Bir servisin içerisinde yapılacak işlerimizi ise SOLID prensiplerinin ilki olan Single Responsibility (Tek sorumluluk) şeklinde tasarlayarak mikro servislerdeki ayrıcalıklardan fonksiyon bazında yararlanmak isteriz. Dolayısı ile yapacağımız işi küçük fonksiyonlara ayırmanın bir bakımdan iyi bir şey olduğunu savunabiliriz.
Bunun dışında yazmış olduğumuz kodlarımızı projelere çevirip bu projeleri de kullanıma açmak için yayınlamamız gerekiyor. Yayınlayabileceğimiz çok fazla çalışma ortamı olabilir ancak Cloud(bulut) sistemler de popülerliğini hızlı bir şekilde artırıyor bu amaç uğrunda. İşte Bulut sistemlerde bile yanlızca projelerimizi değil, projelerimizde kullanacağımız fonksiyonlarımızı da kolaylıkla yayınlayıp yönetebilmek için gerekli altyapıyı bulabiliyoruz. Bugün ki yazımızın konusu ise Microsoft’un Bulut hizmeti olan Azure üzerindeki “Azure Functions” yapısının hayatımızı nasıl kolaylaştırdığını inceleyeceğiz.
Terminoloji;
Bu makale boyunca aşağıda listesini gördüğünüz kelimeleri sıklıkla duyabilirsiniz. Makalenin daha anlaşılır olması için bu kelimeleri en başta açıklamakta fayda var.
Azure; Microsoft tarafından 2010 yılında yayınlanmış Bulut hizmetidir.
Resource; Bulut sistemimiz içerisinde oluşturmuş olduğumuz her bir aracımıza resource diyeceğiz. Örnek: Veritabanı, Network, Azure Function, ServiceBus vs.
Azure Storage; Azure tarafında farklı türdeki verilerin saklanmasını kolaylaştırmak amacıyla üretilmiş sanal bir harddisk gibi düşünebiliriz. Verilerimizi Table (tablo), Queue (kuyruk), Blob (büyük veriler olarak düşünebiliriz) veya File (dosya) Storage olarak tutabiliyoruz. Bu durumda Azure Storage’lar sadece bizim tarafımızdan değil, Resources’larımız tarafından da veri saklamak için kullanılabilir (örneğini göreceğiz).
Serverless; Bu çok özel bir kelime. Zira eğer daha önce Serverless kullanmadıysanız hazmetmesi biraz zor olabilir. Her ne kadar adı Sunucusuz anlamına geliyor olsa da burada bakış açısı önemli. Sunucularımızı genelde projelerimizi üzerinde yayınlamak için kullanıyoruz ve bu durumda sunucunun ve yayınlanacak projenin tüm bakımı, ayarlamaları, yedeklenmesi gibi sorunlar kullanıcı tarafından yani bizim tarafımızdan yapılıyor. Azure’daki Serverless senaryosunda ise elbet projemizin çalışması için bir sunucuya ihtiyaç oluyor ancak kullanıcı olarak bu bizi ilgilendirmiyor. Biz projemizi Azure’a emanet ediyoruz, varsa yapmamız/söylememiz gereken noktalar onları hallediyoruz ve topu bulut sisteme atıp arkamıza yaslanıyoruz. Bu uygulamanın herhangi bir sunucuda(istediğimizi de seçebiliriz) çalıştırılması, çalıştırılmasının devam ettirilmesi, sunucu maliyeti/bakımı gibi tüm işlemler Azure tarafından yönetiliyor oluyor. Azure Functions ise tam olarak bu şekilde kullanılıyor olacak bizim için :)
FaaS; Azure’un bizim için sunmuş olduğu hizmetlerin tiplerini tanımlamak için kullanılan kısaltmalardan birisidir. “Function as a Service” kategorisi altında Azure Functions’lar da yer alacak. Ancak tek servis Functions değil tabi ki.
Azure Functions; Bu konu ise makalemizin ana konusu olacağı için detaylara birazdan gireceğiz.
Triggers; Bir Azure Function’ın çalışmasını sağlayan olay.
Binding; Bir Azure Function’ın sistemimizde girdi veya çıktı üretebilmek için yapmış olduğu entegrasyon.
Not: Bu makale kapsamıda Azure Portal veAzure DevOps gibi kavramların açıklamaları yapılmayacaktır zira konular tek bir makalede anlatılmayacak kadar büyük.
Giriş kısmı biraz uzun oldu biliyorum ama azıcık daha dayanın. İşin zevkli kısmı şimdi başlıyor.
Azure Functions’larla ilgili olarak anlamamız gereken ilk nokta şudur; Azure Functions’ların kullanılma amacı tek bir amaca hizmet eden fonksiyonlar oluşturmaktır. Dolayısı ile aynı Single Responsibility deki gibi, bir fonksiyona bir tane sorumluluk yüklüyoruz. Ancak birbiri ile ilişkili fonksiyon setleri tek bir Azure Function projesinde yer alabilir. (Her ne kadar önerilmese de)
Bundan başka bilmemiz gereken diğer bir nokta da şudur; her nasıl ki projemiz içerisinde yer alan fonksiyonların çalışması için mutlaka çağırılması gerekiyorsa, aynı durum Azure Functions için de geçerlidir. Yani bir fonksiyon oluşturuyoruz ve nasıl veya ne zaman çalışması gerektiğini de belirtiyoruz. (Birazdan nasıl olacağı konusuna da gireceğiz)
Şimdi size öyle bir şey söyleceğim ki Azure Functions konusunda en çok seveceğiniz şey bu olacak. Hazır mıyız? Azure Functions’lar, Azure üzerindeki diğer Resource’lar ile entegre bir şekilde çalışabiliyor. Biliyorum ne var bunda diye soruyorsunuz ama şöyle düşünün. Azure üzerinde bir veritabanınız var, bir ServiceBus’ınız var veya bir CosmosDb’niz var. Herhangi bir Azure Function yazarak, bu ürünler/araçlar ile birlikte çalışması, bunlara bağlanması tek bir satırlık kod ile çok çok kolay ve etkili bir şekilde sağlanabiliyor. Çok iyi değil mi?
Azure Functions’ların çeşitleri ve hangi durumlarda tetiklendiğini anlatma dan önce, fiyatlandırmasından çok kısa bahsetmek istiyorum. Fiyatlandırma ile ilgili bilmemiz gereken ilk şey, Azure Functions Serverless bir yapıda çalıştığı için, tüm sunucu için para vermiyoruz sadece fonksiyonumuz çalışırken, çalıştığı sunucunun kaynaklarını ne kadar kullandıysa ona göre parasını veriyoruz (Consumption plan).
Yani diyelim ki bir fonksiyon yazdık ve yayınladık ancak hiç kullanmadık. Bu durumda cebimizden hiç para çıkmıyor. Ancak Serverless yapıdan bahsederken uygulamamızın nasıl bir sunucuda çalışacağını seçebiliyoruz demiştim. Bununla ilgili ise temelde 2 farklı sunucu planı var. Consumption ve Premium Plan.
Consumption Plan kullandığımızda fonksiyonumuz ortak bir sunucuda çalışır. Idle süresi 20 dakikadır ve bu süre boyunca kullanılmaması durumunda uyku durumuna geçer ve bir sonraki tetiklemede tekrar çalışmaya başlar. Uygulama yeniden başladığında (Cold Start) çalışmaya başlaması birkaç saniyeyi bulabilir.
Premium Plan’da ise, uygulamamız bir web uygulaması gibi sürekli çalışır vaziyette kalacaktır ve dolayısı ile Cold Start gibi bir prolem olmayacaktır. Ancak bunun ücretlendirmesi fonksiyonun kaynak tüketimi ile hesaplanmaz, uygulama sürekli çalıştığı için saat başına hesaplama yapılır.
Ne demiştik, bir fonksiyonun bir şekilde çalışması için tetiklenmesine ihtiyacımız var. Bunlara Azure Functions Triggers diyeceğiz. Zaten Azure Functions diyince konuyu burada ikiye ayıracağız. Triggers ve Bindings.
Triggers ile başlayalım. Bir Azure Function aşağıdaki yöntemler ile tetiklenebiliyor;
HttpTrigger; Bir fonksiyonun Http üzerinden tetiklenmesi ile çalışması için kullanılır. WebApi projenizdeki controller’lar altında bulunan bir Action Method olarak düşünebilirsiniz. Bir URL üzerinden HTTP yöntemi ile çalıştırılabilir.
TimerTrigger; Belirli zamanlarda kendi kendine çalışan bir fonksiyon istediğimizde bu tipte bir fonksiyon kullanabiliriz. Buradaki bilgiye göre TimerTrigger Azure Functions, NCronTab Expression yöntemi kullanılarak zamanlanabilir.
CosmosDbTrigger; Bir NoSql ürünü olan CosmosDb üzerinde yapılan Insert ve Update işlemleri sonrasında bir Azure Function’ın tetiklenmesini sağlayabiliyoruz. Çok iyi değil mi?
BlobTrigger; Azure Storage bölümünde Blob Storage’dan bahsetmiştim. Bu yapı üzerindeki değişiklikler sebebiyle tetiklenen bir fonksiyon yazabiliriz. Mesela bir dosya yüklendiğinde. Örneğin, siteniz üzerinden bir resim dosyası yüklendiğinde bir fonksiyonu tetikleyebilir ve bu resmi yeniden boyutlandırabilirsiniz.
ServiceBusTrigger; ServiceBus kullanıyorsanız bir Queue’ya veya bir Topic’e bir mesaj geldiğinde tetiklenen bir fonksiyon yazabilirsiniz. Örneğin; X kuyruğuna bir mesaj iletildiğinde bunu mesajı alıp mail atabilen bir fonksiyon yazabilirsiniz. Dolayısı ile mail gönderimlerinizi bir kuyruk yapısı ile entegre etmiş olursunuz. Bunun yanında Azure bünyesinde bulunan EventGridTrigger ve EventHubsTrigger gibi tetikleme yöntemleri de kullanılabilir. Hatta Azure üzerinde çalıştırmış olduğunuz RabbitMQ için de RabbitMQTrigger da kullanılabilir.
Bunlar dışında, KafkaTrigger, SignalRTrigger ve QueueStorageTrigger gibi tetikleme yöntemleri de mevcut.
Fonksiyonlar içinde yapabileceğimiz entegrasyonlardan bahsetmiş buna da Bindings diyeceğiz demiştim. Bindings’lerin Trigger’lardan farklı şöyle bir durumu var. Bir Azure Function tek bir şekilde tetiklenebiliyorken, birden fazla Bindings’e sahip olabilir. Bu Bindings’leri input ve output şeklinde ikiye ayıracağız. Bunlar ise, verilerin sistemden çekilmesi veya sisteme işlenmesi olarak düşünülebilir. Şimdi bu Binding yöntemlerinin önemli olanlarını birlikte inceleyelim.
Aşağıdaki listede Azure Function’ların versiyonları ile birlikte hangi Binding’ler kullanılabiliyor diye bir liste var. Bu listeye şuradan ulaşabiliriniz.
Gördüğünüz gibi liste hayli kalabalık. Bu makale bünyesinde tümü için detaylara giremeyeceğiz maalesef. Buradaki Bindings’leri şöyle düşünmenizi istiyorum. Diyelim li RabbitMQ Output Binding kullanıcaz. Bu da RabbitMQ ya bir veri yazacağız demektir. Bu fonksiyonumuz da TimerTrigger olsun. Mesela her 2 saatte bir çalışan bu fonksiyonumuz düzenli olarak RabbitMQ ya veri yazabilir. Bindings’leri kullanmanın ise türlü türlü yolu var. Nasıl kullanacağımıza göre istediğimiz bir tanesini seçebiliriz. Hatta Custom Binding’lerimizi de ekleyebiliyoruz Azure Functions üzerine. Şimdi gelin bilgisayarımızda çalışacak bir Azure Function’ı Visual Studio 2022 ile oluşturalım.
Create Project dedikten sonra Kategorilerden Azure Functions’ı seçelim.
Sonraki adım, projenin kayıt edileceği klasörü seçip, projesi isimlendirmek.
Bir sonraki adımda oluşturacağımız fonksiyon için Trigger seçeceğiz ve eğer seçtiğimiz Trigger ekstradan bilgi gerektiriyorsa ekranın altında o alanları da göreceğiz. Örneğin QueueTrigger olsaydı, hangi Queue olacağını da soracaktı bize. Mesela HttpTrigger bir fonksiyon yazacağımızı söyledik ve bu fonksiyona erişim için bir tip seçmemizi istiyor. Özel bir yetki olmasın, URL’e sahip herkes kullanabilsin diye Anonymous seçeceğiz.
Create butonuna bastıktan sonra ise projemiz oluşuyor ve Visual Studio içerisinde aşağıdaki gibi bir görüntü oluşuyor. Binding yöntemimiz belirlenmediği için, fonksiyon şablonu içerisinde yer almıyor ve sonradan eklememiz gerekiyor.
FunctionName attribute’ü fonksiyon ismini, host.json dosyası fonksiyon ile ilgili IConfiguration interface ile dışarı verilebilecek ayarları, local.settings.json dosyası ile kendi bilgisayarımızda debug yaparken kullanabileceğimiz ayarları ekleyebileceğimiz dosyalar olarak ortaya çıkıyor.
Bu fonksiyonumuzdaki amacımız HttpTrigger ile gelen isteği ServiceBusBindings kullanarak ilgili kuyruğa atmak olacak. O yüzden metodumuzu aşağıdaki hale getiriyoruz.
Öncelikle “Microsoft.Azure.WebJobs.Extensions.ServiceBus” paketini projemize yükleyelim.
Burada ServiceBus attribute’ünü ekleyerek başına return ekliyoruz. Ayrıca metodumuzu string geriye dönecek şekilde düzenliyoruz. Bu da şu anlama geliyor, bu metodun geriye döndüğü string değeri, ServiceBus içindeki myqueue kuyruğuna ekle. Ancak bunu yapmanın tek yolu bu değil, ServiceBus fonksiyonumuz içerisinde bir parametre olarak eklenip de kullanılabilir. Onun da örneği şöyle olabilir.
Bu fonksiyonda ise ServiceBus Binding için bir parametre ekledik. IAsyncCollector<dynamic> olarak tanımlamış olduğumuz parametremiz bizim için ServiceBus anlamına gelecektir. Bu değerin AddAsync metodunu çağırdığımızda ortaya çıkan anlam ise, name değişkenini ServiceBus içerisindeki myqueue kuyruğuna ekle! Çok kolay ve çok etkileyici değil mi? Bu Output Binding’e ait bir örnekti ancak Input Binding kullanarak da bu işlemi yapabilirdik. Mesela QueueTrigger kullanan bir fonksiyondan bize gelen bir bir Json değeri bir Model olarak alıp, ile CosmosDb Input Binding kullanarak bu Queue dan aldığımız modelin Property’lerine göreilgili veriyi CosmosDB den çağırabiliriz. Onun için şöyle bir fonksiyon yazmak gerekecek.
Gördüğünüz gibi myqueue kuyruğundaki veriyi direk olarak ToDoItem objesi olarak alabiliyoruz. Ve yine bu model içindeki Id ve PartValue değerlerini kullaranak CosmosDb Input Binding yöntemi ile veriyi veritabanından getirebiliyoruz. Çok çok çok kolay ve güzel bir yöntem :)
Geldik bu yazının sonuna. Azure Functions tarafında çok fazla Bindings ve Triggers mevbut. Bu makale içerisinde hepsi ile ilgili kodlara ve detaylarına giremediğimiz için üzgünüm ancak Azure konusunda Youtube Kanalımda videolar çekmeye başlayacağım. Sizler de videoları izlemek isterseniz hepinizi beklerim kanala.
Başka yazılarda görüşmek dileği ile. Azure ile kalın… :)