.net Rabbitmq 封装 包含 直连交换机 死信队列(十一)

Connector 文件夹 代码
public interface IConnectors : IDisposable
{
/// <summary>
/// 初始化交换机队列
/// </summary>
/// <param name="definition"></param>
/// <returns></returns>
Task InitExchangeAndQueueAsync(ExchangeAndQueueDefinition definition);
/// <summary>
/// 获取缓存中的key 将已经初始化的交换机队列的信道IChannel存入
/// 缓存中提高性能
/// </summary>
/// <param name="definition">交换机队列的声明对象</param>
/// <returns></returns>
string GetMemoryCacheKey(ExchangeAndQueueDefinition definition);
/// <summary>
/// 信道
/// </summary>
IChannel Channel { get; }
}
using Microsoft.Extensions.Options;
using RabbitCustorm.Message;
using RabbitCustorm.RabbitMQExtends;
using RabbitMQ.Client;
using System;
using System.Collections.Generic;
using System.Diagnostics.Metrics;
using System.Linq;
using System.Reflection.Metadata.Ecma335;
using System.Runtime.CompilerServices;
using System.Text;
using System.Threading.Tasks;
namespace RabbitCustorm.Connection.Connectors
{
/// <summary>
/// 此类需要单例模式
/// </summary>
public class Connectors : IConnectors
{
private IConnection conn;
private IChannel channel;
private ConnectionFactory factory;
private readonly RabbitOptions rabbitOptions;
public Connectors(IOptions<RabbitOptions> rabbitOptions)
{
this.rabbitOptions = rabbitOptions.Value;
//初始化
Init();
}
/// <summary>
/// 初始化
/// </summary>
private void Init()
{
factory = new ConnectionFactory()
{
UserName = rabbitOptions.UserName,
Password = rabbitOptions.Password,
VirtualHost = rabbitOptions.VirtualHost,
HostName = rabbitOptions.HostName,
Port = rabbitOptions.Port,
DispatchConsumersAsync = true//开启异步消费者
};
this.conn = factory.CreateConnection();
this.channel = conn.CreateChannel();
}
/// <summary>
/// 信道
/// </summary>
public IChannel Channel => this.channel;
/// <summary>
/// 初始化 交换机
/// </summary>
/// <param name="definition"></param>
/// <returns></returns>
public async Task InitExchangeAndQueueAsync(ExchangeAndQueueDefinition definition)
{
if (!this.channel.IsOpen)
{
throw new Exception("信道(channel)未开启!!!");
}
//如果包含 死信 定义
if (definition.Arguments != null && definition.Arguments.Keys.Contains("x-dead-letter-exchange"))
{
//死信交换机
string deadLetterExchange = definition.Arguments["x-dead-letter-exchange"].ToString();
//死信队列
string deadLetterQueue = definition.Arguments["x-dead-letter-queue"].ToString();
//死信路由key
string deadletterRoutingKey = definition.Arguments["x-dead-letter-routing-key"].ToString();
await channel.ExchangeDeclareAsync(exchange: deadLetterExchange, type: ExchangeType.Direct, passive: false, durable: true, autoDelete: false, arguments: null);
await channel.QueueDeclareAsync(queue: deadLetterQueue, passive: false, durable: true, exclusive: false, autoDelete: false, arguments: null);
await channel.QueueBindAsync(queue: deadLetterQueue, exchange: deadLetterExchange, routingKey: deadletterRoutingKey, arguments: null);
}
await channel.ExchangeDeclareAsync(exchange: definition.ExchangeName, type: ExchangeType.Direct, passive: false, durable: true, autoDelete: false, arguments: definition.Arguments);
await channel.QueueDeclareAsync(queue: definition.QueueName, passive: false, durable: true, exclusive: false, autoDelete: false, arguments: definition.Arguments);
await channel.QueueBindAsync(queue: definition.QueueName, exchange: definition.ExchangeName, routingKey: definition.RouteKey, arguments: definition.Arguments);
}
/// <summary>
/// 获取 MemoryCache Key
/// </summary>
/// <param name="definition"></param>
/// <returns></returns>
public string GetMemoryCacheKey(ExchangeAndQueueDefinition definition)
{
string key = $"{definition.ExchangeName}_{definition.QueueName}_{definition.RouteKey}";
//如果是死信队列
if (definition.Arguments != null && definition.Arguments.ContainsKey("x-dead-letter-exchange"))
{
//死信交换机
string deadLetterExchange = definition.Arguments["x-dead-letter-exchange"].ToString();
//死信队列
string deadLetterQueue = definition.Arguments["x-dead-letter-queue"].ToString();
//死信路由key
string deadletterRoutingKey = definition.Arguments["x-dead-letter-routing-key"].ToString();
return key += $"{deadLetterExchange}_{deadLetterQueue}_{deadletterRoutingKey}";
}
return $"{definition.ExchangeName}_{definition.QueueName}_{definition.RouteKey}";
}
public async void Dispose()
{
if (conn != null)
{
await conn.CloseAsync();
}
if (channel != null)
{
await channel.CloseAsync();
}
}
}
}
Consumer 文件夹代码
using RabbitCustorm.Message;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace RabbitCustorm
{
/// <summary>
/// 消费者接口
/// </summary>
public interface IConsumer
{
/// <summary>
/// 接收消息
/// </summary>
/// <param name="definition"><交换机队列声明/param>
/// <param name="func">消息处理委托,返回是否处理成功</param>
void Received(ExchangeAndQueueDefinition definition, Func<string, bool> func);
}
}
using Microsoft.Extensions.Caching.Memory;
using Microsoft.Extensions.Logging;
using Newtonsoft.Json;
using RabbitCustorm.Connection.Connectors;
using RabbitCustorm.Message;
using RabbitMQ.Client;
using RabbitMQ.Client.Events;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Channels;
using System.Threading.Tasks;
namespace RabbitCustorm
{
/// <summary>
/// 消费者
/// </summary>
public class Consumer : IConsumer
{
private readonly IConnectors connectors;
private readonly ILogger<Producer> logger;
private readonly IMemoryCache memoryCache;
public Consumer(IConnectors connectors, ILogger<Producer> logger, IMemoryCache memoryCache)
{
this.connectors = connectors;
this.logger = logger;
this.memoryCache = memoryCache;
}
/// <summary>
/// 接收消息
/// </summary>
/// <param name="definition">交换机和队列定义</param>
/// <param name="func">消息处理函数</param>
public async void Received(ExchangeAndQueueDefinition definition, Func<string, bool> func)
{
//获取 memoryCache Key
string memoryCacheKey = this.connectors.GetMemoryCacheKey(definition);
//判断 内存缓存中是否存在 channel 如果不存在 就存进去
if (!this.memoryCache.TryGetValue<IChannel>(memoryCacheKey, out IChannel channel))
{
//初始化交换机
await this.connectors.InitExchangeAndQueueAsync(definition);
channel = this.connectors.Channel;
this.memoryCache.Set<IChannel>(memoryCacheKey, channel);
}
//var consumer = new EventingBasicConsumer(channel);
//异步消费
var consumer = new AsyncEventingBasicConsumer(channel);
consumer.Received += async (ch, ea) =>
{
// channel.BasicQos(0, 1, false);
string str = Encoding.UTF8.GetString(ea.Body.ToArray(), 0, ea.Body.Length);
if (func(str))
{
//应答
await channel.BasicAckAsync(ea.DeliveryTag, false);
}
else
{
//拒绝消息
await channel.BasicRejectAsync(ea.DeliveryTag, false);
}
};
string consumerTag = this.connectors.Channel.BasicConsume(definition.QueueName, false, consumer);
}
}
}
Message 文件夹 代码
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace RabbitCustorm.Message
{
/// <summary>
/// 交换机 队列 定义
/// </summary>
public class ExchangeAndQueueDefinition
{
/// <summary>
/// 交换机名称
/// </summary>
public string ExchangeName { get; set; }
/// <summary>
/// 队列名称
/// </summary>
public string QueueName { get; set; }
/// <summary>
/// 路由key
/// </summary>
public string RouteKey { get; set; }
/// <summary>
/// 参数
/// </summary>
public Dictionary<string, object> Arguments { get; set; } = null;
}
}
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace RabbitCustorm.Message
{
/// <summary>
/// 声明 交换机名称 队列名称 便于复用
/// </summary>
public static class ExchangeAndQueueStatement
{
public static readonly object lockProducer = new object();
#region 交换机类型 direct 的交换机和队列声明
public const string TestDirectExchange = "TestDirectExchange";
public const string TestDirectQueue = "TestDirectQueue";
public const string TestDirectKey = "TestDirectKey";
#endregion
#region 死信交换机和队列,routekey声明
/// <summary>
/// 死信交换机
/// </summary>
public const string ExchangeDeadLetter = "ExchangeDeadLetter";
/// <summary>
/// 死信队列
/// </summary>
public const string QueueDeadLetter = "QueueDeadLetter";
/// <summary>
/// 死信route key
/// </summary>
public const string RouteDeadLetter = "RouteDeadLetter";
/// <summary>
/// 死信中 正常交换机
/// </summary>
public const string TestExchange = "TestExchange";
/// <summary>
/// 死信中 正常队列
/// </summary>
public const string TestQueue = "TestQueue";
/// <summary>
/// 死信中 正常路由Key
/// </summary>
public const string TestKey = "TestKey";
#endregion
}
}
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace RabbitCustorm.Message
{
/// <summary>
/// 消息传输类
/// </summary>
public class MessageTrans
{
/// <summary>
/// 交换机 队列 定义
/// </summary>
public ExchangeAndQueueDefinition Definition { get; set; }
/// <summary>
/// 消息体
/// </summary>
public object Body { get; set; }
}
}
Producer 文件夹代码
using RabbitCustorm.Message;
using RabbitMQ.Client;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace RabbitCustorm
{
/// <summary>
/// 生产者接口
/// </summary>
public interface IProducer
{
/// <summary>
/// 发消息
/// </summary>
/// <param name="message">消息传输对象</param>
/// <returns></returns>
Task SendMessageAsync(MessageTrans message);
}
}
using Microsoft.Extensions.Caching.Memory; using Microsoft.Extensions.Logging; using Newtonsoft.Json; using RabbitCustorm.Connection.Connectors; using RabbitCustorm.Message; using RabbitMQ.Client; using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Text.Json.Serialization; using System.Threading; using System.Threading.Tasks; namespace RabbitCustorm { /// <summary> /// exchange direct 直连 类型 /// </summary> public class Producer : IProducer { private readonly IConnectors connectors; private readonly ILogger<Producer> logger; private readonly IMemoryCache memoryCache; public Producer(IConnectors connectors, ILogger<Producer> logger, IMemoryCache memoryCache) { this.connectors = connectors; this.logger = logger; this.memoryCache = memoryCache; } public async Task SendMessageAsync(MessageTrans message) { try { //获取 memoryCache key string memoryCacheKey = connectors.GetMemoryCacheKey(message.Definition); //判断 内存缓存中是否存在 channel 如果不存在 就存进去 if (!memoryCache.TryGetValue(memoryCacheKey, out IChannel channel)) { //初始化交换机 await connectors.InitExchangeAndQueueAsync(message.Definition); channel = connectors.Channel; memoryCache.Set(memoryCacheKey, channel); } //发送消息 string body = JsonConvert.SerializeObject(message.Body); byte[] messageBodyBytes = Encoding.UTF8.GetBytes(body); //开启事务 await connectors.Channel.TxSelectAsync(); await channel.BasicPublishAsync(message.Definition.ExchangeName, message.Definition.RouteKey, messageBodyBytes); //事务提交 await connectors.Channel.TxCommitAsync(); logger.LogInformation($"发送消息{body}"); } catch (Exception ex) { //事务回滚 await connectors.Channel.TxRollbackAsync(); logger.LogError($"发送消息失败{ex.Message}", ex); } } } }
RabbitMQExtends 文件夹代码
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace RabbitCustorm.RabbitMQExtends
{
/// <summary>
/// rabbit 配置类
/// </summary>
public class RabbitOptions
{
/// <summary>
/// 用户名
/// </summary>
public string UserName { get; set; }
/// <summary>
/// 密码
/// </summary>
public string Password { get; set; }
/// <summary>
/// 虚拟机主机 例如 center
/// </summary>
public string VirtualHost { get; set; }
/// <summary>
/// rabbitmq 主机 例如 192.168.0.168
/// </summary>
public string HostName { get; set; }
/// <summary>
/// 端口号 5672
/// </summary>
public int Port { get; set; } = 5672;
}
}
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using RabbitCustorm.Connection.Connectors;
using RabbitCustorm;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace RabbitCustorm.RabbitMQExtends
{
public static class RabbitMQExtend
{
/// <summary>
/// 添加 生产者 和 消费者
/// </summary>
/// <param name="services"></param>
/// <param name="config"></param>
/// <returns></returns>
public static IServiceCollection AddRabbitMQ(this IServiceCollection services, IConfiguration config)
{
services.Configure<RabbitOptions>(config.GetSection("RabbitOptions"));
//增加内从缓存注入
services.AddMemoryCache();
services.AddSingleton<IConnectors, Connectors>();
services.AddScoped<IProducer, Producer>();
services.AddScoped<IConsumer, Consumer>();
return services;
}
/// <summary>
/// 添加 rabbit 只有 生产者
/// </summary>
/// <param name="services"></param>
/// <param name="config"></param>
/// <returns></returns>
public static IServiceCollection AddRabbitMQProducer(this IServiceCollection services, IConfiguration config)
{
services.Configure<RabbitOptions>(config.GetSection("RabbitOptions"));
//增加内从缓存注入
services.AddMemoryCache();
services.AddSingleton<IConnectors, Connectors>();
services.AddScoped<IProducer, Producer>();
return services;
}
/// <summary>
/// 添加 rabbit 只有 消费者
/// </summary>
/// <param name="services"></param>
/// <param name="config"></param>
/// <returns></returns>
public static IServiceCollection AddRabbitMQConsumer(this IServiceCollection services, IConfiguration config)
{
services.Configure<RabbitOptions>(config.GetSection("RabbitOptions"));
//增加内从缓存注入
services.AddMemoryCache();
services.AddSingleton<IConnectors, Connectors>();
services.AddScoped<IConsumer, Consumer>();
return services;
}
}
}
以上是rabbitmq 封装类库代码
下面 是测试webapi
appsettings.json
{
"Logging": {
"LogLevel": {
"Default": "Information",
"Microsoft.AspNetCore": "Warning"
}
},
"AllowedHosts": "*",
"RabbitOptions": {
"HostName": "192.168.0.168",
"Port": "5672",
"UserName": "test",
"Password": "test",
"VirtualHost": "center"
}
}
Program.cs
using RabbitCustorm.RabbitMQExtends;
using RabbitMqWebAPI;
using System.Runtime.CompilerServices;
var builder = WebApplication.CreateBuilder(args);
// Add services to the container.
builder.Services.AddControllers();
// Learn more about configuring Swagger/OpenAPI at https://aka.ms/aspnetcore/swashbuckle
builder.Services.AddEndpointsApiExplorer();
builder.Services.AddSwaggerGen();
builder.Services.AddRabbitMQ(builder.Configuration);
//测试直连交换机
builder.Services.AddHostedService<TestDirectService>();
//测试私信队列交换机
builder.Services.AddHostedService<TestDeadLetterService>();
var app = builder.Build();
// Configure the HTTP request pipeline.
if (app.Environment.IsDevelopment())
{
app.UseSwagger();
app.UseSwaggerUI();
}
app.UseHttpsRedirection();
app.UseAuthorization();
app.MapControllers();
app.Run();
TestDeadLetterService
using Microsoft.Extensions.Hosting;
using Microsoft.OpenApi.Writers;
using RabbitCustorm;
using RabbitCustorm.Message;
using RabbitMqWebAPI.Controllers;
namespace RabbitMqWebAPI
{
/// <summary>
/// 测试私信队列
/// </summary>
public class TestDeadLetterService : BackgroundService
{
private readonly ILogger<RabbitConsumerController> logger;
private readonly IServiceScopeFactory scopeFactory;
public TestDeadLetterService(ILogger<RabbitConsumerController> logger, IServiceScopeFactory scopeFactory)
{
this.logger = logger;
this.scopeFactory = scopeFactory;
}
protected override Task ExecuteAsync(CancellationToken stoppingToken)
{
using IServiceScope scope = this.scopeFactory.CreateScope();
IConsumer consumer = scope.ServiceProvider.GetRequiredService<IConsumer>();
Task.Factory.StartNew(() =>
{
consumer.Received(
new ExchangeAndQueueDefinition
{
ExchangeName = ExchangeAndQueueStatement.ExchangeDeadLetter,
QueueName = ExchangeAndQueueStatement.QueueDeadLetter,
RouteKey = ExchangeAndQueueStatement.RouteDeadLetter,
}, body =>
{
this.logger.LogInformation($"收到死信消息{body}");
return true;
});
}, TaskCreationOptions.LongRunning);
return Task.CompletedTask;
}
}
}
TestDirectService
using RabbitCustorm;
using RabbitCustorm.Message;
using RabbitMqWebAPI.Controllers;
namespace RabbitMqWebAPI
{
/// <summary>
/// 测试直连交换机
/// </summary>
public class TestDirectService : IHostedService
{
private readonly ILogger<RabbitConsumerController> logger;
private readonly IServiceScopeFactory scopeFactory;
public TestDirectService(ILogger<RabbitConsumerController> logger, IServiceScopeFactory scopeFactory)
{
this.logger = logger;
this.scopeFactory = scopeFactory;
}
public Task StartAsync(CancellationToken cancellationToken)
{
using AsyncServiceScope asyncService = scopeFactory.CreateAsyncScope();
IConsumer consumer = asyncService.ServiceProvider.GetRequiredService<IConsumer>();
Task.Factory.StartNew(() =>
{
consumer.Received(
new ExchangeAndQueueDefinition
{
ExchangeName = ExchangeAndQueueStatement.TestDirectExchange,
QueueName = ExchangeAndQueueStatement.TestDirectQueue,
RouteKey = ExchangeAndQueueStatement.TestDirectKey,
}, body =>
{
this.logger.LogInformation($"收到消息{body}");
return true;
});
}, TaskCreationOptions.LongRunning);
return Task.CompletedTask;
}
public Task StopAsync(CancellationToken cancellationToken)
{
throw new NotImplementedException();
}
}
}
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Mvc;
using RabbitCustorm;
using RabbitCustorm.Message;
namespace RabbitMqWebAPI.Controllers
{
[Route("api/[controller]")]
[ApiController]
public class RabbitProducerController : ControllerBase
{
private readonly ILogger<RabbitProducerController> logger;
private readonly IProducer producer;
private readonly static object lockobj = new object();
//一次访问资源的线程数量 1个
private static SemaphoreSlim _semaphore = new SemaphoreSlim(1);
public RabbitProducerController(ILogger<RabbitProducerController> logger, IProducer producer)
{
this.logger = logger;
this.producer = producer;
}
/// <summary>
/// 发送直连消息
/// </summary>
/// <param name="body"></param>
/// <returns></returns>
[HttpPost]
public async Task<ActionResult> SendMessage([FromBody] string body)
{
await producer.SendMessageAsync(new MessageTrans
{
Definition = new ExchangeAndQueueDefinition
{
ExchangeName = ExchangeAndQueueStatement.TestDirectExchange,
QueueName = ExchangeAndQueueStatement.TestDirectQueue,
RouteKey = ExchangeAndQueueStatement.TestDirectKey
},
Body = body
});
return Ok("发送成功!");
}
/// <summary>
/// 发送延迟消息
/// </summary>
/// <param name="body"></param>
/// <returns></returns>
[HttpPost("SendDelayMessage")]
public async Task<ActionResult> SendDelayMessage([FromBody] Student body)
{
if (await _semaphore.WaitAsync(1000))
{
await producer.SendMessageAsync(new MessageTrans
{
Definition = new ExchangeAndQueueDefinition
{
ExchangeName = ExchangeAndQueueStatement.TestExchange,
QueueName = ExchangeAndQueueStatement.TestQueue,
RouteKey = ExchangeAndQueueStatement.TestKey,
Arguments = new Dictionary<string, object>
{
["x-message-ttl"] = 12000,
["x-dead-letter-exchange"] = ExchangeAndQueueStatement.ExchangeDeadLetter,
["x-dead-letter-queue"] = ExchangeAndQueueStatement.QueueDeadLetter,
["x-dead-letter-routing-key"] = ExchangeAndQueueStatement.RouteDeadLetter,
}
},
Body = body
});
}
_semaphore.Release();
return Ok("发送成功!");
}
}
}
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Mvc;
using RabbitCustorm;
using RabbitCustorm.Message;
namespace RabbitMqWebAPI.Controllers
{
[Route("api/[controller]")]
[ApiController]
public class RabbitConsumerController : ControllerBase
{
private readonly ILogger<RabbitConsumerController> logger;
private readonly IConsumer consumer;
public RabbitConsumerController(ILogger<RabbitConsumerController> logger, IConsumer consumer)
{
this.logger = logger;
this.consumer = consumer;
}
[HttpGet]
public ActionResult Received()
{
this.consumer.Received(
new ExchangeAndQueueDefinition
{
ExchangeName = ExchangeAndQueueStatement.TestDirectExchange,
QueueName = ExchangeAndQueueStatement.TestDirectQueue,
RouteKey = ExchangeAndQueueStatement.TestDirectKey,
}, body =>
{
this.logger.LogInformation($"收到消息{body}");
return true;
});
return Ok();
}
/// <summary>
/// 接收死信队列消息
/// </summary>
/// <returns></returns>
[HttpGet("ReceivedDeadLetter")]
public ActionResult ReceivedDeadLetter()
{
this.consumer.Received(
new ExchangeAndQueueDefinition
{
ExchangeName = ExchangeAndQueueStatement.ExchangeDeadLetter,
QueueName = ExchangeAndQueueStatement.QueueDeadLetter,
RouteKey = ExchangeAndQueueStatement.RouteDeadLetter
}, body =>
{
this.logger.LogInformation($"收到死信消息{body}");
return true;
});
return Ok();
}
}
}
浙公网安备 33010602011771号