14.分布式事件总线MassTransit的简单使用

简介:

        MassTransit,直译公共交通, 是由 Chris Patterson 开发的基于消息驱动的.NET 分布式应用框架,其核心思想是借助消息来实现服务之间的松耦合异步通信,进而确保应用更高的可用性、可靠性和可扩展性。通过对消息模型的高度抽象,以及对主流的消息代理(包括RabbitMQ、ActiveMQ、Kafaka、Azure Service Bus、Amazon SQS等)的集成,大大简化了基于消息驱动的开发门槛,同时内置了连接管理、消息序列化和消费者生命周期管理,以及诸如重试、限流、断路器等异常处理机制,让开发者更好的专注于业务实现。
       简而言之,MassTransit实现了消息代理透明化。无需面向消息代理编程进行诸如连接管理、队列的申明和绑定等操作,即可轻松实现应用间消息的传递和消费。

官网:Concepts · MassTransit

新添加一个项目EventBus.MassTraint(http://localhost:5129)【发布】

安装包:

MassTransit

MassTransit.RabbitMQ

Microsoft.AspNetCore.OpenApi

Swashbuckle.AspNetCore

 

添加一个MQ配置类RabbitMQOptions

public class RabbitMQOptions
{
    /// <summary>
        /// Default password (value: "guest").
        /// </summary>
        /// <remarks>PLEASE KEEP THIS MATCHING THE DOC ABOVE.</remarks>
        public const string DefaultPass = "guest";

        /// <summary>
        /// Default user name (value: "guest").
        /// </summary>
        /// <remarks>PLEASE KEEP THIS MATCHING THE DOC ABOVE.</remarks>
        public const string DefaultUser = "guest";

        /// <summary>
        /// Default virtual host (value: "/").
        /// </summary>
        /// <remarks> PLEASE KEEP THIS MATCHING THE DOC ABOVE.</remarks>
        public const string DefaultVHost = "/";
        
        /// <summary>
        /// The host to connect to.
        /// If you want connect to the cluster, you can assign like “192.168.1.111,192.168.1.112”
        /// </summary>
        public string HostName { get; set; } = "localhost";

        /// <summary>
        /// Password to use when authenticating to the server.
        /// </summary>
        public string Password { get; set; } = DefaultPass;

        /// <summary>
        /// Username to use when authenticating to the server.
        /// </summary>
        public string UserName { get; set; } = DefaultUser;

        /// <summary>
        /// Virtual host to access during this connection.
        /// </summary>
        public string VirtualHost { get; set; } = DefaultVHost;

    
        /// <summary>
        /// The port to connect on.
        /// </summary>
        public ushort Port { get; set; }

        
}

 

appsettings.Development.json配置

{
  "Logging": {
    "LogLevel": {
      "Default": "Information",
      "Microsoft.AspNetCore": "Warning"
    }
  },
  "RabbitMQ": {
    "HostName": "127.0.0.1",
    "VirtualHost": "/",
    "UserName": "admin",
    "Password": "你的密码",
    "Port": "5672"
  }
}

 

Program.cs注入

var builder = WebApplication.CreateBuilder(args);

// Add services to the container.

builder.Services.AddControllers();
builder.Services.AddSwaggerGen();

var rabbitConfig = builder.Configuration.GetSection("RabbitMQ");
builder.Services.Configure<RabbitMQOptions>(rabbitConfig);
var rabbitOptions = rabbitConfig.Get<RabbitMQOptions>();

builder.Services.AddMassTransit(p =>
{
    p.UsingRabbitMq((context, mqConfig) =>
    {
        mqConfig.Host(
            host: rabbitOptions.HostName, // 配置主机地址
            port: rabbitOptions.Port,
            virtualHost: rabbitOptions.VirtualHost, // 虚拟主机
            configure: hostConfig =>
            {
                hostConfig.Username(rabbitOptions.UserName);
                hostConfig.Password(rabbitOptions.Password);
            });
        mqConfig.ConfigureEndpoints(context);
    });
});


var app = builder.Build();

if (app.Environment.IsDevelopment())
{
    app.UseSwagger();
    app.UseSwaggerUI();
}

app.UseHttpsRedirection();

app.UseAuthorization();

app.MapControllers();

app.Run();

添加一个发布的控制器PublishController

[Route("[controller]/[action]")]
[ApiController]
public class PublishController:ControllerBase
{
    private readonly IBus _bus;

    public PublishController(IBus bus)
    {
        _bus = bus;
    }

    // 生产者、发布。执行1.0
    [HttpGet]
    public ActionResult PublishMsg()
    {
        _bus.Publish(new UserInfo {Id = 1, NickName = "张三"});
        return Ok("发布成功");
    }
    
//批量请求接口 [HttpGet]
public ActionResult PublishBatchMsg() { _bus.Publish(new BatchMsg {Id = Thread.CurrentThread.ManagedThreadId}); return Ok("发布成功"); } }

 

 

 

 

新添加一个项目EventBus.MassTransit.Subscribe(http://localhost:5166)【订阅】

安装包:

MassTransit.RabbitMQ

Microsoft.AspNetCore.OpenApi

Swashbuckle.AspNetCore

 

MQ配置类RabbitMQOptions照抄下来

public class RabbitMQOptions
{
    /// <summary>
        /// Default password (value: "guest").
        /// </summary>
        /// <remarks>PLEASE KEEP THIS MATCHING THE DOC ABOVE.</remarks>
        public const string DefaultPass = "guest";

        /// <summary>
        /// Default user name (value: "guest").
        /// </summary>
        /// <remarks>PLEASE KEEP THIS MATCHING THE DOC ABOVE.</remarks>
        public const string DefaultUser = "guest";

        /// <summary>
        /// Default virtual host (value: "/").
        /// </summary>
        /// <remarks> PLEASE KEEP THIS MATCHING THE DOC ABOVE.</remarks>
        public const string DefaultVHost = "/";
        
        /// <summary>
        /// The host to connect to.
        /// If you want connect to the cluster, you can assign like “192.168.1.111,192.168.1.112”
        /// </summary>
        public string HostName { get; set; } = "localhost";

        /// <summary>
        /// Password to use when authenticating to the server.
        /// </summary>
        public string Password { get; set; } = DefaultPass;

        /// <summary>
        /// Username to use when authenticating to the server.
        /// </summary>
        public string UserName { get; set; } = DefaultUser;

        /// <summary>
        /// Virtual host to access during this connection.
        /// </summary>
        public string VirtualHost { get; set; } = DefaultVHost;

    
        /// <summary>
        /// The port to connect on.
        /// </summary>
        public ushort Port { get; set; }

        
}

 

 

appsettings.Development.json配置

{
  "Logging": {
    "LogLevel": {
      "Default": "Information",
      "Microsoft.AspNetCore": "Warning"
    }
  },
  "RabbitMQ": {
    "HostName": "127.0.0.1",
    "VirtualHost": "/",
    "UserName": "admin",
    "Password": "你的密码",
    "Port": "5672"
  }
}

 

Program.cs注入

var builder = WebApplication.CreateBuilder(args);

// Add services to the container.

builder.Services.AddControllers();
builder.Services.AddSwaggerGen();
var rabbitConfig = builder.Configuration.GetSection("RabbitMQ");
builder.Services.Configure<RabbitMQOptions>(rabbitConfig);
var rabbitOptions = rabbitConfig.Get<RabbitMQOptions>();

builder.Services.AddMassTransit(p =>
{
   
    // 批量注册消费者,类名以Consumer结尾和此类继承IConsumer接口,才能注册
    p.AddConsumers(t=>t.Name.EndsWith("Consumer") && t.GetInterface("IConsumer")!=null
        ,Assembly.GetExecutingAssembly());

    // 添加消费者与ConsumerDefinition
    // p.AddConsumer<SubscribeConsumer, SubscribeConsumerDefinition>(p =>
    // {
//     //里面可以做并发数,消息,过滤,限流,超时,事务,重试,熔断,并发限制,延迟
// p.UseMessageRetry(r=>r.Interval(5,TimeSpan.FromSeconds(3))); // // });
//批量请求处理数设置
// p.AddConsumer<BatchMsgConsumer>(b => // { // b.Options<BatchOptions>(opt => // { // opt.SetMessageLimit(30); // }); // }); p.UsingRabbitMq((context, mqConfig) => { mqConfig.Host( host: rabbitOptions.HostName, // 配置主机地址 port: rabbitOptions.Port, virtualHost: rabbitOptions.VirtualHost, // 虚拟主机 configure: hostConfig => { hostConfig.Username(rabbitOptions.UserName); hostConfig.Password(rabbitOptions.Password); }); mqConfig.ConfigureEndpoints(context); }); }); var app = builder.Build(); // Configure the HTTP request pipeline. if (app.Environment.IsDevelopment()) { app.UseSwagger(); app.UseSwaggerUI(); } app.UseAuthorization(); app.MapControllers(); app.Run();

 

 

添加一个类SubscribeConsumer用于消费PublishController控制器里的PublishMsg方法。

// 消费者、订阅。执行1.1
// Message : 只能是接口,记录(record),类, 换句话说,只能是引用类型
public class SubscribeConsumer:IConsumer<UserInfo>
{
    // 由MQ自动调用
    public Task Consume(ConsumeContext<UserInfo> context)
    {
        UserInfo user = context.Message;
        Console.WriteLine(user.NickName);
        return Task.CompletedTask;
    }
}

 

批量处理

添加一个类,用于批量处理类型。

/// <summary>
/// MassTransit 批量处理消息的类型
/// </summary>
[EntityName("batch-msg")]
public class BatchMsg
{
    public int Id { get; set; }
}

 

消费、订阅

/// <summary>
/// 批量处理消息
/// </summary>
public class BatchMsgConsumer:IConsumer<Batch<BatchMsg>>
{
    public Task Consume(ConsumeContext<Batch<BatchMsg>> context)
    {
        foreach (var consumeContext in context.Message)
        {
            Console.WriteLine(consumeContext.Message.Id);
        }

        Console.WriteLine($"{context.Message.Length}===================================");
        
        return Task.CompletedTask;
    }
}

 

打开jmeter工具做批量请求。

设置批量请求数,此方法内可以做并发数,消息,过滤,限流,超时,重试,事务,熔断,并发限制,延迟等。

public class BatchMsgConsumerDefinition:ConsumerDefinition<BatchMsgConsumer>
{
    protected override void ConfigureConsumer(IReceiveEndpointConfigurator endpointConfigurator
        , IConsumerConfigurator<BatchMsgConsumer> consumerConfigurator,
        IRegistrationContext context)
    {
        // endpointConfigurator.ConcurrentMessageLimit = 8;// 并发数据限制,默认是1
        // endpointConfigurator.PrefetchCount = 5; // 每次同时可以执行多少条(取决于CPU的核数)
        
        consumerConfigurator.Options<BatchOptions>(opt =>
        {
            opt.MessageLimit = 30;
        });
    }
}

 

重试

 

// 因为Program中AddConsumers 会将这个ConsumerDefinition一起添加至容器中来
public class SubscribeConsumerDefinition:ConsumerDefinition<SubscribeConsumer>
{
    protected override void ConfigureConsumer(IReceiveEndpointConfigurator endpointConfigurator,
        IConsumerConfigurator<SubscribeConsumer> consumerConfigurator,
        IRegistrationContext context)
    {
        // 重试(可以解决数据库/网络异常   等 短暂性的异常),重试2次,一秒执行一次
        endpointConfigurator.UseMessageRetry(r=>r.Interval(2,TimeSpan.FromSeconds(1)));

        /**
         * 如果执行失败,则会先进行重试,如果重试次数用完了,则会在设置的时间内进行消息重发
         * 使用场景:为了防止服务器宕机的情况
         */
        // endpointConfigurator.UseDelayedRedelivery(r=>r.Interval(1,TimeSpan.FromSeconds(5)));
        
    }
}

 

脏数据,补偿机制

 

// 如果消息重试之后,还是执行失败,则会进入此类来
public class SubscribeFaultConsumer:IConsumer<Fault<UserInfo>>
{
    // 我们可以在这里做一些补偿,或者记录/存储这条失败的数据
    public Task Consume(ConsumeContext<Fault<UserInfo>> context)
    {
        var user = context.Message.Message;
        Console.WriteLine(user.Id);
        return Task.CompletedTask;
    }
}

 

posted @ 2024-03-01 11:51  野码  阅读(202)  评论(0编辑  收藏  举报