Микросервисы на C# (.NET): от теории к практике
В современной разработке программного обеспечения микросервисная архитектура стала стандартом для построения масштабируемых и отказоустойчивых систем. Платформа .NET предоставляет все необходимые инструменты для создания надежных микросервисов на C#. В этой статье мы рассмотрим ключевые принципы архитектуры, практические примеры кода и лучшие практики, которые помогут вам начать строить микросервисы на C#.
1. Основы микросервисной архитектуры на .NET
Микросервисы — это подход к разработке, при котором приложение разбивается на небольшие, независимые сервисы, каждый из которых отвечает за свою бизнес-функцию. В экосистеме .NET для этого используется ASP.NET Core — легковесный и кроссплатформенный фреймворк.
Ключевые принципы микросервисов на C#:
- Независимость развертывания — каждый сервис может быть обновлен без остановки других.
- Слабая связанность — сервисы общаются через API (REST, gRPC) или очереди сообщений.
- Собственная база данных — каждый микросервис владеет своими данными (Database per Service).
- Отказоустойчивость — сбой одного сервиса не должен блокировать всю систему.
Начнем с создания простого микросервиса на C#. Используем шаблон ASP.NET Core Web API.
// Создание нового проекта микросервиса
dotnet new webapi -n OrderService
cd OrderService
// Пример простого контроллера
[ApiController]
[Route("api/[controller]")]
public class OrdersController : ControllerBase
{
private readonly IOrderRepository _repository;
public OrdersController(IOrderRepository repository)
{
_repository = repository;
}
[HttpGet("{id}")]
public async Task<IActionResult> GetOrder(int id)
{
var order = await _repository.GetByIdAsync(id);
if (order == null)
return NotFound();
return Ok(order);
}
[HttpPost]
public async Task<IActionResult> CreateOrder(OrderDto orderDto)
{
var order = new Order(orderDto.CustomerId, orderDto.TotalAmount);
await _repository.AddAsync(order);
return CreatedAtAction(nameof(GetOrder), new { id = order.Id }, order);
}
}
2. Межсервисное взаимодействие: REST, gRPC и очереди
Микросервисы должны общаться друг с другом. В .NET доступны три основных подхода:
REST API (HTTP)
Самый простой и распространенный способ. Используйте HttpClient с фабрикой для правильного управления подключениями.
// Пример вызова другого микросервиса через HttpClient
public class InventoryServiceClient
{
private readonly HttpClient _httpClient;
public InventoryServiceClient(HttpClient httpClient)
{
_httpClient = httpClient;
}
public async Task<bool> CheckStockAsync(int productId, int quantity)
{
var response = await _httpClient.GetAsync(
$"/api/inventory/check?productId={productId}&quantity={quantity}");
return response.IsSuccessStatusCode;
}
}
// Регистрация в Program.cs
builder.Services.AddHttpClient<InventoryServiceClient>(client =>
{
client.BaseAddress = new Uri("http://inventory-service:5001");
});
gRPC
Для высокопроизводительной синхронной связи используйте gRPC. Он основан на HTTP/2 и Protocol Buffers.
// Определение сервиса в .proto файле
syntax = "proto3";
service OrderProcessor {
rpc ProcessOrder (OrderRequest) returns (OrderResponse);
}
message OrderRequest {
int32 order_id = 1;
string customer_email = 2;
}
message OrderResponse {
bool success = 1;
string message = 2;
}
// Серверная часть на C#
public class OrderProcessorService : OrderProcessor.OrderProcessorBase
{
public override async Task<OrderResponse> ProcessOrder(
OrderRequest request, ServerCallContext context)
{
// Логика обработки заказа
return new OrderResponse
{
Success = true,
Message = $"Order {request.OrderId} processed"
};
}
}
Очереди сообщений (RabbitMQ, Azure Service Bus)
Для асинхронного взаимодействия и обеспечения отказоустойчивости используйте брокеры сообщений.
// Отправка сообщения в RabbitMQ
public class MessagePublisher
{
private readonly IConnection _connection;
private readonly IModel _channel;
public MessagePublisher()
{
var factory = new ConnectionFactory() { HostName = "localhost" };
_connection = factory.CreateConnection();
_channel = _connection.CreateModel();
_channel.QueueDeclare(queue: "order_created",
durable: false,
exclusive: false,
autoDelete: false,
arguments: null);
}
public void Publish(OrderCreatedEvent orderEvent)
{
var json = JsonSerializer.Serialize(orderEvent);
var body = Encoding.UTF8.GetBytes(json);
_channel.BasicPublish(exchange: "",
routingKey: "order_created",
basicProperties: null,
body: body);
}
}
3. Паттерны для отказоустойчивости: Circuit Breaker и Retry
В распределенной системе сбои неизбежны. Библиотека Polly для .NET помогает реализовать паттерны устойчивости.
// Настройка политики Circuit Breaker и Retry
using Polly;
using Polly.Extensions.Http;
var retryPolicy = HttpPolicyExtensions
.HandleTransientHttpError()
.WaitAndRetryAsync(3, retryAttempt =>
TimeSpan.FromSeconds(Math.Pow(2, retryAttempt)));
var circuitBreakerPolicy = HttpPolicyExtensions
.HandleTransientHttpError()
.CircuitBreakerAsync(5, TimeSpan.FromSeconds(30));
// Применение к HttpClient
builder.Services.AddHttpClient<PaymentServiceClient>(client =>
{
client.BaseAddress = new Uri("http://payment-service:5002");
})
.AddPolicyHandler(retryPolicy)
.AddPolicyHandler(circuitBreakerPolicy);
Этот код автоматически повторяет запросы 3 раза с экспоненциальной задержкой, а при 5 последовательных ошибках размыкает цепь на 30 секунд.
4. Контейнеризация и оркестрация с Docker и Kubernetes
Для управления микросервисами используйте Docker. Каждый микросервис упаковывается в отдельный контейнер.
// Dockerfile для микросервиса на C#
FROM mcr.microsoft.com/dotnet/aspnet:8.0 AS base
WORKDIR /app
EXPOSE 80
FROM mcr.microsoft.com/dotnet/sdk:8.0 AS build
WORKDIR /src
COPY ["OrderService/OrderService.csproj", "OrderService/"]
RUN dotnet restore "OrderService/OrderService