🚀 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 — правильный выбор:
- Много микросервисов на разных языках (.NET + Python + Node.js + Go)
- Dapr даёт одинаковый API для всех
- Частая смена инфраструктуры
- Сегодня RabbitMQ, завтра Kafka — код не меняется
- Сегодня Redis, завтра Cosmos DB — код не меняется
- Нужны готовые паттерны распределённых систем
- Pub/Sub с гарантированной доставкой
- Actor model (Virtual Actors)
- Workflows с компенсирующими транзакциями (Saga)
- Мульти-облачные развёртывания
- Один код → Azure, AWS, GCP, on-premise
❌ Случаи, когда Dapr — избыточен:
- Монолит или 2-3 сервиса (как ваш проект сейчас)
- Aspire + HttpClient достаточно
- Вся команда на .NET
- Встроенные средства .NET (gRPC, MassTransit) проще
- Простые сценарии без очередей/акторов
- Лишняя сложность
🔍 Реальный сценарий на базе вашего проекта
Представим, что ваш проект вырос:
Сегодня (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
🎓 Что можно добавить дальше?
- Pub/Sub: ApiService публикурует событие
forecast-generated, Web подписывается - Actors: Реализовать "умный" прогноз с памятью
- Resiliency: Настроить retry-политики в Dapr
- Secrets: Использовать Dapr Secrets API вместо appsettings
Хотите, чтобы я показал, как реализовать Pub/Sub (событийная архитектура) или Actors (умное прогнозирование)? 🚀
Копировать
Спроси Qwen
Объяснить
Перевести(ru-RU)
Комментариев нет:
Отправить комментарий