Практическое руководство по созданию, настройке и деплою Azure Functions для обработки событий и фоновых задач.
Создание Function App
# Storage Account (обязателен для Functions)
az storage account create \
--name storderappfunc \
--resource-group rg-orderapp-dev \
--location westeurope \
--sku Standard_LRS
# Function App на Consumption Plan
az functionapp create \
--name func-orderapp-dev \
--resource-group rg-orderapp-dev \
--storage-account storderappfunc \
--consumption-plan-location westeurope \
--runtime dotnet-isolated \
--runtime-version 8 \
--functions-version 4 \
--assign-identity # Включить Managed Identity
Пример: Service Bus триггер
Функция обрабатывает события из очереди Service Bus:
public class OrderConfirmedFunction
{
private readonly IEmailService _emailService;
private readonly ILogger<OrderConfirmedFunction> _logger;
public OrderConfirmedFunction(
IEmailService emailService,
ILogger<OrderConfirmedFunction> logger)
{
_emailService = emailService;
_logger = logger;
}
[Function("OrderConfirmedHandler")]
public async Task Run(
[ServiceBusTrigger("order-confirmed", Connection = "ServiceBus")]
ServiceBusReceivedMessage message,
ServiceBusMessageActions messageActions)
{
try
{
var evt = message.Body.ToObjectFromJson<OrderConfirmedMessage>();
_logger.LogInformation("Processing order {OrderId}", evt.OrderId);
await _emailService.SendOrderConfirmationAsync(
evt.CustomerEmail, evt.OrderId, evt.TotalAmount);
await messageActions.CompleteMessageAsync(message);
_logger.LogInformation("Order {OrderId} email sent", evt.OrderId);
}
catch (Exception ex)
{
_logger.LogError(ex, "Failed: {MessageId}", message.MessageId);
throw; // Message returns to queue for retry
}
}
}
public record OrderConfirmedMessage(
Guid OrderId,
string CustomerEmail,
decimal TotalAmount);
Пример: Timer триггер
Функция генерирует ежедневный отчет:
[Function("DailyOrderReport")]
public async Task RunReport(
[TimerTrigger("0 0 8 * * *")] TimerInfo timer)
{
_logger.LogInformation("Generating daily report at {Time}", DateTime.UtcNow);
var yesterday = DateTime.UtcNow.Date.AddDays(-1);
var orders = await _orderRepository.GetOrdersByDateAsync(yesterday);
var report = new DailyReport
{
Date = yesterday,
TotalOrders = orders.Count,
TotalRevenue = orders.Sum(o => o.TotalAmount),
AverageOrderValue = orders.Average(o => o.TotalAmount)
};
// Save report to Blob Storage
var blobClient = _blobContainer.GetBlobClient($"reports/{yesterday:yyyy-MM-dd}.json");
await blobClient.UploadAsync(
BinaryData.FromObjectAsJson(report), overwrite: true);
}
Пример: Blob триггер
Функция обрабатывает загруженные файлы:
[Function("ProcessUploadedDocument")]
public async Task RunBlobTrigger(
[BlobTrigger("uploads/{name}", Connection = "Storage")]
Stream stream,
string name)
{
_logger.LogInformation("Processing file: {Name}", name);
if (name.EndsWith(".csv"))
{
using var reader = new StreamReader(stream);
var content = await reader.ReadToEndAsync();
// Parse CSV and process...
}
}
Program.cs для Functions
var host = new HostBuilder()
.ConfigureFunctionsWebApplication()
.ConfigureServices((context, services) =>
{
services.AddApplicationInsightsTelemetryWorkerService();
services.ConfigureFunctionsApplicationInsights();
// Register services from Application and Infrastructure layers
services.AddApplication();
services.AddInfrastructure(context.Configuration);
})
.Build();
host.Run();
Деплой Functions
# Публикация
cd src/OrderManagement.Functions
dotnet publish -c Release -o ./publish
cd publish
zip -r ../functions.zip .
# Деплой
az functionapp deployment source config-zip \
--resource-group rg-orderapp-dev \
--name func-orderapp-dev \
--src ../functions.zip
# Проверка статуса
az functionapp show \
--name func-orderapp-dev \
--resource-group rg-orderapp-dev \
--query "state"
Durable Functions: практический пример
Оркестрация обработки заказа с retry и компенсацией:
[Function("OrderProcessingOrchestrator")]
public static async Task<string> RunOrchestrator(
[OrchestrationTrigger] TaskOrchestrationContext context)
{
var orderId = context.GetInput<Guid>();
// Step 1: Validate
await context.CallActivityAsync("ValidateOrder", orderId);
// Step 2: Reserve inventory
await context.CallActivityAsync("ReserveInventory", orderId);
// Step 3: Process payment (with retry policy)
var retryOptions = new TaskOptions(
new TaskRetryOptions(new RetryPolicy(3, TimeSpan.FromSeconds(5))));
var paymentResult = await context.CallActivityAsync<PaymentResult>(
"ProcessPayment", orderId, retryOptions);
if (!paymentResult.Success)
{
// Compensation: release inventory
await context.CallActivityAsync("ReleaseInventory", orderId);
return "Payment failed";
}
// Step 4: Send notifications (parallel)
var emailTask = context.CallActivityAsync("SendEmail", orderId);
var smsTask = context.CallActivityAsync("SendSms", orderId);
await Task.WhenAll(emailTask, smsTask);
return "Order processed successfully";
}