RabbitMQ消息队列总结

RabbitMQ消息队列

    为什么用到消息队列:

        当操作呈一条链路的时候,如果一个地方宕机了,那就整个都无法使用。

        消息队列可以作为进程间或者同一进程不同线程之间的通信,一个异步通信协议

        同时也有储存功能,等待用户取回它

    

    优点:

        应用解耦:接入MQ之前,代码中需要用到消息的都需要添加相应代码;接入MQ之后,之前的代码无需修改,只需要把代码加上MQ就好,这样修改代码会方便很多

        异步处理:异步发送消息,不像一条链路一样一条路走到黑,而是使用了多线程或者多进程的方式去解决它

        流量削峰:如果一下子请求量过大,会导致服务宕机,这时候可以走消息队列,第一次取出一部分,第二次取出一部分,以此类推

    

    缺点:

        复杂度增大

    

    为什么使用RabbitMQ:

        处理高并发,速度快,也不会存在消息错误的问题,完全开源的一个软件

        

    RabbitMQ安装:

        默认端口:15672

        默认用户名与密码:guest

        默认集群端口:25672

        默认amqp端口:5672

        

        添加用户:可以使用Web控制台的添加用户操作,添加对应的用户及权限

        

        权限: Admin | Monitoring | Policymaker | Management | Impersonator | None

 

    概念:

        生产者:发送消息的一方就是生产者

        队列:传输管道,大小与磁盘有关,其本质就是一个大的缓冲区

        消费者:等待接收消息的一方

        

    Java操作队列:

    简单队列操作流程:连接工厂 --- 连接 --- 信道

        

    Closable接口:

        当实现了这个接口,所用的资源就会在使用后自动关闭,Connection和Channel都是实现了Closable接口

        

    创建连接:

    在ConnectFactory可以使用setHost等方法配置连接工厂,最后使用 newConnection方法 就可以创建Connection实例

    信道配置:

        queueDeclare方法用于绑定队列

            参数:

                队列名称

                持久化

                排他队列(基于第一个创建它的连接可见,其他连接看不到,并且不能创建同名的排他队列,就算设置了持久化当关闭后都会自动删除)

                自动删除

                其他参数

            

        发送方:channel配置basicPublish,可以配置交换机名称、队列名称、自动确认(确认收到消息)以及发送的二进制流信息;

            

        接收方:使用deliveryCallback方法获取RabbitMQ中的消息,消息传入此方法的message中,可以通过getBody获取消息

    

    

    操作工作队列:basicQos && basicAck

        解决发送方与接收方信息速度差的问题:如果发送方快而接收方慢,那就会把内存撑爆

        采用一个生产者多个消费者的策略,进行消息获取

            

        轮询模式:

            当一个信道名称下有多个接收者的时候,会以轮询的方式依次发送给对应的消费者,不需要特殊配置

                

            优点:

                可以减轻RabbitMQ服务器的重担

                    

            缺点:

                如果两个接收方也有速度差,那总时间时长会根据消费慢的那个去计算,而快的那个会一直处于空闲状态

            处理:快的就多消费一点(能者多劳)

            

        公平模式:

        能者多劳,当一个队列名称下有多个接收者,且接收者之间有速度差的时候,就是一边都处理完了,另一边还在处理的情况下可以开启公平模式

        设置队列的basicQos来设置公平模式的的参数:

            设置每个接收者能处理的最大信息量,如果达到该最大信息量而且还没有读取完,则把消息发给下一个接收者

        就是你没处理完,那我这个就不给你先了,等你处理完我才会把消息给你

        还需要开启信道的自动确认:

            basicAck:第一个参数表示回调消息的唯一标识,第二个参数表示是否确认多条信息,并且把信道原本的自动确认basicConsume关掉

                

                

    操作订阅队列;BuiltinExchangeType.FANOUT

        每个接收方都可以接收到相同的全部信息,这就是订阅队列

        采用一个生产者多个信道发到多个消费者的策略,进行消息获取,RabbitMQ会给我们自动生成排他队列,用于绑定消费者

        有四种模式: direct、topic、headers和fanout ==> 做fanout(粉丝订阅)

            

        不再使用队列的名字进行绑定,而是使用交换机名字进行绑定:

        发送方:使用exchangeDeclare替换queueDeclare,把消息发送到交换机处

        接收方:在basicPublish里面声明交换机名称,替换掉原来的队列声明,然后把队列名称从信道里面提取出来,消费者还是需要在basicConsume绑定信道名称

        即先要获取队列,然后绑定队列,最后消费消息

            

            

    操作路由队列:BuiltinExchangeType.DIRECT

        有些消息是所有消费者都能消费,但有些消息是特定消费者才能消费

        同样是采用交换机的策略,但是是使用direct模式,而不是以上的fanout模式、

        绑定的routeKey是设置在交换机上面的,因为平时要先操作接收方,所以routeKey放在接收方与信道进行绑定并传入RabbitMQ

        如果是使用了RabbitTemplate,那么开启的首先是发送方,所以routeKey会放在发送方与信道进行绑定并传入RabbitMQ

            

        和以上一样的,发送方需要修改交换机发送策略为DIRECT,然后在basicPublish中的第二个参数添加routeKey

        而接收方需要在queueBind里面的最后一个参数添加routeKey,要注意设置交换机名称

    操作主题队列:BuiltinExchangeType.TOPIC

        路由队列的缺点:随着使用时间时长的增加,路由数量也随之增加,到最后难以管理

            

        主题队列就是把路由队列的routeKey换成了带通配符的routeKey,其中 * 表示匹配一个单词, # 表示匹配0个或多个单词(用 . 分割)

        一个队列匹配到多个适合的routeKey,也只发一次给对应队列

            

        如果无法匹配,消息会被丢弃

    操作RPC队列:

            远程过程调用的服务器和客户端同时为消费者和生产者:

                客户端发送消息至消息队列,携带自己监听回调的队列ID和唯一标识,然后传入服务器,同时客户端监听回调的队列ID。

                此时服务器获取到消息并处理完成,发送至客户端申请的队列ID,放入消息队列中。

                客户端根据唯一标识获取自己所需要的消息,如果不是自己的就不获取。

                

            使用AMQP.BasicProperties类来构建属性,获取之后可以放在发送方basicPublish的prop属性值里(即第三个参数)

            

    RabbitMQ的事务:

    RabbitMQ的事务很弱,和Redis一样

    三个方法:    channel.txSelect 开启事务

                channel.txCommit 提交事务

                channel.txRollback 回滚事务

    用于生产者判断自己的消息是否发送到RabbitMQ队列里面,防止我们没发成功却没提示

    

    信道的确认模式:

    同步确认:(不推荐)

        单条确认:每发出一条消息,就会调用waitForConfirm方法等待服务器确认

            启用确认模式:channel.confirmSelect方法

            等待确认消息:channel.waitForConfirm方法,返回布尔值,可以用于判断、输出

        批量确认:每发出一批消息,调用waitForConfirmOrDie方法等待服务器确认

            启用确认模式:channel.confirmSelect方法

            等待批量确认消息:channel.waitForConfirmOrDie方法,返回void,如果有一条不成功,就抛出异常

    

    异步确认:(推荐)

        原理:

        维护一个SortedSet,每一条信息都会对应一个唯一的UUID,每发送一条消息就在SortedSet里面加入他的UUID。当它多条确认后,会返回一个UUID给我们,就是说我们在这个ID号之前的消息都已经确认了,已经发送到位了,

        这时,可以拿到返回的ID,在SortedSet里面把在这个ID前的所有元素都删除掉

        

        实践:

        为信道开启异步确认功能:channelconfirmSelect

        维护一个SortedSet

        为信道创建一个监听器,传入并重写ConfirmListener,里面每个方法第一个参数代表着唯一的序列号,第二个参数表示是否多条确认成功,T就是多条确认,F就是单条确认

            在每一次basicPublish之前获取下一次发送的UUID,在发送后把UUID加入到SortedSet中

 

  1. public class Send {
  2.  
  3.  private final static String QUEUE_NAME = "async";
  4.  
  5.  public static void main(String[] argv) throws Exception {
  6.   final SortedSet<Long> set = Collections.synchronizedSortedSet(new TreeSet<Long>());
  7.  
  8.   ConnectionFactory factory = new ConnectionFactory();
  9.   factory.setHost("localhost");
  10.   try (Connection connection = factory.newConnection();
  11.        Channel channel = connection.createChannel()) {
  12.    channel.confirmSelect();
  13.    channel.queueDeclare(QUEUE_NAME, falsefalsefalsenull);
  14.     //监听器
  15.    channel.addConfirmListener(new ConfirmListener() {
  16.     //确认方法 l为唯一序列号 b为是否确认多条:true为已经确认多条,false为已经确认单条
  17.     @Override
  18.     public void handleAck(long l, boolean b) throws IOException {
  19.      if (b) {
  20.       System.out.println("Ack Multiple" + l);
  21.       set.headSet(l+1L).clear();
  22.      }else {
  23.       System.out.println("NAck Multiple" + l);
  24.       set.remove(l);
  25.      }
  26.     }
  27.     //未确认方法
  28.     @Override
  29.     public void handleNack(long l, boolean b) throws IOException {
  30.      if (b) {
  31.       System.out.println(" No Ack Multiple" + l);
  32.       set.headSet(l+1L).clear();
  33.      }else {
  34.       System.out.println(" No NAck Multiple" + l);
  35.       set.remove(l);
  36.      }
  37.     }
  38.    });
  39.  
  40.    while (true) {
  41.     String message = "Hello World!";
  42.     Long id = channel.getNextPublishSeqNo();
  43.     channel.basicPublish("", QUEUE_NAME, null, message.getBytes(StandardCharsets.UTF_8));
  44.     set.add(id);
  45.    }
  46.   }
  47.  }
  48. }

 

            

SpringAMQP:

    通过RabbitTemplate去发送和接收消息

    

    五大步:

        POM引入依赖

        yml配置rabbitMQ的环境信息

        主启动类无需修改

    

    编写配置类,用于配置JavaBean:

        1.交换机(设置交换机名)

        2.队列(设置队列名)

        3.绑定(用于绑定交换机和队列名),使用BindBuilder来构建,还可以在使用with方法绑定信道与routeKey(Topic队列的工作)

    

    编写发送类:

        使用RabbitTemplate的convertAndSend来发送消息,第一个传入交换机名,第二个传入自己的routeKey,第三个传入自己要发送的消息

        由于在上面给信道绑定了匹配方式,所以我们发送的消息会携带着routeKey走入自己所属的信道

        

        接收方此时使用@RabbitListener监听队列,里面填入queues属性,用于表明自己监听的是哪一个信道,然后在监听方法上使用@RabbitHandler方法,表示这是一个监听方法,方法中传入参数类似于MVC的数据绑定,会自动传入参数

        例如我现在发送出一个String,那么我在接收方就要接受一个String,可以对这个String进行一系列操作    

posted @ 2022-03-28 17:00  Quent1nCn  阅读(256)  评论(0编辑  收藏  举报