使用 .NET 5 + MassTransit 8.0.1 实现一个分布式事务,并保证最终一致性
WebAPI
readonly ISendEndpointProvider _sendEndpointProvider; public DemoController(ISendEndpointProvider sendEndpointProvider) { _sendEndpointProvider = sendEndpointProvider; } [HttpGet] public async Task<ActionResult> Get(string id) { string message = "Meeeeeeeee" + id; var endpoint = await _sendEndpointProvider.GetSendEndpoint(new Uri("queue:DeductStock_Queue")); await endpoint.Send<DeductStockDto>(new { Id = id, Message = message }); return Ok(); }
Startup
public void ConfigureServices(IServiceCollection services) { services.AddControllersWithViews(); services.AddMassTransit(x => { x.UsingRabbitMq((context, cfg) => { cfg.Host(new Uri("rabbitmq://192.168.214.129/MassTransit_vhost"), c => { c.Username("guest"); c.Password("guest"); }); }); }); services.Configure<MassTransitHostOptions>(options => { options.WaitUntilStarted = true; options.StartTimeout = TimeSpan.FromSeconds(30); options.StopTimeout = TimeSpan.FromMinutes(1); }); }
减库存·
static void Main(string[] args) { Console.Title = "减库存"; var builder = new HostBuilder(); builder.ConfigureServices((hostContext, services) => { services.AddMassTransit(x => { x.AddConsumer<DeductStockConsumer>(); x.AddConsumer<DeductStockErrorConsumer>(); x.UsingRabbitMq((context, cfg) => { cfg.Host(new Uri("rabbitmq://192.168.214.129/MassTransit_vhost"), c => { c.Username("guest"); c.Password("guest"); }); cfg.ReceiveEndpoint("DeductStock_Queue", e => { e.ConfigureConsumer<DeductStockConsumer>(context); }); cfg.ReceiveEndpoint("DeductStock_Queue_error", e => { e.ConfigureConsumer<DeductStockErrorConsumer>(context); }); }); }); services.Configure<MassTransitHostOptions>(options => { options.WaitUntilStarted = true; options.StartTimeout = TimeSpan.FromSeconds(30); options.StopTimeout = TimeSpan.FromMinutes(1); }); }) .Build().Run(); }
DeductStockConsumer
public class DeductStockConsumer : IConsumer<DeductStockDto> { public async Task Consume(ConsumeContext<DeductStockDto> context) { Console.WriteLine("扣库存:" + context.Message.Id); if (context.Message.Id % 10 == 1 && !context.Message.IsBug) { throw new Exception(""); } var endpoint = await context.GetSendEndpoint(new Uri("queue:DeductBalance_Queue")); await endpoint.Send<DeductBalanceDto>(new { Id = context.Message.Id, Message = context.Message.Message + ":DeductStockConsumer", IsBug = context.Message.IsBug, RetryCount = context.Message.RetryCount }); //return Task.CompletedTask; } } public class DeductStockDto { public int Id { get; set; } public int DeductStock { get; set; } public int DeductBalance { get; set; } public string Message { get; set; } public bool IsBug { get; set; } public int RetryCount { get; set; } }
DeductStockErrorConsumer
public class DeductStockErrorConsumer : IConsumer<DeductStockDto> { public async Task Consume(ConsumeContext<DeductStockDto> context) { Console.WriteLine("减库存异常:" + context.Message.Id); if (context.Message.RetryCount >= 5) { throw new Exception(); } context.Message.RetryCount += 1; context.Message.IsBug = true; var endpoint = await context.GetSendEndpoint(new Uri("queue:DeductStock_Queue")); await endpoint.Send<DeductStockDto>(new { Id = context.Message.Id, Message = context.Message.Message + ":DeductStockConsumer", IsBug = context.Message.IsBug, RetryCount = context.Message.RetryCount }); } }
异常重试
e.UseMessageRetry(r => { r.Immediate(20); });
如果出现异常,MassTransit 会自动创建一个错误队列
如果消息未被消费,MassTransit 会自动创建一个死信队列
MassTransit 对异常的消息可以进行重新投递