MassTransit для C# (.NET): полное руководство с примерами

Онлайн-тренажер Python для начинающих

Изучайте Python легко и без перегрузки теорией. Решайте практические задачи с автоматической проверкой, получайте подсказки на русском языке и пишите код прямо в браузере — без необходимости что-либо устанавливать.

Начать курс

MassTransit для C# (.NET): полное руководство с примерами

1. Что это и зачем нужна

MassTransit — это мощная, открытая и широко используемая библиотека для создания распределённых приложений на платформе .NET. Она предоставляет высокоуровневую абстракцию над различными системами обмена сообщениями (message brokers), такими как RabbitMQ, Azure Service Bus, Amazon SQS/SNS, ActiveMQ и Kafka (через коннекторы). Основная цель MassTransit — упростить разработку сервисов, взаимодействующих через асинхронные сообщения, избавив разработчика от необходимости вникать в низкоуровневые детали конкретного брокера.

Зачем нужна MassTransit? В современной микросервисной архитектуре сервисы должны общаться друг с другом надёжно и асинхронно. Вместо прямых HTTP-вызовов (которые могут привести к каскадным сбоям и жёсткой связности) используется обмен сообщениями. MassTransit берёт на себя такие сложные задачи, как: управление подключениями и каналами, повторные попытки отправки при сбоях, обработка ошибок, распределённые транзакции (через саги), управление очередями и топиками, сериализация/десериализация сообщений, а также предоставляет удобный API для отправки, публикации и потребления сообщений.

Библиотека реализует несколько ключевых паттернов: Command-Query Separation (через отправку команд), Publish-Subscribe (через публикацию событий), Request-Response (для синхронного обмена через асинхронные сообщения) и Saga Pattern (для управления долгоживущими бизнес-процессами). Благодаря этому MassTransit является де-факто стандартом для построения надёжных, масштабируемых и тестируемых распределённых систем на .NET.

2. Установка

MassTransit устанавливается через NuGet. В зависимости от используемого брокера сообщений, вам потребуется установить основной пакет и пакет для конкретного транспорта.

Базовая установка (без транспорта):

dotnet add package MassTransit

Для RabbitMQ:

dotnet add package MassTransit.RabbitMQ

Для Azure Service Bus:

dotnet add package MassTransit.Azure.ServiceBus.Core

Для Amazon SQS:

dotnet add package MassTransit.AmazonSQS

Для Kafka:

dotnet add package MassTransit.Kafka

3. Быстрый старт — минимальный рабочий пример

Ниже приведён простейший пример отправки и получения сообщения с использованием RabbitMQ. Убедитесь, что у вас запущен RabbitMQ (например, через Docker: docker run -d --hostname my-rabbit --name some-rabbit -p 5672:5672 -p 15672:15672 rabbitmq:3-management).

using System;
using System.Threading.Tasks;
using MassTransit;

// 1. Определяем контракт сообщения
public record OrderSubmitted
{
    public Guid OrderId { get; init; }
    public string CustomerName { get; init; }
}

// 2. Определяем потребителя (Consumer)
public class OrderSubmittedConsumer : IConsumer<OrderSubmitted>
{
    public async Task Consume(ConsumeContext<OrderSubmitted> context)
    {
        var message = context.Message;
        Console.WriteLine($"[Consumer] Получен заказ: {message.OrderId} от {message.CustomerName}");
        await Task.CompletedTask;
    }
}

// 3. Настраиваем и запускаем шину
public class Program
{
    public static async Task Main()
    {
        var busControl = Bus.Factory.CreateUsingRabbitMq(cfg =>
        {
            cfg.Host("localhost", "/", h =>
            {
                h.Username("guest");
                h.Password("guest");
            });

            cfg.ReceiveEndpoint("order-queue", e =>
            {
                e.Consumer<OrderSubmittedConsumer>();
            });
        });

        await busControl.StartAsync();
        Console.WriteLine("[Bus] Шина запущена. Нажмите любую клавишу для отправки сообщения...");
        Console.ReadKey();

        // 4. Отправляем сообщение
        var endpoint = await busControl.GetSendEndpoint(new Uri("queue:order-queue"));
        await endpoint.Send(new OrderSubmitted
        {
            OrderId = Guid.NewGuid(),
            CustomerName = "Иван Иванов"
        });

        Console.WriteLine("[Sender] Сообщение отправлено. Нажмите любую клавишу для выхода...");
        Console.ReadKey();

        await busControl.StopAsync();
    }
}

4. Основные методы/функции/классы

4.1. Bus.Factory.CreateUsingRabbitMq

Сигнатура: public static IBusControl CreateUsingRabbitMq(Action<IRabbitMqBusFactoryConfigurator> configure)

Что делает: Создаёт и настраивает экземпляр шины (bus), используя RabbitMQ в качестве транспорта. Это основной метод для инициализации MassTransit с RabbitMQ.

var bus = Bus.Factory.CreateUsingRabbitMq(cfg =>
{
    cfg.Host("localhost", "/", h =>
    {
        h.Username("guest");
        h.Password("guest");
    });
    cfg.ReceiveEndpoint("my-queue", e =>
    {
        e.Consumer<MyConsumer>();
    });
});

4.2. IBusControl.StartAsync

Сигнатура: Task StartAsync(CancellationToken cancellationToken = default)

Что делает: Запускает шину: устанавливает соединение с брокером, создаёт очереди и обменники, начинает прослушивание очередей. Должен быть вызван перед отправкой/приёмом сообщений.

await bus.StartAsync();
Console.WriteLine("Шина запущена!");

4.3. IBusControl.StopAsync

Сигнатура: Task StopAsync(CancellationToken cancellationToken = default)

Что делает: Корректно останавливает шину: завершает обработку текущих сообщений, закрывает соединения с брокером. Должен быть вызван при завершении приложения.

await bus.StopAsync();
Console.WriteLine("Шина остановлена.");

4.4. ISendEndpoint.Send

Сигнатура: Task Send<T>(T message, CancellationToken cancellationToken = default) where T : class

Что делает: Отправляет сообщение в конкретную очередь. Сообщение будет обработано одним из потребителей этой очереди (конкурирующие потребители).

var endpoint = await bus.GetSendEndpoint(new Uri("queue:order-queue"));
await endpoint.Send(new OrderSubmitted { OrderId = Guid.NewGuid(), CustomerName = "Alice" });

4.5. IPublishEndpoint.Publish

Сигнатура: Task Publish<T>(T message, CancellationToken cancellationToken = default) where T : class

Что делает: Публикует событие в шину. Все потребители, подписанные на этот тип сообщения (через свои очереди), получат его. Реализует паттерн Publish-Subscribe.

await bus.Publish(new OrderSubmitted { OrderId = Guid.NewGuid(), CustomerName = "Bob" });

4.6. IConsumer<TMessage>

Сигнатура: public interface IConsumer<in TMessage> where TMessage : class { Task Consume(ConsumeContext<TMessage> context); }

Что делает: Базовый интерфейс для всех потребителей сообщений. Ваш класс должен реализовать метод Consume, который будет вызван при получении сообщения типа TMessage.

public class MyConsumer : IConsumer<MyMessage>
{
    public async Task Consume(ConsumeContext<MyMessage> context)
    {
        var msg = context.Message;
        Console.WriteLine($"Получено: {msg.Text}");
    }
}

4.7. ConsumeContext

Сигнатура: (не создаётся напрямую, предоставляется MassTransit)

Что делает: Контекст потребления сообщения. Предоставляет доступ к самому сообщению (context.Message), а также к методам для отправки ответов (context.RespondAsync), публикации новых событий, доступа к заголовкам, повторной обработки и т.д.

public async Task Consume(ConsumeContext<OrderRequest> context)
{
    var request = context.Message;
    // Отправляем ответ обратно отправителю
    await context.RespondAsync(new OrderResponse { OrderId = request.OrderId, Status = "Processed" });
}

4.8. IRequestClient<TRequest, TResponse>

Сигнатура: public interface IRequestClient<TRequest, TResponse> where TRequest : class where TResponse : class

Что делает: Позволяет реализовать паттерн Request-Response. Клиент отправляет запрос и ожидает ответа от потребителя. Это синхронный паттерн поверх асинхронных сообщений.

// Настройка клиента
var client = bus.CreateRequestClient<OrderRequest, OrderResponse>(new Uri("queue:order-request-queue"));

// Отправка запроса и ожидание ответа
var response = await client.GetResponse<OrderResponse>(new OrderRequest { OrderId = Guid.NewGuid() });
Console.WriteLine($"Статус заказа: {response.Message.Status}");

4.9. ReceiveEndpoint

Сигнатура: (метод конфигурации: cfg.ReceiveEndpoint(string queueName, Action<IReceiveEndpointConfigurator> configure))

Что делает: Определяет точку входа для получения сообщений. Настраивает очередь, связывает её с потребителями, настраивает политики повторных попыток, количество потоков и т.д.

cfg.ReceiveEndpoint("email-queue", e =>
{
    e.Consumer<EmailConsumer>();
    e.UseMessageRetry(r => r.Interval(3, TimeSpan.FromSeconds(5))); // 3 попытки с интервалом 5 сек
    e.PrefetchCount = 10; // Количество сообщений, получаемых за раз
});

4.10. UseMessageRetry

Сигнатура: (метод расширения на IReceiveEndpointConfigurator) void UseMessageRetry(Action<IRetryConfigurator> configure)

Что делает: Настраивает политику повторных попыток для потребителя. Если потребитель выбросил исключение, MassTransit автоматически попытается обработать сообщение снова в соответствии с заданной политикой.

e.UseMessageRetry(r =>
{
    r.Interval(5, TimeSpan.FromSeconds(1)); // 5 попыток с интервалом 1 секунда
    r.Exponential(3, TimeSpan.FromMilliseconds(200), TimeSpan.FromSeconds(30), TimeSpan.FromSeconds(5)); // Экспоненциальная задержка
    r.Incremental(3, TimeSpan.FromSeconds(1), TimeSpan.FromSeconds(2)); // Увеличивающаяся задержка
});

5. Реальный практический пример (100-200 строк кода)

Рассмотрим пример системы обработки заказов в интернет-магазине. У нас есть три микросервиса: OrderService (принимает заказы), PaymentService (обрабатывает платежи) и NotificationService (отправляет уведомления). Они общаются через RabbitMQ с помощью MassTransit.

using System;
using System.Threading.Tasks;
using MassTransit;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;

// ========== Контракты сообщений ==========
public record OrderCreated
{
    public Guid OrderId { get; init; }
    public decimal Amount { get; init; }
    public string CustomerEmail { get; init; }
}

public record PaymentProcessed
{
    public Guid OrderId { get; init; }
    public bool Success { get; init; }
}

public record SendNotification
{
    public string Email { get; init; }
    public string Message { get; init; }
}

// ========== OrderService (Публикует событие) ==========
public class OrderService
{
    private readonly IPublishEndpoint _publishEndpoint;

    public OrderService(IPublishEndpoint publishEndpoint)
    {
        _publishEndpoint = publishEndpoint;
    }

    public async Task CreateOrder(decimal amount, string email)
    {
        var orderId = Guid.NewGuid();
        Console.WriteLine($"[OrderService] Создан заказ {orderId} на сумму {amount}");

        // Публикуем событие о создании заказа
        await _publishEndpoint.Publish(new OrderCreated
        {
            OrderId = orderId,
            Amount = amount,
            CustomerEmail = email
        });
    }
}

// ========== PaymentService (Потребитель OrderCreated, публикует PaymentProcessed) ==========
public class PaymentConsumer : IConsumer<OrderCreated>
{
    private readonly IPublishEndpoint _publishEndpoint;

    public PaymentConsumer(IPublishEndpoint publishEndpoint)
    {
        _publishEndpoint = publishEndpoint;
    }

    public async Task Consume(ConsumeContext<OrderCreated> context)
    {
        var order = context.Message;
        Console.WriteLine($"[PaymentService] Обработка платежа для заказа {order.OrderId}");

        // Симуляция обработки платежа
        bool paymentSuccess = order.Amount < 10000; // Условно: платежи до 10000 успешны
        await Task.Delay(500); // Имитация работы

        // Публикуем результат обработки платежа
        await _publishEndpoint.Publish(new PaymentProcessed
        {
            OrderId = order.OrderId,
            Success = paymentSuccess                    

Рекомендаций