Azure Functions ile QueueTrigger Kullanmak

88 dakikada yazıldı

Düzenle

Azure Functions'da kullanabileceğimiz Trigger yapılarından biri de QueueTrigger. Azure Storage hizmetinin bir parçası olan Queue Storage genelde Web ve Worker Role'lerin birbirinden bağımsız olarak ölçeklendirildiğinde birbirleri ile konuşabilmesi için kullanılıyor. Bu yazıdaki amacım tabi ki Queue Storage'ı anlatmak değil. O konuyu merak edenler için daha tavsiyem daha önce yazdığım bu yazıyı okumaları ;)

Queue Trigger Tanımlamak

Queue Trigger tanımlama işimize yeni yaratacağımız bir Function'ın function.js dosyasından başlayacağız.

[function.js]

{
    "disabled": false,
    "bindings": [
      {
        "name": "queueJob",
        "queueName": "samplequeue",
        "connection": "AzureWebJobsStorage",
        "type": "queueTrigger",
        "direction": "in"
      }
    ]
}

Parametrelere bakacak olursak, name bizim functiona Queue mesajını taşıyacak olan parametrenin adı olacak, queueName dinleyeceğimiz kuyruğun adı olacak (kullanacağımız storage hesabında bu isimde bir kuyruk yaratmamız gerek), connection kısmında appsettings.json'a koyacağımız storage connection stringini key adını yazıyoruz ki functions hangi storage account'a gideceğini bilsin ve son olarak type için doğal olarak queueTrigger diyerek direction olarak da in diyoruz. Böylece queueTrigger tipinde bir inputTrigger yaratmış olduk ve hangi storage account'taki hangi kuyruğu dinlemek istediğimizi ve hangi parametre ismi ile bizim function'a gönderilmesi gerektiğini de belirtmiş olduk.

[appsettings.json]

{
  "IsEncrypted": false,
  "Values": {
    "AzureWebJobsStorage": "UseDevelopmentStorage=true",
    "AzureWebJobsDashboard": "UseDevelopmentStorage=true"
  }
}

AppSettings.json dosyamız yukarıdaki şekilde local storage emülatörünü gösteriyor. Buradaki AzureWebJobsStorage connection stringini zaten function'ı tanımlarken de kullanmıştık. samplequeue adındaki kuyruğu Azure Functions Runtime bu storage account içerisinde arayacak. Localde test edebilmek için Azure Storage Explorer kullanarak local emülatörü bağlanıp elle gerekli kuyruğu yaratabilirsiniz.

[run.csx]

using System;

public static void Run(string queueJob,
    DateTimeOffset expirationTime,
    DateTimeOffset insertionTime,
    DateTimeOffset nextVisibleTime,
    string queueTrigger,
    string id,
    string popReceipt,
    int dequeueCount,
    TraceWriter log)
{
    log.Info($"C# Queue trigger function çalıştı: {queueJob}\n" +
        $"queueTrigger={queueTrigger}\n" +
        $"expirationTime={expirationTime}\n" +
        $"insertionTime={insertionTime}\n" +
        $"nextVisibleTime={nextVisibleTime}\n" +
        $"id={id}\n" +
        $"popReceipt={popReceipt}\n" +
        $"dequeueCount={dequeueCount}");
}

Yukarıda en basit hali ile bir log atan Azure Function var. Gelin şimdi bu function'ın parametrelerini inceleyelim.

Queue Trigger Log çıktısı

Bir mesajın başarılı bir şekilde işlenip işlenmediğine Azure Functions nasıl karar verir? diye sorarsanız, cevabı basit. Eğer function geriye bir exception dönmüyorsa alınan mesaj başarılı bir şekilde işlendi demektir.

Runtime Konfigürasyonu

Azure Functions'daki qeueTriggerlar ile ilgili yapabileceğimiz bazı özelleştirmeler var. Bunları host.json dosyası içerisinde yapabiliyoruz ve bir Function App genelinde geçerli oluyor.

[host.json]

{
  "queues": {
    "maxPollingInterval": 2000,
    "batchSize": 16,
    "maxDequeueCount": 2,
    "newBatchThreshold": 8
  }
}

Queue Binding

Bu noktaya kadar bir queue trigger tanımlayıp kuyruğa mesaj atıldığında onu işlemeyi gördük. Gelin bir de bir kuyruktan diğerine mesaj atma konusuna bakalım. Özetle, queue bindingleri kullanarak birden çok kuyruk arasında iletişim sağlayacağız.

[function.js]

{
  "disabled": false,
  "bindings": [
    {
      "name": "queueJob",
      "queueName": "samplequeue",
      "connection": "AzureWebJobsStorage",
      "type": "queueTrigger",
      "direction": "in"
    },
    {
      "name": "queueJobOutput",
      "queueName": "samplequeueout",
      "connection": "AzureWebJobsStorage",
      "type": "queue",
      "direction": "out"
    }
  ]
}

Yukarıdaki örnekde ikinci bir binding daha görüyorsunuz. Yine name parametresindeki değer bizim function'ın imzasında yer alacak. queueName bu sefer yeni bir output kuyruğunun adı. connectionımız aynı, böylece aynı storage hesabını kullanmış olacağız. Son olarak binding tipimiz queue ve direction da out şeklinde ayarlanmış durumda.

[run.csx]

using System;

public static void Run(string queueJob, out string queueJobOutput, TraceWriter log)
{
    queueJobOutput = queueJob + " devam....";
}

Bu sefer function kodunu biraz daha temiz tutmak istedim. Basit bir şekilde input ve output parametrelerimiz var. queueJobOutput parametresini zaten output bindingimizi tanımlarken kullandığımız name değeri oldu. Bu functionın yapacağı şey bir kuyruğa mesaj eklendiğinde tetiklenip gelen mesajın sonunda " devam..." metnini ekleyip yeni kuyruğa yeni bir mesaj olarak eklemek olacak. Eminim siz daha anlamlı senaryolar düşünebilirsiniz :)

[run.csx]

using System;

public static void Run(string queueJob, ICollector<string> queueJobOutput, TraceWriter log)
{
    queueJobOutput.Add(queueJob + " devam...)");
    queueJobOutput.Add(queueJob + " daha da devam...");
}

Eğer aynı kuyruğa birden çok mesaj / görev atmanız gerekirse bu sefer ICollector'ı kullanabilirsiniz. Yukarıdaki örnekte bizim kaynak kuyruktan gelen görevi alıp iki farklı görev (queue job) yaratıp output bindingde tanımlı kuyruğa gönderiyoruz.

POCO Kullanımı

İsterseniz bindinglerde kendi özel objelerinizi de kullanabilirsiniz.

[run.csx]

using System;

public static void Run(string queueJob, out OrnekMesaj queueJobOutput, TraceWriter log)
{
    queueJobOutput = new OrnekMesaj() { Metin = $"{queueJob} devam..." };
}

public class OrnekMesaj
{
    public string Metin { get; set; }
}

Yukarıdaki örnekte kaynak kuyruktan gelen metnin üzerine " devam..." metnini eklerken artık geriye basit bir String olarak değil de custom OrnekMesaj nesnesi ile gönderiyoruz. Buradan yeni kuyruk objesine, göreve deserialize işlemini JSON deserializer kullaran Azure Functions Runtime kendisi halledecek. Aynı işlemi input binding'lerde de kullanabilirsiniz.

Output Binding'de JSON Deserialization

Yukarıdaki ekran görüntüsünde yaptığımız örneklerin output binding sonuçlarını görebilirsiniz.

Kolay gelsin ;)