- 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();