linux 下安装 RabbitMq 及 .net core 实操多种模式
当前系统
Debian GNU/Linux 12
安装命令
1、sudo apt update //更新系统2、sudo apt-get install rabbitmq-server //安装rabbitMq 服务
3、sudo service rabbitmq-server start //启动 rabbitMq
4、sudo systemctl enable rabbitmq-server //设置开机启动
5、sudo rabbitmq-plugins enable rabbitmq_management //启动管理平台 15672
sudo service rabbitmq-server status //查看服务状态
netstat -lnpt | grep 15672 //查看端口是否运行
访问管理平台
默认帐号及密码:guest
异常:User can only log in via localhost
sudo rabbitmqctl add_user admin 123456 //创建 admin 管理员,密码 123456
sudo rabbitmqctl set_user_tags admin administrator //将新用户设置为管理员
sudo rabbitmqctl set_permissions -p / admin ".*" ".*" ".*" //为新用户分配所有权限
注意:新版的 RabbitMq 就算设置了管理员权限,登录后台后很多权限无法操作或显示 404或405,这时必需使用 localhost:15672,如果部署在远程服务器可以使用隧道隐射后在用 localhost 访问权限才显示正常
RabbitMq 缺点及功能特色:
RabbitMq 由于是单线程处理,故性能方面一般;但在一些特殊场景使用,主要用于队列中消息可以多个客户端同时处理,不会对同一条消息进行多次处理;
1、现有 .net 客户端没有异步操作(建议使用单例服务);
2、消息被分配到客户端后,如果未 Ack 操作,可以关闭通道、断开连接、结束客户端进程才会回滚到处理队列;
3、队列消息被客户端获取后,其它客户端获取不到同一条消息;
4、初始化通道时可选是否持久化,创建消息时可以配置是否持久化;
几种常见模式:
RabbitMq 主要是由交换机和队列组织,简单理解就是将消息发送到交换机,由交换机将消息投送到队列中;
fanout 订阅/发布模式,指定一些队列绑定到交换机,消息发送到交换机后,会自动投递到绑该交换机的队列中;
topic 主题模式:两种特殊字符“*”与“#”,用于做模糊匹配,其中“”用于匹配一个单词,“#”用于匹配多个单词。(可以给队列添加多个 routingKey ,添加后必需要绑定交换机),消息发布相对比较慢
direct 简单方式,队列模式就是这种方式,在发布的时候需要指定 队列名,交换机名可以为空;
headers 根据发送的消息内容中的 headers 属性进行匹配,一般用的很少;
C# RabbitMq 常用单例服务代码
using Microsoft.Extensions.Options;
using RabbitMQ.Client;
using RabbitMQ.Client.Events;
using System.Text;
/// <summary> /// 表示amqp客户端 /// </summary> public class RabbitClient : IDisposable { private readonly IOptionsMonitor<RabbitOptions> options; private readonly ConnectionFactory factory; private readonly object syncRoot = new object(); private IConnection? _connection; //持久化消息,需要跟持久化队列共同使用 private IBasicProperties? properties = null; private IModel? _channel; /// <summary> /// 获取Channel /// </summary> private IModel Channel { get { lock (this.syncRoot) { if (this._connection == null) { this._connection = this.factory.CreateConnection(); } if (this._channel == null) { this._channel = this._connection.CreateModel(); if (properties == null) { properties = this._channel.CreateBasicProperties(); properties.Persistent = this.options.CurrentValue.IsDurable; //持久化消息 } //创建默认队列 this._channel.QueueDeclare(this.options.CurrentValue.Queue, // 队列名称 durable: this.options.CurrentValue.IsDurable, //持久化队列 exclusive: false, //是否私有化,false表示所有消费者都可以访问,true表示只有第一次拥有它的消费者才能访问 autoDelete: false, //是否自动删除,true表示不再使用队列时自动删除队列 arguments: null); //其他额外参数 //创建配置交换机及队列 this._channel.ExchangeDeclare( this.options.CurrentValue.ExchangeOptions.Name, //交换机名称 ExchangeType.Fanout, //交换机类型 Fanout:广播模式, this.options.CurrentValue.IsDurable ); //创建主题交换机 this._channel.ExchangeDeclare( this.options.CurrentValue.TopicName, //交换机名称 ExchangeType.Topic, //交换机类型 Fanout:主题模式, this.options.CurrentValue.IsDurable ); //交换机机下创建队列 foreach (var item in this.options.CurrentValue.ExchangeOptions.Queues) { //创建队列 this._channel.QueueDeclare(item, // 队列名称 durable: this.options.CurrentValue.IsDurable, //持久化队列 exclusive: false, //是否私有化,false表示所有消费者都可以访问,true表示只有第一次拥有它的消费者才能访问 autoDelete: false, //是否自动删除,true表示不再使用队列时自动删除队列 arguments: null); //其他额外参数 //绑定队列到交换机 this._channel.QueueBind( item, //队列名称 this.options.CurrentValue.ExchangeOptions.Name, //交换机名称 "" ); //给队列添加routingKey,并绑定 topic交换机 this._channel.QueueBind( item, //队列名称 this.options.CurrentValue.TopicName, //交换机名称 $"#.{item}.#" //路由关键字,发布订阅模式写""即可 ); } } return this._channel; } } } /// <summary> /// amqp客户端 /// </summary> /// <param name="optionsMonitor"></param> public RabbitClient(IOptionsMonitor<RabbitOptions> optionsMonitor) { this.options = optionsMonitor; this.factory = new ConnectionFactory { UserName = options.CurrentValue.UserName, Password = options.CurrentValue.Password, HostName = options.CurrentValue.HostName, Port = options.CurrentValue.Port, AutomaticRecoveryEnabled = true }; } /// <summary> /// 简单模式发布消息 /// </summary> /// <param name="msg">消息内容</param> public void SimplePublish(string msg) { var message = Encoding.UTF8.GetBytes(msg); this.Channel.BasicPublish( "", //交换机名,""表示默认交换机direct this.options.CurrentValue.Queue, //路由键,简单模式就是队列名 properties, //其他额外参数 message //要传递的消息字节数组 ); } /// <summary> /// 交换机模式发布消息 /// </summary> /// <param name="msg">消息内容</param> public void ExchangePublish(string msg) { var message = Encoding.UTF8.GetBytes(msg); //持久化消息,需要跟持久化队列共同使用 this.Channel.BasicPublish( options.CurrentValue.ExchangeOptions.Name, //交换机名,""表示默认交换机direct "", //路由键,简单模式就是队列名 properties, //其他额外参数 message //要传递的消息字节数组 ); } /// <summary> /// 主题模式发布消息 /// </summary> /// <param name="msg"></param> /// <param name="topic"></param> public void TopicPublish(string msg, string? topic = null) { var message = Encoding.UTF8.GetBytes(msg); var routingKey = string.IsNullOrEmpty(topic) ? "anonymous.info" : topic; this.Channel.BasicPublish( this.options.CurrentValue.TopicName, //交换机名,""表示默认交换机direct routingKey, //路由键 properties, //其他额外参数 message //要传递的消息字节数组 ); } /// <summary> /// 提取交换机消息 /// </summary> /// <param name="queue">交换机中的队列名称</param> /// <param name="autoAck"></param> /// <returns></returns> public RabbitMessage? GetExchange(string queue, bool autoAck = false) { var result = this.Channel.BasicGet(queue, autoAck); return result == null ? null : new RabbitMessage(result.DeliveryTag, result.Body); } /// <summary> /// 提取消息 /// </summary> /// <param name="autoAck">是否同时从服务器中删除消息</param> /// <returns></returns> public RabbitMessage? Get(bool autoAck = false) { var result = this.Channel.BasicGet(this.options.CurrentValue.Queue, autoAck); return result == null ? null : new RabbitMessage(result.DeliveryTag, result.Body); } /// <summary> /// 监听简单队列 /// </summary> /// <param name="callback"></param> /// <param name="autoAck"></param> public void SimpleConsume(string queue, Action<RabbitMessage> callback, bool autoAck = false) { var consumer = new EventingBasicConsumer(this.Channel); this.Channel.BasicConsume(queue, autoAck, consumer); consumer.Received += (model, ea) => { var msg = new RabbitMessage(ea.DeliveryTag, ea.Body); callback(msg); }; } /// <summary> /// 确认并删除消息 /// </summary> /// <param name="tag">消息标记</param> public void Ack(ulong tag) { this.Channel.BasicAck(tag, false); } /// <summary> /// 释放资源 /// </summary> public void Dispose() { if (this._channel != null) { this._channel.Dispose(); } if (this._connection != null) { this._connection.Dispose(); } }
/// <summary> /// 表示amqp消息 /// </summary> public class RabbitMessage { /// <summary> /// 标记 /// </summary> public ulong Tag { get; } /// <summary> /// 内容 /// </summary> public byte[] Body { get; } /// <summary> /// amqp消息 /// </summary> /// <param name="tag"></param> /// <param name="body"></param> public RabbitMessage(ulong tag, ReadOnlyMemory<byte> body) { this.Tag = tag; this.Body = body.ToArray(); } /// <summary> /// 转换为字符串 /// </summary> /// <returns></returns> public override string ToString() { return System.Text.Encoding.UTF8.GetString(this.Body); } }
public class RabbitOptions { /// <summary> /// 用户名 /// </summary> public string UserName { get; set; } = string.Empty; /// <summary> /// 密码 /// </summary> public string Password { get; set; } = string.Empty; /// <summary> /// 域名 /// </summary> public string HostName { get; set; } = string.Empty; /// <summary> /// 端口 /// </summary> public int Port { get; set; } = 5672; /// <summary> /// 队列名称 /// </summary> public string Queue { get; set; } = string.Empty; /// <summary> /// 交换机信息 /// </summary> public ExchangeOptions ExchangeOptions { get; set; } = new ExchangeOptions(); /// <summary> /// 是否持久化 /// </summary> public bool IsDurable { get; set; } = false; /// <summary> /// 主题模式名称 /// </summary> public string TopicName { get; set; } = "topic_test"; } /// <summary> /// 交换机信息 /// </summary> public class ExchangeOptions { /// <summary> /// 交换机名称 /// </summary> public string? Name { get; set; } /// <summary> /// 队列集合 /// </summary> public string[] Queues { get; set; } = Array.Empty<string>(); }