RabbitMQ学习笔记

MQ简介

MQ(message quene)即消息队列,其实就是一种生产者消费者模式,生产者往队列放信息,消费者则从队列拿出信息进行处理,生产和消费是异步的,便能轻松完成系统间的解耦,在开发过程中只需要单独关心发送和接收消息,没有业务逻辑的入侵

RabbitMQ特点

基于elang语言开发的消息队列系统,基于AMQP来实现。

AMQP主要特征是面向消息,队列,路由,可靠,安全。AMQP无图说锤更多用在企业系统内对数据一致性、稳定性和可靠性要求很高的场景,对性能和吞吐量要求没有太高

RabbitMQ之模型简介

添加虚拟主机及用户

在web页面上选择admin选项卡,右边选择virtual host,添加一个virtual host,之后再添加一个用户,让这个新添加的用户能够访问到这个虚拟主机

导入依赖

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

最简单的模型:点对点

在这里插入图片描述

生产消息

@Test
public void test() throws IOException, TimeoutException {
    ConnectionFactory connectionFactory = new ConnectionFactory();
    connectionFactory.setHost("localhost");
    connectionFactory.setPort(5672);
    connectionFactory.setVirtualHost("/ems");
    connectionFactory.setUsername("ems");
    connectionFactory.setPassword("123");
    Connection connection = connectionFactory.newConnection();
    Channel channel = connection.createChannel();
    //队列名,是否独占队列,是否持久化,是否消费完成后删除队列,额外附加参数
    channel.queueDeclare("hello", false, false, false, null);
    //交换机名,队列名,传递消息额外设置,消息具体内容
    channel.basicPublish("", "hello", null, "hello rebbitmq".getBytes());
    channel.close();
    connection.close();
}

消费消息

//这里要用一个main函数写,直接用@Test的话线程在拿到请求来不及处理就没中断了看不到效果
public static void main(String[] args) throws IOException, TimeoutException {
    ConnectionFactory connectionFactory = new ConnectionFactory();
    connectionFactory.setHost("localhost");
    connectionFactory.setPort(5672);
    connectionFactory.setVirtualHost("/ems");
    connectionFactory.setUsername("ems");
    connectionFactory.setPassword("123");
    Connection connection = connectionFactory.newConnection();
    Channel channel = connection.createChannel();
    channel.queueDeclare("hello", false, false, false, null);
    //消费队列,自动确认参数,一个Consumer
    channel.basicConsume("hello", false, new DefaultConsumer(channel){
        @Override
        public void handleDelivery(String consumerTag, Envelope envelope, AMQP.BasicProperties properties, byte[] body) throws IOException {
            System.out.println("new String(body) = " + new String(body));
        }
    });
}

部分参数说明

  1. 持久化参数
//在队列声明部分将持久化参数设置为true,保证队列持久化
channel.queueDeclare("hello", true, false, false, null);
//还需要在basicPublish中传入MessageProperties.PERSISTENT_TEXT_PLAIN,保证队列中的消息持久化
channel.basicPublish("", "hello", MessageProperties.PERSISTENT_TEXT_PLAIN, "hello rebbitmq".getBytes());
  1. 独占队列参数

如果将独占队列参数设置为true,该队列只能与当前通道绑定,其他通道要绑定的话会报错(亲测)

  1. 消费完毕后删除队列参数

注意即使消息队列中没有参数但消费者线程仍未断开,它还在继续监听着队列,这个时候队列还不会删除,内有当断开线程了才会删除队列

平均消费工作模型

在这里插入图片描述

原理跟点对点的差不多,只需要额外设置一个消费者并开启线程监听队列即可,此时的消息是平均分配的,所以当两个消费者的消费速度不一样时,并不能很好地提高消费信息的速度,应该提倡能者多劳的思想

能者多劳工作模型

消息确认机制

在介绍能者多劳工作模型时先提一下消息确认机制,它是这里的第二个参数,设置为true时每当它拿到消息时,它拿到消息不管消息是否处理完成(使用消息进行的业务逻辑处理)就进行了确认,这就导致了每个消费者能无视自己的处理消息的能力(性能)一直先在队列中抢消息

//消费队列,自动确认参数,一个Consumer
channel.basicConsume("hello", true, new DefaultConsumer(channel){
    @Override
    public void handleDelivery(String consumerTag, Envelope envelope, AMQP.BasicProperties properties, byte[] body) throws IOException {
        System.out.println("new String(body) = " + new String(body));
    }
});

平均消费模型改造为能者多劳模型

  • 把消息自动确认设置为false

  • 设置通道每次只能拿一个消息

channel.basicQos(1);
  • 设置在业务逻辑处理完后再进行消息确认
public void handleDelivery(String consumerTag, Envelope envelope, AMQP.BasicProperties properties, byte[] body) throws IOException {
    System.out.println("new String(body) = " + new String(body));
    channel.basicAck(envelope.getDeliveryTag(),false);
}

fanout模型(广播模型)

在这里插入图片描述

  • 顾名思义生产者发送的消息并非只有一个消费者能接收到
  • 生产者只发送消息给交换机
  • 交换机再做决定将消息发送给哪个队列

要注意各个参数的含义

provider

public class Provider {

    public static void main(String[] args) throws IOException, TimeoutException {
        Connection connection = RabbitMQUtil.getConnection();
        Channel channel = connection.createChannel();
        //交换机名,类型
        channel.exchangeDeclare("myExchange", "fanout");
        //交换机名,routing key,设置持久化信息的,消息信息
        channel.basicPublish("myExchange","",null,"My message".getBytes());
        RabbitMQUtil.closeResource(channel, connection);
    }

}

两个一样的consumer

public class Consumer {
    //要两个consumer,多建一个类换个名就好
    public static void main(String[] args) throws IOException, TimeoutException {
        Connection connection = RabbitMQUtil.getConnection();
        Channel channel = connection.createChannel();
        //创建一个临时 队列,用完即删的那种
        String tempQueue = channel.queueDeclare().getQueue();
        //绑定交换机和队列,队列名,交换机名,routing key
        channel.queueBind(tempQueue, "myExchange", "");
        channel.basicConsume(tempQueue, false, new DefaultConsumer(channel){
            @Override
            public void handleDelivery(String consumerTag, Envelope envelope, AMQP.BasicProperties properties, byte[] body) throws IOException {
                System.out.println("new String(body) = " + new String(body));
            }
        });
    }

}

Direct模型

在这里插入图片描述

direct模型与fanout类似

  • 在发送消息时指定了交换机之后还要再指定一个routing key
  • 消费者在绑定队列的时候也要指定一下交换机和routing key
  • 在消费者中也要声明一下交换机

生产者

public class Provider {
    public static void main(String[] args) throws IOException, TimeoutException {
        Connection connection = RabbitMQUtil.getConnection();
        Channel channel = connection.createChannel();
        //声明交换机,指定名字和类型
        channel.exchangeDeclare("myDirectExchange", "direct");
        //指定的routing key
        String routingKey = "key2";
        channel.basicPublish("myDirectExchange", routingKey, null, ("发送到" + routingKey + "的消息").getBytes());
        RabbitMQUtil.closeResource(channel, connection);
    }
}

消费者

public class Consumer {

    public static void main(String[] args) throws IOException, TimeoutException {
        Connection connection = RabbitMQUtil.getConnection();
        Channel channel = connection.createChannel();
        //消费者这边也记得声明交换机!!
        channel.exchangeDeclare("myDirectExchange", "direct");
        String tempQueue = channel.queueDeclare().getQueue();
        channel.queueBind(tempQueue, "myDirectExchange", "key1");
        //再多一个消费都可以多绑定或少绑定routing key,自己看得到效果就好
        channel.queueBind(tempQueue, "myDirectExchange", "key2");
        channel.basicConsume(tempQueue,false,new DefaultConsumer(channel){
            @Override
            public void handleDelivery(String consumerTag, Envelope envelope, AMQP.BasicProperties properties, byte[] body) throws IOException {
                System.out.println("new String(body) = " + new String(body));
            }
        });
    }

}

Topic模型

topic模型与Direct模型基本一样,只是支持了两个通配符,方便绑定队列

  • *:绑定一个单词
  • #:绑定多个单词

SpringBoot整合RabbitMQ

maven依赖

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-amqp</artifactId>
</dependency>

yml配置

spring:
  rabbitmq:
    host: localhost
    port: 5672
    username: ems
    password: 123
    virtual-host: /ems

点对点模型

生产者测试类

@SpringBootTest()
@RunWith(SpringRunner.class)
public class RabbitMQTest {

    @Autowired
    private RabbitTemplate rabbitTemplate;

    @Test
    public void test() {
        rabbitTemplate.convertAndSend("hello", "这是我发送的消息");
    }
}

消费者

//需要将该类注册到spring容器中去
@Component
//可在这里设置创建队列时的那几个参数
@RabbitListener(queuesToDeclare = @Queue(value = "hello"))
public class MyConsumer {
    //标记当从消息队列拿到消息后执行的方法
    @RabbitHandler
    public void doComsume(String message) throws InterruptedException {
        System.out.println("message = " + message);
    }
}

工作模型

生产者

@Test
public void testWork(){
    for (int i = 0; i < 10; i++) {
        rabbitTemplate.convertAndSend("work", "work模型");
    }
}

消费者

@Component
public class WorkConsumer {
    //该注解直接加在方法上,可以省去加上@RabbitHandler
    @RabbitListener(queuesToDeclare = @Queue(value = "work"))
    public void consumeMessage1(String message) {
        System.out.println("consumer1:"+message);
    }

    @RabbitListener(queuesToDeclare = @Queue(value = "work"))
    public void consumeMessage2(String message) {
        System.out.println("consumer2:"+message);
    }

}

能者多劳模型

先略

Fanout模型

生产者

@Test
public void testFanout(){
    rabbitTemplate.convertAndSend("fanoutExchange","","fanout模型的消息");
}

消费者

@Component
public class FanoutConsumer {

    @RabbitListener(bindings = {@QueueBinding(
            value = @Queue,//临时队列
            exchange = @Exchange(value = "fanoutExchange",type = "fanout") //绑定交换机
    )})
    public void consumer1(String message){
        System.out.println("consumer1:"+message);
    }

    @RabbitListener(bindings = {@QueueBinding(
            value = @Queue,
            exchange = @Exchange(value = "fanoutExchange",type = "fanout")
    )})
    public void consumer2(String message){
        System.out.println("consumer2:"+message);
    }

}

Direct模型

生产者

@Test
public void testDirect(){
    rabbitTemplate.convertAndSend("directExchange", "key1", "通过key1这个routing key传递的消息");
    rabbitTemplate.convertAndSend("directExchange", "key2", "通过key2这个routing key传递的消息");
}

消费者

@Component
public class DirectConsumer {

    @RabbitListener(bindings = {
            @QueueBinding(
                    value = @Queue,
                    exchange = @Exchange(value = "directExchange",type = "direct"),
                    key = {"key1","key2"}
            )
    })
    public void consumer1(String message) {
        System.out.println("consumer1:"+message);
    }

    @RabbitListener(bindings = {
            @QueueBinding(
                    value = @Queue,
                    exchange = @Exchange(value = "directExchange",type = "direct"),
                    key = "key1"
            )
    })
    public void consumer2(String message) {
        System.out.println("consumer2:"+message);
    }
}

Topics模型

这个跟Direct模型差不多,上面都略了这次就写一下吧

生产者

@Test
public void testTopics(){
    rabbitTemplate.convertAndSend("topicsExchange", "user.name", "user.name");
    rabbitTemplate.convertAndSend("topicsExchange", "user.name.firstName", "user.name.firstName");
}

消费者

@Component
public class TopicsConsumer {

    @RabbitListener(bindings = {
            @QueueBinding(
                    value = @Queue,
                    exchange = @Exchange(value = "topicsExchange",type = "topic"),
                    key = "user.*"
            )
    })
    public void consumer1(String message) {
        System.out.println("consumer1:"+message);
    }

    @RabbitListener(bindings = {
            @QueueBinding(
                    value = @Queue,
                    exchange = @Exchange(value = "topicsExchange",type = "topic"),
                    key = "user.#"
            )
    })
    public void consumer2(String message) {
        System.out.println("consumer2:"+message);
    }

}

MQ的作用

  • 异步处理请求
  • 应用解耦
©️2020 CSDN 皮肤主题: 数字20 设计师:CSDN官方博客 返回首页