A close up of a blue object with a blurry background — Azure işlevlerini kullanarak sunucusuz uygulamalar geliştirin

Mersin’de 280 araçlık bir lojistik şirketi, 22 Azure Function App ile araç GPS, müşteri sipariş, teslimat onay, fatura entegrasyonu, EDI mesaj işleme, SMS bildirim gibi event-driven süreçleri yönetiyor. 8 ayda Consumption plan’dan başlayıp, hibrit Consumption + Premium + Flex Consumption modeline geçtik. Bu yazı sahadaki teknik notlar.

Plan Seçimi Karar Matrisi

Plan Cold start Max execution Aylık baz Use case
Consumption Var (1-5 sn) 5/10 dk $0 + execution Düşük volume, batch
Premium Yok (warm instance) 30+ dk $155+/ay Yüksek volume, latency-sensitive
Flex Consumption Az (always-ready) 30 dk $0 + execution + always-ready instances Modern öneri, esnek
Dedicated (App Service Plan) Yok Sınırsız VM bazlı Predictable, uzun süreli

22 Function App dağılımı:

  • 14 düşük-orta volume (1-100/dk) → Consumption
  • 5 yüksek volume + latency-critical (GPS pozisyon, müşteri tracking) → Premium
  • 3 batch işlem (gece raporlama) → Flex Consumption

Trigger Tipleri Sahada

HTTP Trigger

Müşteri portalından “siparişimi takip et” çağrısı:

[Function("OrderTracking")]
public async Task<HttpResponseData> Run(
    [HttpTrigger(AuthorizationLevel.Anonymous, "get")] HttpRequestData req,
    string orderId)
{
    var order = await _cosmosClient
        .GetContainer("orders", "tracking")
        .ReadItemAsync<Order>(orderId, new PartitionKey(orderId));
    
    var response = req.CreateResponse(HttpStatusCode.OK);
    await response.WriteAsJsonAsync(order.Resource);
    return response;
}

Service Bus Trigger

EDI mesaj işleme: müşteriden gelen sipariş Service Bus queue’ya düşer, function işler, ERP’ye yazar:

[Function("ProcessEdiOrder")]
public async Task Run(
    [ServiceBusTrigger("edi-orders", Connection = "ServiceBusConnection")] 
    EdiMessage message,
    FunctionContext context)
{
    var validated = _validator.Validate(message);
    if (!validated.IsValid)
        throw new EdiValidationException(validated.Errors);
    
    await _erpClient.CreateOrderAsync(message.ToOrder());
    await _notifier.SendAsync($"Order received from {message.CustomerCode}");
}

Hata durumunda otomatik retry, max retry sonrası dead-letter queue. Manuel inceleme için DLQ monitoring.

Event Grid Trigger

Storage’a yüklenen yeni teslimat fotoğrafı → blob created event → function fotoğrafı işler (resize, watermark, AI vision ile içerik tanıma):

[Function("ProcessDeliveryPhoto")]
public async Task Run(
    [EventGridTrigger] EventGridEvent eventGridEvent)
{
    var blobUrl = eventGridEvent.Data.ToObject<StorageBlobCreatedEventData>().Url;
    var photo = await _blobClient.DownloadAsync(blobUrl);
    
    // Resize
    var thumbnail = ImageProcessor.Resize(photo, 800);
    await _blobClient.UploadAsync($"thumbnails/{Path.GetFileName(blobUrl)}", thumbnail);
    
    // AI Vision: paket hasarı tespiti
    var visionResult = await _visionClient.AnalyzeImageAsync(blobUrl);
    if (visionResult.Tags.Any(t => t.Name == "damaged"))
        await _alerts.SendAsync($"Possible damage in delivery photo: {blobUrl}");
}

Cosmos DB Change Feed Trigger

Yeni sipariş Cosmos’a yazıldığında change feed function tetiklenir, müşteriye SMS atılır, dispatcher’a uyarı gider:

[Function("OnNewOrder")]
public async Task Run(
    [CosmosDBTrigger(
        databaseName: "logistics",
        containerName: "orders",
        Connection = "CosmosDBConnection",
        LeaseContainerName = "leases")]
    IReadOnlyList<Order> orders)
{
    foreach (var order in orders)
    {
        await _smsClient.SendAsync(order.CustomerPhone, 
            $"Siparişiniz alındı: {order.Id}, tahmini teslimat: {order.EstimatedDelivery}");
        await _dispatcher.AssignAsync(order);
    }
}

Timer Trigger

Günlük gece 02:00 raporlama, 06:00 müşteri özet email’i:

[Function("DailyReport")]
public async Task Run(
    [TimerTrigger("0 0 2 * * *")] TimerInfo timer)
{
    var report = await _reportingService.GenerateDailyAsync();
    await _emailClient.SendToManagementAsync(report);
}

Durable Functions: Long-Running Workflow

Müşteri siparişi gelince: validate → ERP’ye yaz → ödeme onayı bekle → kargo şirketine bildir → müşteriye SMS. Bu adımlar saatler/günler sürebilir, durable orchestrator ile yönetilir.

[Function("OrderOrchestrator")]
public async Task Run(
    [OrchestrationTrigger] TaskOrchestrationContext context)
{
    var order = context.GetInput<Order>();
    
    // 1. Validate
    var validated = await context.CallActivityAsync<bool>("ValidateOrder", order);
    if (!validated) return;
    
    // 2. ERP'ye yaz
    await context.CallActivityAsync("CreateInErp", order);
    
    // 3. Ödeme onayı bekle (max 48 saat)
    using (var timer = context.CreateTimer(TimeSpan.FromHours(48), CancellationToken.None))
    {
        var paymentEvent = context.WaitForExternalEvent<PaymentEvent>("PaymentReceived");
        var winner = await Task.WhenAny(timer, paymentEvent);
        if (winner == timer) {
            await context.CallActivityAsync("CancelOrder", order);
            return;
        }
    }
    
    // 4. Kargo şirketine bildir
    await context.CallActivityAsync("NotifyShipping", order);
    
    // 5. SMS gönder
    await context.CallActivityAsync("SendSms", order);
}

Cold Start Mücadelesi

Consumption plan’ında ilk request 2-5 sn cold start. Latency-sensitive function’lar için:

  • Premium plan’a taşı (always-warm)
  • Always-ready instances 1-2 ayarla (Flex Consumption)
  • Health check endpoint + ping (keep-warm strategy)
  • Dependency injection ve large object’leri lazy init

Maliyet Optimizasyonu

Consumption plan: ilk 1M execution + 400.000 GB-second ücretsiz/ay. Sonrası $0.20/M execution + $0.000016/GB-second.

22 function dağılımı, aylık cost:

Plan Function sayısı Aylık execution Aylık cost (USD)
Consumption 14 ~~3.2M ~~$45
Premium (EP1) 5 ~~28M ~~$420 (plan + execution)
Flex Consumption 3 ~~120K (batch) ~~$22
Toplam 22 ~~$487

Karşılaştırma: 22 function VM’de host edilse Standard_B4ms × 4 instance × ~$140 = $560/ay + management overhead. Functions ucuz + zero-ops.

Monitoring: Application Insights

Her function App Insights’a bağlı. Kustom KQL sorguları:

// En yavaş function
requests
| where timestamp > ago(24h)
| where cloud_RoleName startswith "func-"
| summarize 
    Count=count(), 
    AvgDuration=avg(duration), 
    P95=percentile(duration, 95)
  by name
| order by P95 desc

// Hata oranı yüksek function
requests
| where timestamp > ago(24h)
| where cloud_RoleName startswith "func-"
| summarize 
    Total=count(), 
    Failed=countif(success == false)
  by name
| extend FailRate = round(100.0 * Failed / Total, 2)
| where FailRate > 5
| order by FailRate desc

Sahada Düşülen Üç Tuzak

  1. Tüm function’ları aynı plan’a koymak: Düşük volume Consumption, latency-critical Premium. Hibrit yaklaşım gerekli.
  2. Stateless varsayımı kırmak: Function instance’ı bir sonraki çağrıda farklı olabilir. Static field’larda state tutma, distributed cache (Redis) kullan.
  3. Long-running iş için durable yerine async retry: 5 dk timeout aşılınca patlıyor. Durable functions veya queue-based long-poll pattern kullan.

CloudSpark olarak Azure Functions mimarisi, event-driven entegrasyon, Durable Functions, plan seçimi ve cold start optimizasyonu için danışmanlık veriyoruz.

🇹🇷 Türkçe🇬🇧 English🇩🇪 Deutsch🇫🇷 Français🇸🇦 العربية🇷🇺 Русский🇪🇸 Español