.net core使用RabbitMQ

贾光辉發表於2024-03-12

目錄
  • 1.引言
  • 2.基本概念
  • 3.環境搭建
  • 4.使用

1.引言

RabbitMQ 是一個可靠且成熟的訊息傳遞和流代理,它很容易部署在雲環境、內部部署和本地機器上。它目前被全世界數百萬人使用。

  • 可靠性

    RabbitMQ提供了多種技術可以讓你在效能和可靠性之間進行權衡。這些技術包括永續性機制、投遞確認、釋出者證實和高可用性機制。

  • 靈活的路由

    訊息在到達佇列前是透過交換機進行路由的。RabbitMQ為典型的路由邏輯提供了多種內建交換機型別。如果你有更復雜的路由需求,可以將這些交換機組合起來使用,你甚至可以實現自己的交換機型別,並且當做RabbitMQ的外掛來使用。

  • 叢集

    在相同區域網中的多個RabbitMQ伺服器可以聚合在一起,作為一個獨立的邏輯代理來使用。

  • 聯合

    對於伺服器來說,它比叢集需要更多的鬆散和非可靠連結。為此 RabbitMQ 提供了聯合模型。

  • 高可用的佇列

    在同一個叢集裡,佇列可以被映象到多個機器中,以確保當其中某些硬體出現故障後,你的訊息仍然安全。

  • 多協議

    RabbitMQ支援多種訊息傳遞協議。AMQP是RabbitMQ的核心協議,但是RabbitMQ也支援STOMP、MQTT、HTTP等多種協議。

  • 多語言客戶端

    RabbitMQ提供了多種語言的客戶端,包括Java、.NET、Python、PHP、Ruby、JavaScript、Erlang、Swift、Go等。

  • 管理介面

    RabbitMQ提供了一個易用的管理介面,可以讓你監控和管理RabbitMQ伺服器。

  • 外掛

    RabbitMQ提供了多種外掛,可以讓你擴充套件RabbitMQ的功能。這些外掛包括支援新的協議、新的後端儲存、新的交換機型別、新的工具等。

  • 社群

    RabbitMQ有一個活躍的社群,你可以在這裡找到很多有用的資訊。

  • 可擴充套件性

    RabbitMQ可以很容易地擴充套件,你可以在需要的時候增加或減少伺服器。

2.基本概念

  • 生產者(Producer)

    生產者是一個傳送訊息的程式。傳送訊息的程式可以是任何語言編寫的,只要它能夠連線到RabbitMQ伺服器,並且能夠傳送訊息到RabbitMQ伺服器。

  • 消費者(Consumer)

    消費者是一個接收訊息的程式。接收訊息的程式可以是任何語言編寫的,只要它能夠連線到RabbitMQ伺服器,並且能夠從RabbitMQ伺服器接收訊息。

  • 佇列(Queue)

    佇列是RabbitMQ的內部物件,用於儲存訊息。多個生產者可以向一個佇列傳送訊息,多個消費者可以嘗試從一個佇列接收訊息。佇列支援多種訊息分發策略。

  • 交換機(Exchange)

    交換機是訊息的分發中心。它接收來自生產者的訊息,然後將這些訊息分發給佇列。交換機有多種型別,包括直連交換機、主題交換機、扇形交換機、頭交換機。

  • 繫結(Binding)

    繫結是交換機和佇列之間的關聯關係。繫結可以使用路由鍵進行繫結,也可以使用萬用字元進行繫結。

  • 路由鍵(Routing Key)

    路由鍵是生產者傳送訊息時附帶的一個屬性。路由鍵的作用是決定訊息被分發到哪個佇列。

  • 萬用字元(Wildcard)

    萬用字元是一種模式匹配的方式。RabbitMQ支援兩種萬用字元:`*`和`#`。

  • 繫結鍵(Binding Key)

    繫結鍵是交換機和佇列之間的關聯關係。繫結鍵可以使用路由鍵進行繫結,也可以使用萬用字元進行繫結。

  • 持久化(Durable)

    持久化是指RabbitMQ伺服器重啟後,訊息是否還存在。持久化可以應用到交換機、佇列、繫結、訊息等。

  • 確認機制(Acknowledge)

    確認機制是指消費者接收到訊息後,向RabbitMQ伺服器傳送一個確認訊息。RabbitMQ伺服器收到確認訊息後,會刪除這條訊息。

    • 自動確認

      消費者接收到訊息後,RabbitMQ伺服器會自動刪除這條訊息。

    • 手動確認

      消費者接收到訊息後,需要向RabbitMQ伺服器傳送一個確認訊息。RabbitMQ伺服器收到確認訊息後,會刪除這條訊息。

  • 拒絕機制(Reject)

    拒絕機制是指消費者接收到訊息後,向RabbitMQ伺服器傳送一個拒絕訊息。RabbitMQ伺服器收到拒絕訊息後,會將這條訊息重新傳送給其他消費者。

  • 死信佇列(Dead Letter Queue)

    死信佇列是指訊息被拒絕、過期或者達到最大重試次數後,會被髮送到死信佇列。

  • 訊息過期(Message TTL)

    訊息過期是指訊息在指定時間內沒有被消費者消費,會被刪除。

  • 訊息優先順序(Message Priority)

    訊息優先順序是指訊息在佇列中的優先順序。訊息優先順序高的訊息會被優先消費。

  • 訊息分發

    訊息分發是指訊息在佇列中的分發策略。訊息分發策略包括輪詢分發、公平分發、負載均衡分發。

3.環境搭建

  • Docker 安裝 RabbitMQ

    docker run -d --name rabbitmq -p 5672:5672 -p 15672:15672 --restart=always --hostname my-rabbit -e RABBITMQ_DEFAULT_USER=admin -e RABBITMQ_DEFAULT_PASS=admin -e TZ=Asia/Shanghai rabbitmq:management
    
    • -d:後臺執行
    • --restart:重啟策略
    • --name:容器名稱
    • -p:埠對映
    • --hostname:主機名
    • -e:環境變數
      • RABBITMQ_DEFAULT_USER:預設使用者名稱
      • RABBITMQ_DEFAULT_PASS:預設密碼
      • TZ:時區
    • rabbitmq:management:映象名稱
  • Docker Compose 安裝 RabbitMQ

    version: "3.1"
    services:
        rabbitmq:
            restart: always
            image: rabbitmq:management
            container_name: rabbitmq
            hostname: my-rabbit
            ports:
                - 5672:5672
                - 15672:15672 # RabbitMQ管理介面埠
            environment:
                TZ: Asia/Shanghai
                RABBITMQ_DEFAULT_USER: admin
                RABBITMQ_DEFAULT_PASS: admin
    
    • restart:重啟策略
    • image:映象名稱
    • container_name:容器名稱
    • hostname:主機名
    • ports:埠對映
    • environment:環境變數
      • TZ:時區
      • RABBITMQ_DEFAULT_USER:預設使用者名稱
      • RABBITMQ_DEFAULT_PASS:預設密碼
    • rabbitmq:management:映象名稱

4.使用

客戶端SDK程式碼在GitHub:https://github.com/Tangtang1997/IKunLibrary

  • 新建 TestRequest 類,實現 IRabbitMqRequest 介面,定義訊息體
public class TestRequest : IRabbitMqRequest
{
    /// <summary>
    /// 重試次數
    /// </summary>
    public int RetryCount { get; set; }

    #region 自定義欄位

    /// <summary>
    /// id
    /// </summary>
    public string Id { get; set; } = default!;

    /// <summary>
    /// 名稱
    /// </summary>
    public string Name { get; set; } = default!;

    /// <summary>
    /// 年齡
    /// </summary>
    public int Age { get; set; }

    #endregion
}
  • 新建TestRequestHandler類,實現IRabbitMqRequestHandler<TestRequest>介面,處理訊息
public class TestRequestHanlder : IRequestProcessorHandler<TestRequest>
{
    private readonly ILogger<TestRequestHanlder> _logger;

    public TestRequestHanlder(ILogger<TestRequestHanlder> logger)
    {
        _logger = logger;
    }

    public Task StartAsync(CancellationToken cancellationToken)
    {
        return Task.CompletedTask;
    }

    public Task StopAsync(int milliseconds, CancellationToken cancellationToken = default)
    {
        return Task.CompletedTask;
    }

    public async Task HandleAsync(TestRequest request, CancellationToken cancellationToken = default)
    {
        _logger.LogInformation($"開始處理訊息: {request.Id}");

        //模擬處理訊息耗時操作
        await Task.Delay(1000, cancellationToken);

        _logger.LogInformation($"訊息處理完成: {request.Id}");
    }
}
  • 使用 IHostedService 來託管服務
public class SampleHostedService : IHostedService
{
    private readonly IConsumerProcessorManager<TestRequest> _consumerProcessorManager;
    private readonly IHostApplicationLifetime _applicationLifetime;
    private readonly ILogger<SampleHostedService> _logger;

    public SampleHostedService(
        IConsumerProcessorManager<TestRequest> consumerProcessorManager,
        IHostApplicationLifetime applicationLifetime,
        ILogger<SampleHostedService> logger)
    {
        _consumerProcessorManager = consumerProcessorManager;
        _applicationLifetime = applicationLifetime;
        _logger = logger;
    }

    public async Task StartAsync(CancellationToken cancellationToken)
    {
        _applicationLifetime.ApplicationStarted.Register(() =>
        {
            _logger.LogInformation("SampleHostedService is starting.");
            _consumerProcessorManager.StartAsync(cancellationToken);
        });

        _applicationLifetime.ApplicationStopping.Register(() =>
        {
            _logger.LogInformation("SampleHostedService is stopping.");
            _consumerProcessorManager.StopAsync(3000, cancellationToken);
        });

        await Task.CompletedTask;
    }

    public async Task StopAsync(CancellationToken cancellationToken)
    {
        await Task.CompletedTask;
    }
}
  • 註冊並啟用服務
IHost host = Host.CreateDefaultBuilder(args)
    .ConfigureServices(services =>
    {
        services.AddHostedService<SampleHostedService>();

        var configuration = services.BuildServiceProvider().GetRequiredService<IConfiguration>();

        var hostName = configuration["RabbitMq:Host"] ?? throw new Exception("HostName is not configured");
        var port = int.Parse(configuration["RabbitMq:Port"] ?? throw new Exception("Port is not configured"));
        var userName = configuration["RabbitMq:Username"] ?? throw new Exception("Username is not configured");
        var password = configuration["RabbitMq:Password"] ?? throw new Exception("Password is not configured");
        var queueName = configuration["RabbitMq:QueueName"] ?? throw new Exception("QueueName is not configured");

        services.AddRabbitMq<TestRequest, TestRequestHanlder>(options =>
        {
            options.UseSsl = false;
            options.HostName = hostName;
            options.Port = port;
            options.UserName = userName;
            options.Password = password;
            options.Durable = true;
            options.NetworkRecoveryInterval = 10000;
            options.ExchangeType = ExchangeType.Direct;
            options.QueueName = queueName;
            options.Exchange = $"{queueName}_SERVICE_EXCHANGE";
            options.RoutingKey = $"{queueName}_ROUTING_KEY";
            options.DeadLetterExchange = $"{queueName}_SERVICE_EXCHANGE_DEAD";
            options.DeadLetterQueueName = $"{queueName}_DEAD";
            options.DeadLetterRoutingKey = $"{queueName}_ROUTING_KEY";
        });
    })
    .Build();

await host.RunAsync();

相關文章