RabbitMQ

1.简介

RabbitMQ是用Erlang实现的一个高并发高可靠AMQP消息队列服务器。支持消息的持久化、事务、拥塞控制、负载均衡等特性,使得RabbitMQ拥有更加广泛的应用场景。RabbitMQ跟Erlang和AMQP有关。下面简单介绍一下Erlang和AMQP。
Erlang是一门动态类型的函数式编程语言,它也是一门解释型语言,由Erlang虚拟机解释执行。从语言模型上说,Erlang是基于Actor模型的实现。在Actor模型里面,万物皆Actor,每个Actor都封装着内部状态,Actor相互之间只能通过消息传递这一种方式来进行通信。对应到Erlang里,每个Actor对应着一个Erlang进程,进程之间通过消息传递进行通信。相比共享内存,进程间通过消息传递来通信带来的直接好处就是消除了直接的锁开销(不考虑Erlang虚拟机底层实现中的锁应用)。
​ AMQP(Advanced Message Queue
Protocol)定义了一种消息系统规范。这个规范描述了在一个分布式的系统中各个子系统如何通过消息交互。而RabbitMQ则是AMQP的一种基于erlang的实现。AMQP将分布式系统中各个子系统隔离开来,子系统之间不再有依赖。子系统仅依赖于消息。子系统不关心消息的发送者,也不关心消息的接受者。

2.RabbitMQ的基本原理

RabbitMQ是消息队列的一种实现,那么一个消息队列到底需要什么?答案是队列,即Queue,那么接下来所有名词都是围绕这个Queue来拓展的。

​ 就RabbimtMQ而言,Queue是其中的一个逻辑上的实现,我们需要连接到RabbitMQ来操作队列进而实现业务功能,所以就会有Connection,我们发一条消息连接一次,这样很显然是浪费资源的,建立连接的过程也很耗时,所以我们就会做一个东西让他来管理连接,当我用的时候,直接从里边拿出来已经建立好的连接发信息,那么ConnectionFactory应运而生。

​ 接下来,当程序开发时,可能不止用到一个队列,可能有订单的队列、消息的队列、任务的队列等等,那么就需要给不同的queue发信息,那么和每一个队列连接的这个概念,就叫Channel

​ 再往下来,当我们开发的时候还有时候会用到这样一种功能,就是当我发送一条消息,需要让几个queue都收到,那么怎么解决这个问题呢,难道我要给每一个queue发送一次消息?那岂不是浪费带宽又浪费资源,我们能想到什么办法呢,当然是我们发送给RabbitMQ服务器一次,然后让RabbitMQ服务器自己解析需要给哪个Queue发,那么Exchange就是干这件事的
但是我们给Exchange发消息,他怎么知道给哪个Queue发呢?这里就用到了RoutingKey和BindingKey
BindingKey是Exchange和Queue绑定的规则描述,这个描述用来解析当Exchange接收到消息时,Exchange接收到的消息会带有RoutingKey这个字段,Exchange就是根据这个RoutingKey和当前Exchange所有绑定的BindingKey做匹配,如果满足要求,就往BindingKey所绑定的Queue发送消息,这样我们就解决了我们向RabbitMQ发送一次消息,可以分发到不同的Queue的过程

至此,我们就把所有的名词贯通咯,接下来做个概要描述:

  • ConnectionFactory:与RabbitMQ服务器连接的管理器
  • Connection:与RabbitMQ服务器的TCP连接
  • Channel:与Exchange的连接,一个Connection可以包含多个Channel。之所以需要Channel,是因为TCP连接的建立和释放都是十分昂贵的,为了多路复用。RabbitMQ建议客户端线程之间不要共用Channel,但是建议尽量共用Connection。
  • Exchange:接受消息生产者的消息,并根据消息的RoutingKey和 Exchange绑定的BindingKey,以及Binding规则将消息路由给服务器中的队列。ExchangeType决定了Exchange路由消息的行为,例如,在RabbitMQ中,ExchangeType有direct、Fanout和Topic三种,不同类型的Exchange路由的行为是不一样的。
  • Message Queue:消息队列,用于存储还未被消费者消费的消息。
  • Message: 由Header和Body组成,Header是由生产者添加的各种属性的集合,包括Message是否被持久化、由哪个Message Queue接受、优先级是多少等。而Body是真正需要传输的APP数据。
  • RoutingKey:由Producer发送Message时指定,指定当前消息被谁接受
  • BindingKey:由Consumer在Binding Exchange与Message Queue时指定,指定当前Exchange下,什么样的RoutingKey会被下派到当前绑定的Queue中
  • Binding:联系了Exchange与Message Queue。Exchange在与多个Message Queue发生Binding后会生成一张路由表,路由表中存储着Message Queue所需消息的限制条件即Binding Key。当Exchange收到Message时会解析其Header得到Routing Key,Exchange根据Routing Key与Exchange Type将Message路由到Message Queue。Binding Key由Consumer在Binding Exchange与Message Queue时指定,而Routing Key由Producer发送Message时指定,两者的匹配方式由Exchange Type决定。
  • Server(broker): 接受客户端连接,实现AMQP消息队列和路由功能的进程。
  • Virtual Host:其实是一个虚拟概念,类似于权限控制组,可以通过命令分配给用户Virtual Host的权限,默认的guest用户是管理员权限,初始空间有/,一个Virtual Host里面可以有若干个Exchange和Queue,但是权限控制的最小粒度是Virtual Host**

3.RabbitMQ的安装

1.安装erlang环境

rabbitmq是使用erlang语言开发的,所以必须要先安装erlang环境。erlang的软件环境下载地址为:https://www.erlang.org/downloads,安装完毕之后需要配置环境变量:

ERLANG_HOME=G:\erlang\erl-23.0

新建环境变量
2.安装RabbitMQ

本教程采用解压的方式进行安装。rabbitmq的下载地址为:https://www.rabbitmq.com/install-windows.html

2.1 配置环境变量

1.RABBITMQ_SERVER=G:\rabbitmq\rabbitmq_server-3.8.3
2.在path中加入:%RABBITMQ_SERVER%\sbin

2.2 安装可视化工具
安装可视化工具

4 RabbitMQ程序的编写

4.1 导入jar包依赖

<dependency>
      <groupId>com.rabbitmq</groupId>
      <artifactId>amqp-client</artifactId>
      <version>5.8.0</version>
</dependency>

4.2 RabbitMQ支持的消息模型
4.2.1 简单模型(直连)
简单模型

  • P:生产者,也就是要发送消息的程序
    • C:消费者:消息的接受者,会一直等待消息到来。
    • queue:消息队列,图中红色部分。类似一个邮箱,可以缓存消息;生产者向其中投递消息,消费者从其中取出消息

消息的生成者

public class Producer 
    public static void main(String[] args) throws Exception {
        //创建连接
        Connection connection= RabbitMQUtils.getConnection();
        //创建通道
        Channel channel=connection.createChannel();
        /**
         *  参数含义:
         *   1.对列的名字
         *   2.是否持久化
         *   3.是否为排插队列  true 表示与当前连接绑定  连接没了  它就没了
         *   4.当前队列被消费后   该队列是否自动删除
         *   5.队列的相关参数
         */
         //创建对列
        **channel.queueDeclare("test",true,false,false,null);**
        //发送消息
        for (int i=0;i<10;i++){
            channel.basicPublish("","test",null,("first message"+i).getBytes());
        }
        RabbitMQUtils.close(channel,connection);
    }
}

消息的消费者

public class Consumer {
    public static void main(String[] args) throws Exception {
        Connection connection= RabbitMQUtils.getConnection();
        Channel channel=connection.createChannel();
        channel.basicConsume("test",true,new DefaultConsumer(channel){
            @Override
            public void handleDelivery(String consumerTag, Envelope envelope, AMQP.BasicProperties                                      properties, byte[] body) throws IOException {
                try {
                    TimeUnit.SECONDS.sleep(5);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                System.out.println(new String(body, Charset.defaultCharset()));
            }
        });
    }
}

4.2.2 工作模式

Work queues,也被称为(Task queues),任务模型。当消息处理比较耗时的时候,可能生产消息的速度会远远大于消息的消费速度。长此以往,消息就会堆积越来越多,无法及时处理。此时就可以使用work 模型:让多个消费者绑定到一个队列,共同消费队列中的消息。队列中的消息一旦消费,就会消失,因此任务是不会被重复执行的。

工作模型
消息生产者

public class MessageProducer {
    public static void main(String[] args) throws Exception {

        //得到连接
        Connection connection= RabbitMQUtils.getConnection();
        //得到通道
        Channel channel=connection.createChannel();
        //创建队列
        /**
         *    参数的含义:
         *        1.队列的名字
         *        2.是否持久化
         *        3.是否为排他队列   连接没了  它就跟着没有了
         *       4.是否自动删除
         *       5。队列的其他信息
         */
        channel.queueDeclare("test",true,false,false,null);
        //发送消息
        for (int i=0;i<20;i++){
          channel.basicPublish("","test",null,("first message"+i).getBytes());
        }
        RabbitMQUtils.close(channel,connection);
    }
}

消费者一

public class MessageConsumer {
    public static void main(String[] args) throws Exception {
        Connection connection= RabbitMQUtils.getConnection();
        Channel channel=connection.createChannel();
       // channel.queueDeclare("test",true,false,false,null);
        //每次消费一条消息
        channel.basicQos(1);
        //把消息改为手动确定
        channel.basicConsume("test",false,new DefaultConsumer(channel){
            @Override
            public void handleDelivery(String consumerTag, Envelope envelope, AMQP.BasicProperties properties, byte[] body) throws IOException {
                System.out.println("消费者  1 :"+new String(body, Charset.defaultCharset()));
                channel.basicAck(envelope.getDeliveryTag(),false);
            }
        });
    }
}

消费者二

public class MessageConsumer1 {
    public static void main(String[] args) throws Exception {
        Connection connection= RabbitMQUtils.getConnection();
        Channel channel=connection.createChannel();
        //channel.queueDeclare("test",true,false,false,null);
        //每次消费一条信息
        channel.basicQos(1);
        channel.basicConsume("test",false,new DefaultConsumer(channel){
            @Override
            public void handleDelivery(String consumerTag, Envelope envelope, AMQP.BasicProperties properties, byte[] body) throws IOException {
                System.out.println("消费者  2 :"+new String(body, Charset.defaultCharset()));
                channel.basicAck(envelope.getDeliveryTag(),false);
            }
        });
    }
}

消息自动确认

channel.basicQos(1); // 每次只消费一条消息
channel.queueDeclare("firstQueue", false, false, false, null);
// 消息改为手动确认
channel.basicConsume("firstQueue", false, new DefaultConsumer(channel) {
@Override
public void handleDelivery(String consumerTag, Envelope envelope, 
                           AMQP.BasicProperties properties, byte[] body) throws IOException {
        try {
            TimeUnit.SECONDS.sleep(4);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println(new String(body, Charset.defaultCharset()));
        // 手动确认消息已经被消费了, 第一个参数是当前消费的消息的标签(递增的整数)
        // 第二个参数是否确认多条消息,包括之前消费的消息
        channel.basicAck(envelope.getDeliveryTag(), false);
	}
});

4.2.3 发布订阅模型
发布订阅模型
在广播模式下,消息的发送流程是这样的:

  • 可以有多个消费者
  • 每个消费者都有自己的队列
  • 每个队列都要绑定在交换机上
  • 生产者发送的信息,只能发送到交换机上,再由交换机决定发送到哪个队列中,生产者无法决定
  • 交换机把消息发送给绑定的的所有对列
  • 队列的消费者都能拿到消息,实现一条消息被多个消费者消费

消息的生成者

public class MessageProducer {
    public static final String FANOUT_EXCHANGE_NAME = "fanout-exchange";
    public static void main(String[] args) throws Exception {
        //得到连接
        Connection connection= RabbitMQUtils.getConnection();
        //得到通道
        Channel channel=connection.createChannel();
        //创建队列
       // channel.queueDeclare("test",true,false,false,null);
        //声明一个交换机  交换机的类型是fanout  消息的订阅与发布
        channel.exchangeDeclare(FANOUT_EXCHANGE_NAME, BuiltinExchangeType.FANOUT);
        //发送消息
        for (int i=0;i<20;i++){
          channel.basicPublish(FANOUT_EXCHANGE_NAME,"",null,("fanout message"+i).getBytes());
        }
        RabbitMQUtils.close(channel,connection);
    }
}

多个消息的消费者

public class MessageConsumer {
    public static void main(String[] args) throws Exception {
        Connection connection= RabbitMQUtils.getConnection();
        Channel channel=connection.createChannel();
       // channel.queueDeclare("test",true,false,false,null);
        //每次消费一条消息
        channel.basicQos(1);
        //声明一个交换机
        String queueName=channel.queueDeclare().getQueue();
        //将交换机和队列进行绑定
        channel.queueBind(queueName, MessageProducer.FANOUT_EXCHANGE_NAME, "");
        //把消息改为手动确定
        channel.basicConsume(queueName,false,new DefaultConsumer(channel){
            @Override
            public void handleDelivery(String consumerTag, Envelope envelope, AMQP.BasicProperties properties, byte[] body) throws IOException {
                System.out.println("消费者  1 :"+new String(body, Charset.defaultCharset()));
                channel.basicAck(envelope.getDeliveryTag(),false);
            }
        });
    }
}
public class MessageConsumer1 {
    public static void main(String[] args) throws Exception {
        Connection connection= RabbitMQUtils.getConnection();
        Channel channel=connection.createChannel();
        //channel.queueDeclare("test",true,false,false,null);
        channel.basicQos(1);
        //声明一个队列
        String queueName=channel.queueDeclare().getQueue();
        //将对列和交换机进行绑定  因为交换机的类型是:fanout, 所以不用指定 bindingkey(指定也没用)
        channel.queueBind(queueName,MessageProducer.FANOUT_EXCHANGE_NAME,"");
        channel.basicConsume(queueName,false,new DefaultConsumer(channel){
            @Override
            public void handleDelivery(String consumerTag, Envelope envelope, AMQP.BasicProperties properties, byte[] body) throws IOException {
                System.out.println("消费者  2 :"+new String(body, Charset.defaultCharset()));
                channel.basicAck(envelope.getDeliveryTag(),false);
            }
        });
    }
}

4.2.4 直连模式

在Fanout模式中,一条消息,会被所有订阅的队列都消费。但是,在某些场景下,我们希望不同的消息被不同的队列消费。这时就要用到Direct类型的Exchange。
直连模式

  • 队列与交换机的绑定,不能是任意绑定了,而是要指定一个RoutingKey(路由key)
  • 消息的发送方在 向 Exchange发送消息时,也必须指定消息的 RoutingKey
  • Exchange不再把消息交给每一个绑定的队列,而是根据消息的Routing Key进行判断,只有队列的Routingkey与消息的 Routing key完全一致,才会接收到消息

消息的生成者

public class MessageProducer {
    public static final String DIRECT_EXCHANGE_NAME = "direct-exchange";
    public static void main(String[] args) throws Exception {
        Connection connection= RabbitMQUtils.getConnection();
        Channel channel=connection.createChannel();
        //声明交换机
        channel.exchangeDeclare(MessageProducer.DIRECT_EXCHANGE_NAME, BuiltinExchangeType.DIRECT);
        //发送消息   消息发送要指定 RoutingKey
        channel.basicPublish(MessageProducer.DIRECT_EXCHANGE_NAME,"success",null,"direct message".getBytes());
        RabbitMQUtils.close(channel,connection);
    }

多个消息的消费者

public class MessageConsumer1 {
    public static void main(String[] args) throws Exception {
        Connection connection= RabbitMQUtils.getConnection();
        Channel channel=connection.createChannel();
        channel.basicQos(1);
        //channel.exchangeDeclare(MessageProducer.DIRECT_EXCHANGE_NAME, BuiltinExchangeType.DIRECT);
        //得到队列的名字
        String queueName=channel.queueDeclare().getQueue();
        //将队列与交换机进行绑定
        channel.queueBind(queueName,MessageProducer.DIRECT_EXCHANGE_NAME,"fail");
        channel.basicConsume(queueName,false,new DefaultConsumer(channel){
            @Override
            public void handleDelivery(String consumerTag, Envelope envelope, AMQP.BasicProperties properties, byte[] body) throws IOException {
                System.out.println("fail:"+new String(body, Charset.defaultCharset()));
                channel.basicAck(envelope.getDeliveryTag(),false);
            }
        });
    }
}

```java
public class MessageConsumer {
    public static void main(String[] args) throws Exception {
        Connection connection= RabbitMQUtils.getConnection();
        Channel channel=connection.createChannel();
        channel.basicQos(1);
        //channel.exchangeDeclare(MessageProducer.DIRECT_EXCHANGE_NAME, BuiltinExchangeType.DIRECT);
        String queueName=channel.queueDeclare().getQueue();
        channel.queueBind(queueName,MessageProducer.DIRECT_EXCHANGE_NAME,"success");
        channel.basicConsume(queueName,false,new DefaultConsumer(channel){
            @Override
            public void handleDelivery(String consumerTag, Envelope envelope, AMQP.BasicProperties properties, byte[] body) throws IOException {
                System.out.println("success:"+new String(body, Charset.defaultCharset()));
                channel.basicAck(envelope.getDeliveryTag(),false);
            }
        });
    }
}

2.2.5 主题模式

Topic类型的ExchangeDirect相比,都是可以根据RoutingKey把消息路由到不同的队列。只不过Topic类型Exchange可以让队列在绑定Routing key 的时候使用通配符!这种模型Routingkey 一般都是由一个或多个单词组成,多个单词之间以”.”分割,例如: item.insert

主题模型

统配符
* (star) can substitute for exactly one word. 匹配不多不少恰好1个词
# (hash) can substitute for zero or more words. 匹配一个或多个词
如:
audit.# 匹配audit.irs.corporate或者audit.irs 等
audit.* 只能匹配 audit.irs

消息的生成者

public class TopicProducer {
    public static final String TOPIC_EXCHANGE_NAME = "topic-exchange";
    public static void main(String[] args) throws Exception {
        Connection connection= RabbitMQUtils.getConnection();
        Channel channel=connection.createChannel();
        //声明交换机
        channel.exchangeDeclare(TOPIC_EXCHANGE_NAME, BuiltinExchangeType.TOPIC);
        //发送消息
        channel.basicPublish(TopicProducer.TOPIC_EXCHANGE_NAME,"company.html.manager",null,"topic message".getBytes());
        RabbitMQUtils.close(channel, connection);

    }
}

多个消费者

public class TopicConsumer {
    public static void main(String[] args) throws Exception {
        Connection connection= RabbitMQUtils.getConnection();
        Channel channel=connection.createChannel();
        channel.basicQos(1);
        String queueName=channel.queueDeclare().getQueue();
        channel.queueBind(queueName,TopicProducer.TOPIC_EXCHANGE_NAME,"company.#");
        channel.basicConsume(queueName,false,new DefaultConsumer(channel){
            @Override
            public void handleDelivery(String consumerTag, Envelope envelope, AMQP.BasicProperties properties, byte[] body) throws IOException {
                System.out.println("消费者 1 :"+new String(body, Charset.defaultCharset()));
                channel.basicAck(envelope.getDeliveryTag(),false);
            }
        });
    }
}
public class TopicConsumer2 {
    public static void main(String[] args) throws Exception {
        Connection connection= RabbitMQUtils.getConnection();
        Channel channel=connection.createChannel();
        channel.basicQos(1);
        String queueName=channel.queueDeclare().getQueue();
        channel.queueBind(queueName,TopicProducer.TOPIC_EXCHANGE_NAME,"company.java.#");
        channel.basicConsume(queueName,false,new DefaultConsumer(channel){
            @Override
            public void handleDelivery(String consumerTag, Envelope envelope, AMQP.BasicProperties properties, byte[] body) throws IOException {
                System.out.println("消费者 1 :"+new String(body, Charset.defaultCharset()));
                channel.basicAck(envelope.getDeliveryTag(),false);
            }
        });
    }
}

public class TopicConsumer1 {
    public static void main(String[] args) throws Exception {
        Connection connection= RabbitMQUtils.getConnection();
        Channel channel=connection.createChannel();
        channel.basicQos(1);
        String queueName=channel.queueDeclare().getQueue();
        channel.queueBind(queueName,TopicProducer.TOPIC_EXCHANGE_NAME,"company.html.*");
        channel.basicConsume(queueName,false,new DefaultConsumer(channel){
            @Override
            public void handleDelivery(String consumerTag, Envelope envelope, AMQP.BasicProperties properties, byte[] body) throws IOException {
                System.out.println("消费者 1 :"+new String(body, Charset.defaultCharset()));
                channel.basicAck(envelope.getDeliveryTag(),false);
            }
        });
    }
}

5.RabbitMQ与SpringBoot整合

5.1 jar包依赖

<dependencies>
	<dependency>
		<groupId>org.springframework.boot</groupId>
		<artifactId>spring-boot-starter-amqp</artifactId>
	</dependency>
	<dependency>
		<groupId>org.springframework.boot</groupId>
		<artifactId>spring-boot-starter-web</artifactId>
	</dependency>
	<dependency>
		<groupId>com.alibaba</groupId>
		<artifactId>fastjson</artifactId>
		<version>1.2.4</version>
	</dependency>
	<dependency>
		<groupId>org.springframework.boot</groupId>
		<artifactId>spring-boot-starter-test</artifactId>
		<scope>test</scope>
		<exclusions>
			<exclusion>
				<groupId>org.junit.vintage</groupId>
				<artifactId>junit-vintage-engine</artifactId>
			</exclusion>
		</exclusions>
	</dependency>
	<dependency>
		<groupId>org.projectlombok</groupId>
		<artifactId>lombok</artifactId>
	</dependency>
</dependencies>

5.2 简单模型

消息消费方

@Component
public class SimpleModel {
    // 简单模型  直连   是否进行持久化
    @RabbitListener(queuesToDeclare ={@Queue(name ="simple-model",durable = "true")} )
    public void receiverMessage(String text){
        System.out.println(text);
    }
}

工作模型

 //创建信息的消费者
     @RabbitListener(queues ={"work-model"})
    public void receiverMessage(String text, Channel channel, Message message) throws IOException {
        System.out.println("消费者 1:"+text);
        channel.basicAck(message.getMessageProperties().getDeliveryTag(),false);
    }

    @RabbitListener(queues ={"work-model"})
    public void receiverMessage1(String text, Channel channel, Message message) throws IOException {
        System.out.println("消费者 2:"+text);
        channel.basicAck(message.getMessageProperties().getDeliveryTag(),false);
    }

发布订阅模型

消息的消费方

//    @RabbitListener(bindings = @QueueBinding(
//            value = @Queue(name = "fanout-consumer-2"),
//            exchange = @Exchange(name = "fanout-exchange", type = ExchangeTypes.FANOUT)))
        @RabbitListener(bindings = {
                @QueueBinding(value = @Queue(name ="fanout-consumer-1"),
                        exchange = @Exchange(value = "fanout-exchange", type = ExchangeTypes.FANOUT))
        })
    public void receiverMessage(String text, Channel channel, Message message) throws IOException {
        System.out.println("消费者 1:"+text);
        channel.basicAck(message.getMessageProperties().getDeliveryTag(),false);
    }

    @RabbitListener(bindings = {
            @QueueBinding(value = @Queue(name ="fanout-consumer-2"),
                    exchange = @Exchange(value = "fanout-exchange", type = ExchangeTypes.FANOUT))
    })
    public void receiverMessage1(String text, Channel channel, Message message) throws IOException {
        System.out.println("消费者 2:"+text);
        channel.basicAck(message.getMessageProperties().getDeliveryTag(),false);
    }

直连模型
消息的消费方

 @RabbitListener(
            bindings ={
                    @QueueBinding(
                            value = @Queue(name ="direct-model-1",durable = "true"),
                            key = {"error","success"},
                            exchange =@Exchange(name ="direct-exchange",type =ExchangeTypes.DIRECT)
                    )
            }
    )
    public void receiverMessage(User user, Channel channel, Message message) throws IOException {
        System.out.println("消费者 1:"+user);
        channel.basicAck(message.getMessageProperties().getDeliveryTag(),false);
    }

    @RabbitListener(
            bindings = {
                    @QueueBinding(
                            value = @Queue(name ="direct-model-2",durable = "true"),
                            key = {"success"},
                            exchange =@Exchange(name ="direct-exchange",type =ExchangeTypes.DIRECT)
                    )
            }
    )
    public void receiverMessage1(User user, Channel channel, Message message) throws IOException {
        System.out.println("消费者 2:"+user);
       channel.basicAck(message.getMessageProperties().getDeliveryTag(),false);
    }

主题模型

 @RabbitListener(
            bindings = {
                  @QueueBinding(
                          value =@Queue(name ="topic model 1",durable = "true"),
                          key = {"company.#"},
                          exchange = @Exchange(name ="topic-exchange",type = ExchangeTypes.TOPIC)
                  )
            }
    )
    public void receiverMessage(User user, Channel channel, Message message) throws IOException {
        System.out.println("消费者 1:"+user);
        channel.basicAck(message.getMessageProperties().getDeliveryTag(),false);
    }
    @RabbitListener(
            bindings ={
              @QueueBinding(
                      value = @Queue(name ="topic model 2",durable = "true"),
                      key = {"company.*"},
                      exchange = @Exchange(name ="topic-exchange",type =ExchangeTypes.TOPIC)
              )
         }
    )
    public void receiverMessage1(User user, Channel channel, Message message) throws IOException {
        System.out.println("消费者 2:"+user);
        channel.basicAck(message.getMessageProperties().getDeliveryTag(),false);
    }

各个模型的测试`

@RestController
@RequestMapping("/mq")
public class RabbitmqController {
    @Resource
    private RabbitTemplate rabbitTemplate;
    @GetMapping("/simple")
    public String simpleModel(){
        rabbitTemplate.convertAndSend("simple-model","simple message");
        return "success";
    }

    @GetMapping("/work")
    public String workModel(){
        for (int i=0;i<20;i++){
          rabbitTemplate.convertAndSend(WorkModel.QUEUE_NAME,"work message"+i);
        }
        return "success";
    }

    @GetMapping("/fanout")
    public String fanoutModel(){
        for (int i=0;i<20;i++){
            rabbitTemplate.convertAndSend("fanout-exchange","","fanout message"+i);
        }
        return "success";
    }


    @GetMapping("/direct")
    public String directModel(){
       rabbitTemplate.convertAndSend("direct-exchange","error",new User(1,"张三"));
        return "success";
    }
    @GetMapping("/topic")
    public String topicModel(){
        rabbitTemplate.convertAndSend("topic-exchange","company.java.massage",new User(1,"李四"));
        return "success";
    }
}

6.RabbitMQ发送Json数据消息

定义使用json的方式转换数据

 // 将 rabbitTemplate 在发送数据的时候,序列化方式改为 json
    @PostConstruct
    public void addJsonSerializeToTemplate() {
        rabbitTemplate.setMessageConverter(new Jackson2JsonMessageConverter());
    }

消息的消费方json数据的反序列化

如果我们自己实例化了一个 RabbitListenerContainerFactory 项目中会使用该 factory, 会覆盖
springboot中原本的 SimpleRabbitListenerContainerFactory 这个信息,会导致yml文件中的配置失效。

 @Bean
    public RabbitListenerContainerFactory<?> rabbitListenerContainerFactory(
            ConnectionFactory connectionFactory){
        SimpleRabbitListenerContainerFactory factory = new SimpleRabbitListenerContainerFactory();
        factory.setAcknowledgeMode(AcknowledgeMode.MANUAL);
        factory.setPrefetchCount(5);
        factory.setConnectionFactory(connectionFactory);
        factory.setMessageConverter(new Jackson2JsonMessageConverter());
        return factory;
    }
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值