rabbitMQ基础

什么是rabbitmq?

1184221-20170701113208946-1950151700.png

消息传输流程

Producer —> exchange —> queue —> consumer

消息生产者将消息发送给exchange,exchange根据不同的路由算法将消息发给相应的队列quueue,队列在消息没有正常消费时负责消息的缓存,当队列与consumer直接连接畅通时,队列将消息发送给consumer

一个broker中会存在多个exchange和多个queue,一个exchange是如何决定要把一条消息发送给哪个queue呢?首先exchange只会把消息发送给跟它建立绑定的队列,然后一个exchange可能会有多个queue与其建立绑定关系,那么exchange是如何决定把消息发送给跟它绑定的哪个queue呢?这就由路由算法来决定。这里需要说明exchange有不同的exchange type,每个exchange type的路由算法是不相同的,消息生产者在发送消息时,会同时发送一个routingkey,最终消息会发给哪些queue,这就由相应的exchang type对应的路由算法和routing key共同决定。

Binding key是由consumer在binding Exchange与Queue时决定的,而Routing key是由producer指定。而它们的匹配方式是由exchange TYPE决定的

rabbitmq发送消息流程

  • 创建连接
factory.setHost("10.151.178.7**");
factory.setUsername("test");
factory.setPassword("test");                
factory.setPort(8123);                                
Connection conn = factory.newConnection();
  • 创建channel
Channel channel = conn.createChannel();
  • 声明交换机exchange
channel.exchangeDeclare(EXCHANGE_NAME, "direct",true);
  • 发送消息
String message = "hello";        
channel.basicPublish(EXCHANGE, "queue3", null, message.getBytes()); // 第2个参数为routingkey

rabbitmq接收消息流程

  • 创建连接
factory.setHost("10.151.178.7**");
factory.setUsername("test");
factory.setPassword("test");                
factory.setPort(8123);                                
Connection conn = factory.newConnection();
  • 创建channel
Channel channel = conn.createChannel();      
  • 声明交换机exchange
channel.exchangeDeclare(EXCHANGE_NAME, "direct",true);
  • 声明队列queue
channel.queueDeclare(QUEUE_NAME, false, false, false, null);
  • 绑定交换机&队列
channel.queueBind(QUEUE_NAME,EXCHANGE,"queue3"); // 第3个参数为bindingkey
  • 设置每个消费者可接收的未应答消息个数(一旦有N个消息还没有ack,那么这个consumer将会block)
channel.basicQos(2);
  • 声明消费者
QueueingConsumer consumer = new QueueingConsumer(channel);
  • 为消费者指定消费队列
boolean autoAck = false;
channel.basicConsume(QUEUE_NAME, autoAck, consumer);
  • 消费消息&应答
QueueingConsumer.Delivery delivery = consumer.nextDelivery();
String message = new String(delivery.getBody());
channel.basicAck(delivery.getEnvelope().getDeliveryTag(), false);

相关概念介绍

Exchange

每个Virtual Host包含0或多个Exchange。Exchange负责把Message转发到Queue。每个Exchange可以有0或者多个Queue。每个Queue只能监听1个Exchange。

Queue

Queue即消息队列,负责存储Exchange转发过来的Message。注意,是Queue,所以Message是First-In-First-Out。当一个队列拥有多个消费者时,每条消息只会发送给一个消费者,只有当这个消费者死机或者挂掉的情况,这条消息才会被重新发送给其他消费者。

Binding

Binding指的是Exchange根据规则作出消息转发决策的过程。Message到达Exchange,Exchange此时并不知道Message应该被转发到哪些Queue,然后Exchange根据规则对Message进行Binding决策,Binding完成之后,Exchange根据Binding的结果将Message转发到正确的Queues。

Routing Key

每个Message一般来说必须指定一个Routing Key,Exchange根据Message的Routing Key进行Binding,然后完成Message的转发。

Binding Key

每个Queue一般来说必须指定一个Binding Key。 Binding的过程其实就是根据一定的规则判定Message的Routing Key是否与Queue的Binding Key匹配,如果匹配,则转发Message到Queue。如果Message的Routing Key与多个Queue的Binding Key匹配,则所有匹配的Queue都会收到该Message。

Exchange type, Exchange Type决定了Binding的匹配规则

  • 默认交换机(default exchange)
    由消息代理预先声明好的没有名字(名字为空字符串)的直连交换机(direct exchange)。它有一个特殊的属性,使得它对于简单应用特别有用处:每个新建队列(queue)都会自动绑定到默认交换机上,binding key 的名称与队列名称相同。
  • Direct (直连交换机)
    直连交换机,根据消息携带的路由键(routing key)将消息投递给对应的队列。工作原理:一个队列绑定到某个交换机上时的binding key为R, 当一个携带着routing key为R的消息被发送给直连交换机时,交换机会把它路由给绑定值同样为R的队列。
  • Fanout (扇型交换机)
    funout exchange将消息路由给绑定到它身上的所有队列,而不理会routing key。如果N个队列绑定到某个扇型交换机上,当有消息发送给此扇型交换机时,交换机会将消息的拷贝分别发送给这所有的N个队列。扇型用来交换机处理消息的广播路由(broadcast routing)。
  • Topic (主题交换机)
    消息的routing key是有格式要求的,必须为以“.”分割的字符表,如:“stock.my.test”、“su.edu.test”
    这里需说明两个特殊字符:
    *(星号):可以匹配任意一个单词
    #:可以匹配0个或者多个单词
    假设现在有两个队列Q1和Q2,Q1与exchang1的binding key为:“.my.”,Q2与exchang1的binding key为:“su.#”
    Routing key为“stock.my.test”的消息将发给Q1,routing key为“su.edu.test”的消息将发给Q2
    参考:
    http://dev.dafan.info/detail/371922?p=28-71
    http://blog.csdn.net/anzhsoft/article/details/19633079

Virtual Host

每个Message Broker由一个Virtual Host构成;可以类比一下物理机和虚拟机。Virtual Host相当于一个独立的名称空间,它有属于自己的Exchange、Queue以及的一些相关对象。不同Virtual Host之间的Exchange可以重名。Queue只能和同一个Virtual Host中的Exchange进行绑定。

消息应答

消息应答主要是消费者在服务中设置,在消费者指定消费队列时,打开手动应答机制(应答就是消费者处理完消息之后,发送应答,通知rabbitmq消息已经被处理,可以从内存中删除)
消费者指定消费队列,打开应答机制,注意false是打开手动应答,true为自动应答
开启应答模式:

boolean autoAck = false;
channel.basicConsume(QUEUE_NAME, autoAck, consumer);

返回应答状态:

channel.basicAck(delivery.getEnvelope().getDeliveryTag(), false); 

注意:如果关闭了消息自动应答,同时手动应答并未设置,那么消息发出去之后得不到应答,就会一直放在内存中;(只有消费者挂掉,消息才会投递给其他消费者,这里消息并不会把消息投递给其他消费者)
但是:如果消费者死机(通道关闭、连接关闭或者TCP连接丢失),而没有发送消息应答,rabbitmq会认为消息未被处理,重新将消息发给其他消费者

消息公平派遣

举例:producer发送了20个消息,随即启动的consumer_1把这20个消息全都独占了。在consumer_1工作期间又有consumer_2被启动,但此时consumer_2没有任何任务。此时provider又发送了20个消息,这时consumer_2会得到10个任务。(亲自试过,确实是这样)
解决方法:channel.basicQos(int prefetchCount)方法限制预获取的数量——会告诉RabbitMQ不要同时给一个消费者推送多于N个消息,即一旦有N个消息还没有ack,则该consumer将block掉,直到有消息ack

channel.basicQos(2);

持久化

开启消息应答之后即使消费者死亡,任务也不会丢失。但是如果RabbitMQ服务器停止或者奔溃,我们的任务仍将失去,最终导致队列和消息的丢失!
因此:我们必须把“队列”和“消息”设为持久化。
在声明队列时设置持久化:

boolean durable = true; 
channel.queueDeclare("hello", durable, false, false, null); 

如果已经定义了一个名叫hello的未持久化的队列。RabbitMQ不允许使用不同的参数设定重新定义已经存在的队列,并且会向尝试如此做的程序返回一个错误。一个快速的解决方案——就是声明一个不同名字的队列

总结

转载于:https://www.cnblogs.com/anne-ne/p/7101811.html

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值