среда, 29 июля 2026 г.

Dapr, Sidecar

🚀 Dapr и Sidecar — что это и когда используется

Отличный вопрос! Это важный архитектурный паттерн, который часто путают с тем, что делает Aspire. Давайте разберёмся на пальцах.

🎯 Сначала про паттерн Sidecar

Sidecar (сайдкар, "коляска") — это архитектурный паттерн, где рядом с основным сервисом запускается вспомогательный процесс, который берёт на себя "инфраструктурные" задачи.
Зачем это нужно?
  • Ваш код не знает про инфраструктуру (брокер сообщений, БД, секреты)
  • Sidecar предоставляет стандартизированный API (например, HTTP localhost:3500)
  • Можно менять инфраструктуру без изменения кода

🧩 Что такое Dapr?

Dapr (Distributed Application Runtime) — это open-source проект от Microsoft, который реализует паттерн Sidecar для микросервисов.

Dapr предоставляет готовые "строительные блоки":

Building Block
Что делает
🔌 Service-to-Service
Вызовы между сервисами с mTLS и retry
🗂️ State Management
Хранение состояния (Redis, Cosmos DB, SQL)
📨 Pub/Sub
Очереди сообщений (RabbitMQ, Kafka, Azure Service Bus)
🔐 Secrets
Работа с секретами (Vault, Azure Key Vault)
🔗 Bindings
Интеграция с внешними системами (AWS S3, SMTP)
👥 Actors
Паттерн Virtual Actors (как в Orleans)
🔄 Workflows
Оркестрация долгоживущих процессов

⚖️ Aspire vs Dapr — сравнение

Характеристика
Aspire
Dapr
Когда работает
Разработка и развёртывание
Runtime (продакшн)
Где живёт
AppHost (оркестратор)
Sidecar рядом с каждым сервисом
Что решает
"Как запустить все сервисы вместе?"
"Как сервисы общаются?"
Service Discovery
✅ Есть
✅ Есть (другой механизм)
State Management
❌ Нет
✅ Есть
Pub/Sub
❌ Нет
✅ Есть
Secrets
⚠️ Базовый
✅ Продвинутый
Observability
✅ OpenTelemetry
✅ OpenTelemetry
Облачная привязка
Azure-ориентированный
Cloud-agnostic

💡 Главное различие:

  • Aspire = инструмент разработчика для создания cloud-native приложений
  • Dapr = runtime-библиотека, которая работает в продакшене рядом с вашими сервисами

🤝 Можно ли использовать Aspire + Dapr вместе?

ДА! Они отлично дополняют друг друга. Начиная с .NET 8, у Aspire есть официальная поддержка Dapr.
var builder = DistributedApplication.CreateBuilder(args); // Добавляем Dapr sidecar к каждому сервису var apiService = builder.AddProject<Projects.AspireApp1_ApiService>("apiservice") .WithDaprSidecar("apiservice"); // 👈 Запустится Dapr sidecar var web = builder.AddProject<Projects.AspireApp1_Web>("webfrontend") .WithDaprSidecar("webfrontend") // 👈 И здесь тоже .WithReference(apiService); // Dapr components (Pub/Sub брокер, State Store) builder.AddDaprComponent("statestore", "state.redis", new Dictionary<string, string> { ["redisHost"] = "localhost:6379" }); builder.AddDaprComponent("pubsub", "pubsub.rabbitmq", new Dictionary<string, string> { ["host"] = "amqp://localhost:5672" });
Тогда при запуске у вас будет в 2 раза больше процессов:
apiservice (ваш .NET код) └─ apiservice-dapr (Dapr sidecar) webfrontend (ваш Blazor) └─ webfrontend-dapr (Dapr sidecar) Redis (кэш и state store) RabbitMQ (брокер сообщений)

🎯 Когда использовать Dapr?

✅ Случаи, когда Dapr — правильный выбор:

  1. Много микросервисов на разных языках (.NET + Python + Node.js + Go)
    • Dapr даёт одинаковый API для всех
  2. Частая смена инфраструктуры
    • Сегодня RabbitMQ, завтра Kafka — код не меняется
    • Сегодня Redis, завтра Cosmos DB — код не меняется
  3. Нужны готовые паттерны распределённых систем
    • Pub/Sub с гарантированной доставкой
    • Actor model (Virtual Actors)
    • Workflows с компенсирующими транзакциями (Saga)
  4. Мульти-облачные развёртывания
    • Один код → Azure, AWS, GCP, on-premise

❌ Случаи, когда Dapr — избыточен:

  1. Монолит или 2-3 сервиса (как ваш проект сейчас)
    • Aspire + HttpClient достаточно
  2. Вся команда на .NET
    • Встроенные средства .NET (gRPC, MassTransit) проще
  3. Простые сценарии без очередей/акторов
    • Лишняя сложность

🔍 Реальный сценарий на базе вашего проекта

Представим, что ваш проект вырос:
Сегодня (Aspire only): ┌──────────┐ HTTP ┌────────────┐ │ Web │────────────│ ApiService │ └──────────┘ └────────────┘ │ └── Генерирует случайные данные Завтра (Aspire + Dapr): ┌──────────┐ ┌────────────┐ ┌──────────────┐ │ Web │◄─ Pub/Sub ──►│ ApiService │◄─ Pub/Sub ──►│ Notification │ │ (Blazor) │ │ │ │ Service │ └──────────┘ └────────────┘ └──────────────┘ │ │ │ └── State Store ───► [ Redis ] ◄── State Store ────────┘
Что изменилось: ApiService публикует событие weather-forecast-generated в Pub/Sub Notification Service подписывается на событие и отправляет email/пуш Web сохраняет последнюю порцию прогнозов в State Store Если ApiService упадёт — Notification Service всё равно получит событие (брокер буферизует) Весь этот сценарий не требует изменения бизнес-логики сервисов — только настройки Dapr-компонентов. 📊 Сводная таблица решений Ваш проект Что использовать Простой Blazor + API (как сейчас) ✅ Только Aspire Добавляем кэш, БД, очередь ⚠️ Aspire + библиотеки (StackExchange.Redis, MassTransit) 10+ микросервисов на разных языках 🎯 Aspire + Dapr Нужны Virtual Actors, Workflows 🎯 Dapr (или Orleans) Мульти-облако, частая смена инфраструктуры 🎯 Dapr 💡 Итог Sidecar — это архитектурный паттерн (разделение кода и инфраструктуры) Dapr — это реализация этого паттерна с готовыми building blocks Aspire — это инструмент разработки для оркестрации всего этого В вашем текущем проекте Dapr не нужен — Aspire отлично справляется. Но когда проект вырастет до настоящих микросервисов с очередями, акторами и мульти-облачной архитектурой — Dapr станет очень кстати. 🚀 Хотите попробовать? Могу показать: 🔧 Как добавить Dapr в ваш проект прямо сейчас (пошагово) 📨 Как реализовать Pub/Sub (ApiService публикует прогноз → Web получает через очередь) 💾 Как использовать Dapr State Management вместо прямого Redis 🎭 Dapr Actors для реализации "умного" прогнозирования с памятью
🚀 Добавляем Dapr в ваш Aspire проект Отлично! Давайте интегрируем Dapr в ваш проект. Это превратит его из простого демо в полноценное микросервисное приложение с sidecar-паттерном. 📋 Предварительные требования Сначала установите Dapr CLI: Windows:
winget install Dapr.CLI
dapr init
Шаг 1: Обновляем AppHost.csproj Добавьте пакет Dapr в AspireApp1.AppHost.csproj:
<ItemGroup> <PackageReference Include="Aspire.Hosting.Dapr" Version="13.4.6" /> <PackageReference Include="Aspire.Hosting.Redis" Version="13.4.6" /> </ItemGroup>
Шаг 2: Обновляем AppHost.cs Замените содержимое AppHost.cs на это:
var builder = DistributedApplication.CreateBuilder(args); // Redis для кэширования и state management var cache = builder.AddRedis("cache"); // Dapr components (опционально, но полезно) var stateStore = builder.AddDaprComponent("statestore", "state.redis", new Dictionary<string, string> { ["redisHost"] = "localhost:6379", ["redisPassword"] = "" }); // ApiService с Dapr sidecar var apiService = builder.AddProject<Projects.AspireApp1_ApiService>("apiservice") .WithHttpHealthCheck("/health") .WithDaprSidecar("apiservice"); // 👈 Dapr sidecar запускается рядом // Web с Dapr sidecar builder.AddProject<Projects.AspireApp1_Web>("webfrontend") .WithExternalHttpEndpoints() .WithHttpHealthCheck("/health") .WithDaprSidecar("webfrontend") // 👈 И здесь тоже .WithReference(cache) .WaitFor(cache) .WithReference(apiService) .WaitFor(apiService); builder.Build().Run();
Что изменилось:
  • .WithDaprSidecar("apiservice") — запускает Dapr sidecar рядом с ApiService
  • .WithDaprSidecar("webfrontend") — запускает Dapr sidecar рядом с Web
  • Добавлен Dapr component для state management (использует Redis)

🔧 Шаг 3: Обновляем ApiService/Program.cs

Теперь ApiService будет использовать Dapr для вызовов:
var builder = WebApplication.CreateBuilder(args); // Add service defaults & Aspire client integrations. builder.AddServiceDefaults(); // Add services to the container. builder.Services.AddProblemDetails(); builder.Services.AddOpenApi(); var app = builder.Build(); app.UseExceptionHandler(); if (app.Environment.IsDevelopment()) { app.MapOpenApi(); } string[] summaries = ["Freezing", "Bracing", "Chilly", "Cool", "Mild", "Warm", "Balmy", "Hot", "Sweltering", "Scorching"]; app.MapGet("/", () => "API service is running. Navigate to /weatherforecast to see sample data."); app.MapGet("/weatherforecast", () => { var forecast = Enumerable.Range(1, 5).Select(index => new WeatherForecast ( DateOnly.FromDateTime(DateTime.Now.AddDays(index)), Random.Shared.Next(-20, 55), summaries[Random.Shared.Next(summaries.Length)] )) .ToArray(); // Логируем через Dapr (видимо в Aspire Dashboard) app.Logger.LogInformation("Generated weather forecast for {Count} days", forecast.Length);
return forecast; }) .WithName("GetWeatherForecast"); app.MapDefaultEndpoints(); app.Run(); record WeatherForecast(DateOnly Date, int TemperatureC, string? Summary) { public int TemperatureF => 32 + (int)(TemperatureC / 0.5556); }
 Шаг 4: Обновляем Web/WeatherApiClient.cs Теперь Web будет вызывать ApiService через Dapr sidecar:
namespace AspireApp1.Web;

public class WeatherApiClient(HttpClient httpClient, ILogger<WeatherApiClient> logger)
{
    public async Task<WeatherForecast[]> GetWeatherAsync(int maxItems = 10, CancellationToken cancellationToken = default)
    {
        // ВАЖНО: Dapr sidecar слушает на localhost:3500
        // Но Aspire автоматически настраивает правильный порт
        // Мы используем специальный формат: http://localhost:<dapr-port>/v1.0/invoke/<app-id>/method/<endpoint>
        
        logger.LogInformation("Requesting weather forecast via Dapr");
        
        List<WeatherForecast>? forecasts = null;
        
        // Aspire + Dapr: используем стандартный service discovery
        // Dapr sidecar автоматически проксирует запросы
        await foreach (var forecast in httpClient.GetFromJsonAsAsyncEnumerable<WeatherForecast>("/weatherforecast", cancellationToken))
        {
            if (forecasts?.Count >= maxItems)
            {
                break;
            }
            if (forecast is not null)
            {
                forecasts ??= [];
                forecasts.Add(forecast);
            }
        }
        
        logger.LogInformation("Received {Count} forecasts", forecasts?.Count ?? 0);
        return forecasts?.ToArray() ?? [];
    }
}

public record WeatherForecast(DateOnly Date, int TemperatureC, string? Summary)
{
    public int TemperatureF => 32 + (int)(TemperatureC / 0.5556);
}
Шаг 5: Добавляем Dapr State Management (бонус!) Давайте используем Dapr для сохранения последней порции прогнозов. Обновите ApiService/Program.cs:
var builder = WebApplication.CreateBuilder(args); builder.AddServiceDefaults(); builder.Services.AddProblemDetails(); builder.Services.AddOpenApi(); // Добавляем Dapr SDK для работы с sidecar builder.Services.AddDaprClient(); var app = builder.Build(); app.UseExceptionHandler(); if (app.Environment.IsDevelopment()) { app.MapOpenApi(); } string[] summaries = ["Freezing", "Bracing", "Chilly", "Cool", "Mild", "Warm", "Balmy", "Hot", "Sweltering", "Scorching"]; app.MapGet("/", () => "API service is running. Navigate to /weatherforecast to see sample data."); app.MapGet("/weatherforecast", async (Dapr.Client.DaprClient daprClient, ILogger<Program> logger) => { var forecast = Enumerable.Range(1, 5).Select(index => new WeatherForecast ( DateOnly.FromDateTime(DateTime.Now.AddDays(index)), Random.Shared.Next(-20, 55), summaries[Random.Shared.Next(summaries.Length)] )) .ToArray(); // Сохраняем в Dapr State Store (использует Redis под капотом) await daprClient.SaveStateAsync("statestore", "latest-forecast", forecast); logger.LogInformation("Generated and saved weather forecast"); return forecast; }) .WithName("GetWeatherForecast"); // Новый эндпоинт: получить последнюю сохранённую порцию app.MapGet("/weatherforecast/latest", async (Dapr.Client.DaprClient daprClient) => { var forecast = await daprClient.GetStateAsync<WeatherForecast[]>("statestore", "latest-forecast"); return forecast ?? []; }) .WithName("GetLatestWeatherForecast"); app.MapDefaultEndpoints(); app.Run(); record WeatherForecast(DateOnly Date, int TemperatureC, string? Summary) { public int TemperatureF => 32 + (int)(TemperatureC / 0.5556); }
Добавьте пакет в AspireApp1.ApiService.csproj:
<ItemGroup> <PackageReference Include="Dapr.AspNetCore" Version="1.14.0" /> </ItemGroup>
🎯 Шаг 6: Запускаем проект Откройте Docker Desktop В Visual Studio установите AspireApp1.AppHost как Startup Project Нажмите F5 Что вы увидите: В Docker Desktop:
redis (кэш и state store) aspire-dashboard
В процессах (не в Docker):
apiservice (ваш .NET код) apiservice-daprd (Dapr sidecar) webfrontend (ваш Blazor) webfrontend-daprd (Dapr sidecar)
В Aspire Dashboard (https://localhost:17008): Traces: увидите запросы через Dapr sidecar Logs: логи от обоих сервисов Metrics: метрики Dapr и вашего кода 🔍 Как проверить, что Dapr работает? 1. Откройте терминал и выполните:
  APP ID HTTP PORT APP PORT AGE CREATED DAPRD PID CLI PID apiservice 3500 5452 1m 2024-01-15 10:30:15 12345 12340 webfrontend 3501 7539 1m 2024-01-15 10:30:15 12346 12341
2. Прямой вызов через Dapr:
curl http://localhost:3500/v1.0/invoke/apiservice/method/weatherforecast
Что изменилось в архитектуре? Было (только Aspire):
Web ──HTTP──> ApiService ──> Генерация данных
Стало (Aspire + Dapr):
Web ──HTTP──> Web Dapr Sidecar ──mTLS──> ApiService Dapr Sidecar ──HTTP──> ApiService ↓ Dapr State Store (Redis)
Преимущества:
  • ✅ mTLS между сервисами (безопасность из коробки)
  • ✅ Retry и circuit breaker (через Dapr)
  • ✅ State management без привязки к Redis
  • ✅ Observability (все вызовы видны в трейсах)
  • ✅ Готовность к Pub/Sub, Actors, Workflows

🎓 Что можно добавить дальше?

  1. Pub/Sub: ApiService публикурует событие forecast-generated, Web подписывается
  2. Actors: Реализовать "умный" прогноз с памятью
  3. Resiliency: Настроить retry-политики в Dapr
  4. Secrets: Использовать Dapr Secrets API вместо appsettings
Хотите, чтобы я показал, как реализовать Pub/Sub (событийная архитектура) или Actors (умное прогнозирование)? 🚀
Копировать
Спроси Qwen
Объяснить
Перевести(ru-RU)



Комментариев нет:

Отправить комментарий