就算笔记写的再差,也要自己写, 写了感觉就是自己的,其他优秀的资料看到最多,感觉也不是自己的
第一步,创建连接和通道
第二步,声明交换机
channel.ExchangeDeclare(exchange: EXCHANGE_NAME, type: exchange_type);
public static void ExchangeDeclare(this IModel model, string exchange, string type, bool durable = false, bool autoDelete = false, IDictionary<string, object> arguments = null)
{
model.ExchangeDeclare(exchange, type, durable, autoDelete, arguments);
}
声明方法的五个参数
string exchange :名称
string type, 类型,参考我上一篇文章
bool durable ,是否持久化,durable设置为true表示持久化,反之是非持久化,持久化的可以将交换器存盘,在服务器重启的时候会从盘里重新加载数据,因此数据不会丢失
bool autoDelete ,是否自动删除,设置为TRUE则表是自动删除,自删除的前提是至少有一个队列或者交换器与这交换器绑定,之后所有与这个交换器绑定的队列或者交换器都与此解绑,一般都设置为fase
IDictionary<string, object> arguments ,alternate-exchange等一些结构化参数
第三步绑定队列
声明队列
if (!IsFanout)
{
this.channel.QueueDeclare(queue: queueName,
//队列名称
durable: true,
//是否持久化, 队列的声明默认是存放到内存中的,如果rabbitmq重启会丢失,如果想重启之后还存在就要使队列持久化,保存到Erlang自带的Mnesia数据库中,当rabbitmq重启之后会读取该数据库
exclusive: false,
//是否排外的,有两个作用,一:当连接关闭时connection.close()该队列是否会自动删除;二:该队列是否是私有的private,如果不是排外的,可以使用两个消费者都访问同一个队列,没有任何问题,如果是排外的,会对当前队列加锁,其他通道channel是不能访问的,如果强制访问会报异常:com.rabbitmq.client.ShutdownSignalException: channel error; protocol method: #method<channel.close>(reply-code=405, reply-text=RESOURCE_LOCKED - cannot obtain exclusive access to locked queue 'queue_name' in vhost '/', class-id=50, method-id=20)一般等于true的话用于一个队列只能有一个消费者来消费的场景
autoDelete: false,
//是否自动删除,当最后一个消费者断开连接之后队列是否自动被删除,可以通过RabbitMQ Management,查看某个队列的消费者数量,当consumers = 0时队列就会自动删除
arguments: null);
//队列中的消息什么时候会自动被删除?
}
else
{
queueName = channel.QueueDeclare().QueueName;
routingKey = "";
}
绑定队列
public static void QueueBind(this IModel model, string queue, string exchange, string routingKey, IDictionary<string, object> arguments = null)
{
model.QueueBind(queue, exchange, routingKey, arguments);
}
queue:队列名
exchange:交换机
routingKey:路由key
arguments
第四步 流量设置
void BasicQos(uint prefetchSize, ushort prefetchCount, bool global);
配置Basic内容类的QoS参数。
第一个参数是可接收消息的大小的 0不受限制
第二个参数是处理消息最大的数量 1 那如果接收一个消息,但是没有应答,则客户端不会收到下一个消息,消息只会在队列中阻塞
第三个参数则设置了是不是针对整个Connection的,因为一个Connection可以有多个Channel,如果是false则说明只是针对于这个Channel的。
basic.qos是针对channel进行设置的,也就是说只有在channel建立之后才能发送basic.qos信令。
在rabbitmq的实现中,每个channel都对应会有一个rabbit_limiter进程,当收到basic.qos信令后,在rabbit_limiter进程中记录信令中prefetch_count的值,同时记录的还有该channel未ack的消息个数。
第五步,发送者类型
// 1种
EventingBasicConsumer consumer = new EventingBasicConsumer(this.channel);
// 2.种
BasicGetResult result = channel.BasicGet(queue:"routingKey",autoAck:true);
EventingBasicConsumer是基于长连接,发布订阅模式的消费方式,节省资源且实时性好,这是开发中最常用的消费模式。
在一些需要消费者主动获取消息的场合,我们可以使用Get方式,Get方式是基于短连接的,请求响应模式的消费方式,也就是说只有消费者这边主动请求时才获取数据
第六步 处理接收到的信息
// 订阅事件
consumer.Received += (model, bdea) =>
{
};
第七步,消费者处理
public static string BasicConsume(this IModel model, string queue, bool autoAck, IBasicConsumer consumer)
{
return model.BasicConsume(queue, autoAck, "", noLocal: false, exclusive: false, null, consumer);
}
queue 队列名
autoAck 是否自动确认消息,true自动确认,false 不自动要手动调用,建立设置为false
consumer 发送者方式
具体实现
// Func<RbtMessage, bool> func,
public void BindReceiveMqMsg(Func<RbtMessage, bool> func, ICallbackFunction function, string routingKey, string EXCHANGE_NAME = null, string exchange_type = null)
{
try
{
//创建与指定端点的连接。
connection = RbtMQConnection._connectionFactory.CreateConnection();
//创建并返回新的频道,会话和模型。
channel = connection.CreateModel();
bool IsFanout = false;//广播方式
if (exchange_type == ExchangeType.Fanout)
{
//广播方式
IsFanout = true;
EXCHANGE_NAME = EXCHANGE_NAME ?? "mst_exchange_fanout";
}
else
{
EXCHANGE_NAME = EXCHANGE_NAME ?? "mst_exchange_topic";
exchange_type = ExchangeType.Topic;
}
channel.ExchangeDeclare(exchange: EXCHANGE_NAME, type: exchange_type);
if (!IsFanout)
{
this.channel.QueueDeclare(queue: routingKey,//队列名称
durable: true,
arguments: null);
}
else
{
routingKey = channel.QueueDeclare().QueueName;
}
channel.QueueBind(routingKey, EXCHANGE_NAME, IsFanout ? EXCHANGE_NAME : "");
EventingBasicConsumer consumer = new EventingBasicConsumer(this.channel);
consumer.Received += (model, bdea) =>
{
byte[] body = bdea.Body.ToArray();
string message = Encoding.UTF8.GetString(body);
if (message == null || message.Length == 0)
{
this.channel.BasicAck(deliveryTag: bdea.DeliveryTag, multiple: false);
return;
}
RbtMessage mqMsg = JsonConvert.DeserializeObject<RbtMessage>(message);
function.ProcessMsgAsync(mqMsg);
bool result = true;
if (func != null)
{
result = func(mqMsg);
}
if (result)
{
channel.BasicAck(deliveryTag: bdea.DeliveryTag, multiple: false);
}
};
channel.BasicConsume(queue: routingKey, autoAck: false, consumer: consumer);
}
catch (Exception ex)
{
// MstCore.Pub.MstPub.Logs("RabbitMQ出错:" + ex.ToString());
}
}
rabbmitmq能写的还有很多,但工作的原因,怕不写就忘了,而且现在也只是了解调的阶段。所以虽然笔记是流水账,也要记录下来