net core开发技巧之消息队列 (二)

2,553 阅读2分钟

这是我参与8月更文挑战的第7天,活动详情查看:8月更文挑战

消息队列

异步

例如,现在有一个订单系统,下单基本都在一个方法里面完成(扣完余额、创建完订单就可)
后来产品加了一个需求。需要在用户点击的下单的时候,发个APP通知一下对方。然后呢,还需要占用商品呀、结算鸭...

总之,现在的代码经过改造后变的十分卡顿。以前下单只需一秒。现在使用下单要等上个4~5秒。

嗯嗯...我们只能通过异步来实现了,现在用户再次下单时,我们先是按以往的流程走,然后将耗时的操作丢到队列里面...队列里的消费者会去慢慢处理(反正一定会处理的吧🤣)。

解耦

上线了订单系统后,由于我们消息服务都是集中放在了一个项目,这天。需求要改一下其中一条短信的参数。改好啦,发布啦。挺容易的... 5分钟后,小x啊,在把刚刚那个短信的参数在改回去吧...

这可不妙,频繁发布项目会导致先生用户体验下降鸭...你一沉思。咦,可以用队列来解决这个问题啊,每次发送消息都只是发给队列,就算是需要更新项目消息内容也能存储下来,上线后继续发送未发送完的短信...nice鸭

Queue

在熟悉消息队列之前,我们先来了解一下C#中的队列:

public record ChatMsg(string Content, string UserId);
public static void Main()
{
    Queue<ChatMsg> chatMsgs = new Queue<ChatMsg>();
    chatMsgs.Enqueue(new ChatMsg("hello wrold...", $"用户{new Random().Next(1, 10)}"));
    chatMsgs.Enqueue(new ChatMsg("hello C#", $"用户{new Random().Next(2, 20)}"));

    Task.Factory.StartNew(() =>
    {
        while (chatMsgs.TryDequeue(out var chat))
        {
            Console.WriteLine("{0} 说:{1}", chat.UserId, chat.Content);
        }
    });
    Console.WriteLine("干别的事...");
    Console.ReadKey();
    
    //output:
    //干别的事...
    //用户5 说:hello wrold...
    //用户14 说:hello C#
}

芜湖,感觉还行。开启了一个新线程在Queue里面取值,如果外界又新增了新消息的话。他还能继续运作

RabbitMQ

其实,简单理解RabbitMQ 的话,它就是在Queue上加了一个储存。以便消费者(取消息的那一方)可以正确的读到消息(不丢失消息)

📢 3.1 通过EasyNetQ(NuGet包)来使用RabbitMQ

PM> Install-Package EasyNetQ

配置中间件:

public void ConfigureServices(IServiceCollection services)
{
    services.AddControllers();

    using var bus = RabbitHutch.CreateBus(Configuration.GetConnectionString("MQ"));
    var subscriber = new AutoSubscriber(bus, "appId");
    subscriber.SubscribeAsync(new[] { Assembly.GetExecutingAssembly() });
    services.AddSingleton(bus);
}

消息实体:


public record ChatMsg(string Content, string UserId);

消费者:

public class ChatMsgConsumer : IConsumeAsync<ChatMsg>
{
    private readonly ILogger<ChatMsgConsumer> _logger;

    public ChatMsgConsumer(ILogger<ChatMsgConsumer> logger)
    {
        _logger = logger;
    }
    [AutoSubscriberConsumer(SubscriptionId = "message.consumer")]
    public Task ConsumeAsync(ChatMsg message, CancellationToken cancellationToken = default)
    {
        _logger.LogInformation("用户{0} 说:{1}", message.UserId, message.Content);
        return Task.CompletedTask;
    }
}

发布者:

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

    [HttpGet]
    public bool SendMessage()
    {
        _bus.PubSub.Publish(new ChatMsg("hello rabbitmq", "用户张三"));
        return true;
    }
}