RabbitMQ入门(二、RabbitMQ基本发送方式)

  • 本文出现的代码只是简述,详细代码之后文章有涉及。

简单队列

RabbitMQ中的消息都只能存储在Queue中,生产者(下图中的P)生产消息并最终投递到Queue中,消费者(下图中的C)可以从Queue中获取消息并消费。
在这里插入图片描述

工作队列

即单生产者,多个消费者消费队列的情况。
在这里插入图片描述

工作队列–轮询分发

即使一个处理快,一个处理慢,消息队列也还是平均的分发

工作队列–公平分发(fair dipatch)

  • 消费者关闭自动应答,ack改为手动;

    每个消费者发送确认消息之前,消息队列不发送下一个消息到消费者,一次只处理一个消息,限制发送给同一个消费者不得超过一个消息

    生产者(消息队列)

    //保证一次只分发一个
    int prefetchCount=1;
    channel.baseicQos(prefetchCount)
    

    消费者

    //保证一次只分发一个
    int prefetchCount=1;
    channel.baseicQos(prefetchCount);
    channel.basicAck(envelope.getDeliveryTag(),false); 
    boolean autoAck=false;
    

autoAck为true时,为自动确认模式,即一旦rabbitMQ将消息分发给消费者,就会从内存中删除;这种情况下,如果正在执行的消费者线程挂了,就会丢失正在处理的消息;

autoAck为false时,如果有一个消费者挂掉,就会交付给其他消费者,rabbitmq支持消息应答,消费者发送一个消息应答,告诉rabbitmq这个消息我已经处理完成,你可以删了,然后rabbitmq再删除内存中的消息。

消息应答默认是打开的。

消息的持久化

如果rabbitMq挂了,怎么办?

扫描二维码关注公众号,回复: 9390836 查看本文章

这时候,引入持久化。

//声明队列
boolean durable=false;
channel.queueDeclare(QUEUE_NAME,durable,false,false,null);

我们将程序中的boolean durable=false改为true是不可以的,尽管代码是正确的,他也不会成功运行;因为我们已经定义了一个名为QUEUE_NAME的队列,这个队列是未持久化的,rabbitmq不准重新定义(不同参数)一个已存在的队列。

订阅模式(Publish /Subscribe)

在这里插入图片描述
解读:

  • 一个生产者,多个消费者
  • 每个消费者都有自己的队列
  • 生产者没有直接把消息队列发送到队列,而是发送到了交换机、转发器、exchange
  • 每个队列都要绑定到交换机上
  • 生产者发送的消息,经过交换机,到达队列,就能实现一个消息被多个消费者消息

疑问:

  • 如果只声明交换机,未声明队列,此时发送消息,会发现消息丢失了,消息哪去了?

    交换机没有存储的能力,在rabbitMq中只有消息队列有存储的能力,这时候没有队列绑定到交换机上,所以数据丢失了。

Exchange(交换机、转发器)

两方面的操作:接收生产者的消息,同时向队列推送消息

fanout

匿名模式(非路由模式)

//其中第二个参数就为:routingKey
channel.basicPublish(EXCHANGE_NAME,"",null,msg.getBytes())

它会把所有发送到该Exchange的消息路由到所有与它绑定的Queue中。

Routing

路由模式

Direct

在这里插入图片描述
exchangeType为direct,它会把消息路由到那些binding key与routing key完全匹配的Queue中。

Topic exchange

将路由键和某模式匹配。

前面讲到direct类型的Exchange路由规则是完全匹配binding key与routing key,但这种严格的匹配方式在很多情况下不能满足实际业务需求。topic类型的Exchange在匹配规则上进行了扩展,它与direct类型的Exchage相似,也是将消息路由到binding key与routing key相匹配的Queue中,但这里的匹配规则有些不同,它约定:

  • routing key为一个句点号“. ”分隔的字符串(我们将被句点号“. ”分隔开的每一段独立的字符串称为一个单词),如“stock.usd.nyse”、“nyse.vmw”、“quick.orange.rabbit”
  • binding key与routing key一样也是句点号“. ”分隔的字符串
  • binding key中可以存在两种特殊字符“*”与“#”,用于做模糊匹配,其中“ * ”用于匹配一个单词,“ # ”用于匹配多个单词(可以是零个)
    在这里插入图片描述
    (商品:发布、删除、修改…)

RabbitMQ的消息确认机制(事务+confirm)

在rabbitmq中可以通过持久化数据,解决rabbitmq服务器异常的数据丢失问题。

问题:生产者将消息发送出去之后,消息到底有没有到达rabbitmq服务器,默认的情况是不知道的。(之前持久化是解决了:开启自动确认模式时,消费者突然断电的清空)。

两种方式:

  • AMQP实现了事务机制
  • Confirm模式

事务机制

txSelect、txCommit、txRollback

txSelect:用户将当前channel设置成transation模式

txCommit:用于提交事务

txRollback:回滚事务

channel.TxSelect();//将信道设置为事务模式
try
{
    //do something
    var message = Encoding.UTF8.GetBytes("TestMsg");
    channel.BasicPublish("normalExchange", "NormalRoutingKey", true, null, message);
    //do something
    channel.TxCommit();//提交事务
}
catch (Exception ex)
{
    //log(ex);
    channel.TxRollback();
}

事务确实能够解决消息发送方和RabbitMQ之间消息确认的问题,只有消息成功被RabbitMQ接收,事务才能提交成功,否则便可在捕获异常之后进行事务回滚,与此同时可以进行消息重发。但是使用事务同样会带来一些问题。

  • 会阻塞,发布者必须等待broker处理每个消息。
  • 事务是重量级的,每次提交都需要fsync(),需要耗费大量的时间
  • 事务非常耗性能,会降低RabbitMQ的消息吞吐量。

Confirm模式

这里就引入了一种轻量级的方式一发送方确认(publisher confirm)机制。生产者将信道设置成confirm确认模式,一旦信道进入confirm模式,所有在该信道上面发布的消息都会被指派一个唯一的ID( 从1开始),一旦消息被投递到所有匹配的队列之后,RabbitMQ就会发送一个确认(BasicAck) 给生产者(包含消息的唯一ID),这就使得生产者知晓消息已经正确到达了目的地了。如果消息和队列是可持久化的,那么确认消息会在消息写入磁盘之后发出。

Comfirm模式的最大好处就是它是异步的。

开启Confirm模式:

channel.ConfirmSelect();//开启确认模式

编程模式:

  • 普通,发一条,waitForConfirms()
  • 批量的,发一批
  • 异步confirm模式:提供一个回调方法
发布了63 篇原创文章 · 获赞 29 · 访问量 2万+

猜你喜欢

转载自blog.csdn.net/Hpsyche/article/details/100189728