RabbitMq初识

一、认识

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-SWerRPXz-1586753363532)(RabiitMQ.assets/image-20200409124054690.png)]

二、安装

1.docker安装

docker run -it --rm --name rabbitmq -p 5672:5672 -p 15672:15672 rabbitmq:3-management
[root@localhost ~]# docker logs rabbitmq
 Starting RabbitMQ 3.8.3 on Erlang 22.3
 Copyright (c) 2007-2020 Pivotal Software, Inc.
 Licensed under the MPL 1.1. Website: https://rabbitmq.com

  ##  ##      RabbitMQ 3.8.3
  ##  ##
  ##########  Copyright (c) 2007-2020 Pivotal Software, Inc.
  ######  ##
  ##########  Licensed under the MPL 1.1. Website: https://rabbitmq.com

  Doc guides: https://rabbitmq.com/documentation.html
  Support:    https://rabbitmq.com/contact.html
  Tutorials:  https://rabbitmq.com/getstarted.html
  Monitoring: https://rabbitmq.com/monitoring.html

  Logs: <stdout>

  Config file(s): /etc/rabbitmq/rabbitmq.conf

  Starting broker...2020-02-04 09:50:28.118 [info] <0.278.0>
 node           : rabbit@372182626d08
 home dir       : /var/lib/rabbitmq
 config file(s) : /etc/rabbitmq/rabbitmq.conf
 cookie hash    : i+iD3VVt4nHYnHzfhKS/mw==
 log(s)         : <stdout>
 database dir   : /var/lib/rabbitmq/mnesia/rabbit@372182626d08

2. 配置文件

[root@localhost lib]$ cat
/var/lib/docker/volumes/8ed25145a2d59a1b7ba4698bd326f2fe291c843e5158a27d30d3bb8a5ae2017c/_data/config/generated/rabbitmq.config
<!--{loopback_users,[]} 打开:guest user from anywhere on the network-->
[{rabbitmq_management,[{tcp_config,[{port,15672}]}]},
  {rabbit,[{tcp_listeners,[5672]},{loopback_users,[]}]}].

2.RabbitMQ的简单指令

启动服务:rabbitmq-server -detached【 /usr/local/rabbitmq/sbin/rabbitmq-server  -detached 】
重启服务:rabbitmq-server restart
关闭服务:rabbitmqctl stop
查看状态:rabbitmqctl status
列出角色:rabbitmqctl list_users
开启某个插件:rabbitmq-pluginsenable xxx
关闭某个插件:rabbitmq-pluginsdisablexxx
注意:重启服务器后生效。

三、分类

rabbitmq.getsatrted

1. Direct直连

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-jJrXEhm3-1586753363535)(RabiitMQ.assets/image-20200409125445036.png)]

P:生产者,发送消息

C:消费者,等待消息

queue:消息队列,类似邮箱

  • 依赖
  <!--rabbitMQ-->
        <dependency>
            <groupId>com.rabbitmq</groupId>
            <artifactId>amqp-client</artifactId>
            <version>5.8.0</version>
        </dependency>
  • 代码示例1:
    @Test
    public void sendMessage() throws IOException, TimeoutException {
        //创建连接mq的连接工厂对象
        ConnectionFactory connectionFactory = new ConnectionFactory();
        connectionFactory.setHost("192.168.56.15");
        connectionFactory.setPort(5672);
        connectionFactory.setVirtualHost("/dev");
        connectionFactory.setUsername("dev");
        connectionFactory.setPassword("dev");
        //获取链接对象
        Connection connection = connectionFactory.newConnection();
        //获取链接通道对象
        Channel channel = connection.createChannel();
        //通道绑定消息队列
        //参数1:队列不存在自动创建
        //参数2:durable是否持久化到磁盘,即mq关闭后,数据会持久化到磁盘,false则关闭后删除
        //参数3:exclusive是否独占队列,false指其他连接也可以通过该队列
        // 参数4:autoDelete是否消费后立即删除队列,false不自动删除
        // 参数5:额外参数
        channel.queueDeclare("hello", false, false, false, null);

        //发布消息
        //参数1:交换机  参数2:routingKey队列名称 参数3:额外设置  参数4:消息内容
        channel.basicPublish("", "hello", null, "hello rabbitmq".getBytes());
        channel.close();
        connection.close();
    }
  • 问题:
  1. 用户名密码错误
com.rabbitmq.client.AuthenticationFailureException: ACCESS_REFUSED - Login was refused using authentication mechanism PLAIN. For details see the broker logfile.

	at com.rabbitmq.client.impl.AMQConnection.start(AMQConnection.java:384)
  • 代码示例2:
public class RabiitMqTest {
    //创建连接mq的连接工厂对象
    public static ConnectionFactory connectionFactory;
    static {
        connectionFactory = new ConnectionFactory();
        connectionFactory.setHost("192.168.56.15");
        connectionFactory.setPort(5672);
        connectionFactory.setVirtualHost("/");
        connectionFactory.setUsername("guest");
        connectionFactory.setPassword("guest");
    }

    private static Connection createConnection() {
        try {
            //获取链接对象
            return connectionFactory.newConnection();
        } catch (Exception e) {
            e.printStackTrace();
        }
        return null;
    }

    private void closeConnection(Connection connection,Channel channel){
        try {
            if(channel != null) channel.close();
            if(connection != null)connection.close();
        } catch (IOException e) {
            e.printStackTrace();
        } catch (TimeoutException e) {
            e.printStackTrace();
        }
    }

    @Test
    public void publisherSendMessage() throws IOException {
        Connection connection = createConnection();
        //获取链接通道对象
        Channel channel = connection.createChannel();
        //通道绑定消息队列
        //参数1:队列不存在自动创建
        //参数2:durable是否持久化到磁盘,即mq关闭后,数据会持久化到磁盘,false则关闭后删除队列,队列中未消费的消息将会丢失
        //参数3:exclusive是否独占队列,false指其他连接也可以通过该队列
        // 参数4:autoDelete是否消费者消费且关闭后立即删除队列,false不自动删除
        // 参数5:额外参数
        channel.queueDeclare("hai", true, false, true, null);

        //发布消息
        //参数1:交换机  参数2:routingKey队列名称 参数3:额外设置(持久化消息,关闭重启后消息不会消失)  参数4:消息内容
        channel.basicPublish("", "hai", MessageProperties.PERSISTENT_TEXT_PLAIN, "hello rabbitmq".getBytes());

        closeConnection(connection,channel);

    }

    public static void main(String[] args) throws IOException {
        Connection connection = createConnection();
        Channel channel = connection.createChannel();
        channel.queueDeclare("hai", true, false, true, null);
        //消费消息
        //参数1:消费队列名称
        //参数2:开始消费自动确认机制
        //参数3:消费消息时回调接口
        channel.basicConsume("hai",true,new DefaultConsumer(channel){
            @Override
            public void handleDelivery(String consumerTag, Envelope envelope, AMQP.BasicProperties properties, byte[] body) throws IOException {
                System.out.println("==================" + body);
            }
        });
    }
}

2. Work queue

也称Task queues,任务模型。消息处理比较耗时时,生产消息远大于消费消息,会导致消息堆积。Work queue 模式:多个消费者绑定一个队列共同消费队列消息,消息一旦消费即消失,不会重复执行。

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-YxKklJne-1586753363536)(RabiitMQ.assets/image-20200410131040607.png)]

P:生产者,发送消息

C1、C2:消费者,消费消息

默认情况下,RabbitMQ将按顺序将每个消息发送给下一个使用者。平均每个消费者将得到相同数量的消息。这种分发消息的方式称为循环。在三个或更多的工人中尝试这种方法。

即使一个消费者消费的很慢,也是平均分配,消息循环分配到消费者的队列,等待消费者消费。

  • 代码1:略
两个消费者消费同一个队列
  • 代码2:手动确认
package rabbitmq;

import com.rabbitmq.client.*;
import org.junit.Test;

import java.io.IOException;

public class WorkQueueConsumer1 {

    public static void main(String[] args) throws IOException {
        Connection connection = RabbitMqUtil.createConnection();
        Channel channel = connection.createChannel();
        channel.basicQos(1);//每次消费通道只能消费一个消息
        channel.queueDeclare("workQueue", true, false, false, null);
        //消费消息
        //参数1:消费队列名称
        //参数2:开始消费自动确认机制
        //参数3:消费消息时回调接口
        channel.basicConsume("workQueue",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));
                //手动确认消息 参数1:确认队列中具体的消息  参数2:是否开启多个消息同时确认
                channel.basicAck(envelope.getDeliveryTag(),false);
                try {
                    Thread.sleep(1000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        });
    }
}

3. Fanout

广播 putting it all together

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-ES8poDRB-1586753363537)(RabiitMQ.assets/image-20200410142817530.png)]

  • 可以有多个消费者

  • 每个消费者有自己的队列,每个队列绑定到交换机

  • 生产者发送的消息到交换机,交换机决定发给哪个队列,生产者无法决定

  • 交换机把消息发送给绑定过的所有队列,临时队列,消费者消费,队列删除。

  • 队列的消费者都能拿到消息,实现一条消息多个消费者消费。

  • 代码示例1:

publisher

package rabbitmq;

import com.rabbitmq.client.Channel;
import com.rabbitmq.client.Connection;
import com.rabbitmq.client.MessageProperties;

import java.io.IOException;

import static rabbitmq.RabbitMqUtil.createConnection;

public class FanoutPublisher {
    public static void main(String[] args) throws IOException {

        Connection connection = createConnection();
        //获取链接通道对象
        Channel channel = connection.createChannel();
        //参数1:交换机名称    参数2:交换机type
        channel.exchangeDeclare("logs","fanout");
        channel.basicPublish("logs","",null,"fanout type".getBytes());
        RabbitMqUtil.closeConnection(connection,channel);
    }
}

consumer1:

package rabbitmq;

import com.rabbitmq.client.*;

import java.io.IOException;

import static rabbitmq.RabbitMqUtil.createConnection;

public class FanoutConsumer2 {
    public static void main(String[] args) throws IOException {
        Connection connection = createConnection();
        Channel channel = connection.createChannel();
        channel.exchangeDeclare("logs","fanout");
        //临时队列
        String queue = channel.queueDeclare().getQueue();
        //绑定交换机  参数1:队列名,参数2:交换机名,参数3:routingKey
        channel.queueBind(queue,"logs","");
        channel.basicConsume(queue,true,new DefaultConsumer(channel){
            @Override
            public void handleDelivery(String consumerTag, Envelope envelope, AMQP.BasicProperties properties, byte[] body) throws IOException {
                System.out.println("消费者3:=====" + new String(body));
            }
        });
    }
}

consumer2、 consumer3同上

4. Routing

4.1 Direct(订阅)

不同消息被不同的队列消费

  • 队列与交换机的绑定,要指定一个RoutingKey

  • 向Exchange发送消息时,必须指定RoutingKey

  • Exchange根据RoutingKey判断消息发送

![image-20200412222236932](RabiitMQ.assets/image-20200412222236932.png

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-RZYJ9HOg-1586753363538)(RabiitMQ.assets/image-20200412222406467.png)]

  • 代码示例1

    publisher

    package rabbitmq;
    
    import com.rabbitmq.client.Channel;
    import com.rabbitmq.client.Connection;
    import com.rabbitmq.client.MessageProperties;
    
    import java.io.IOException;
    
    import static rabbitmq.RabbitMqUtil.createConnection;
    
    public class RoutingDirectPublisher {
        public static void main(String[] args) throws IOException {
    
            Connection connection = createConnection();
            //获取链接通道对象
            Channel channel = connection.createChannel();
            //参数1:交换机名称    参数2:交换机type
            String exchangeName = "logs_direct";
            channel.exchangeDeclare(exchangeName,"direct");
            String routingKey = "error";
            channel.basicPublish(exchangeName,routingKey, MessageProperties.PERSISTENT_TEXT_PLAIN,("基于direct模式的【"+ routingKey+"】的交换机发送消息:").getBytes());
            RabbitMqUtil.closeConnection(connection,channel);
        }
    }
    

    consumer1:

    package rabbitmq;
    
    import com.rabbitmq.client.*;
    
    import java.io.IOException;
    
    import static rabbitmq.RabbitMqUtil.createConnection;
    
    public class RoutingDirectConsumer1 {
        public static void main(String[] args) throws IOException {
            Connection connection = createConnection();
            Channel channel = connection.createChannel();
            String exchangeName = "logs_direct";
            channel.exchangeDeclare(exchangeName,"direct");
            //临时队列
            String queue = channel.queueDeclare().getQueue();
            //绑定交换机  参数1:队列名,参数2:交换机名,参数3:routingKey
            channel.queueBind(queue,exchangeName,"error");
            channel.queueBind(queue,exchangeName,"info");
            channel.queueBind(queue,exchangeName,"warning");
            channel.basicConsume(queue,true,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));
                }
            });
        }
    }
    

    consumer2

    package rabbitmq;
    
    import com.rabbitmq.client.*;
    
    import java.io.IOException;
    import java.util.concurrent.Exchanger;
    
    import static rabbitmq.RabbitMqUtil.createConnection;
    
    public class RoutingDirectConsumer2 {
        public static void main(String[] args) throws IOException {
            Connection connection = createConnection();
            Channel channel = connection.createChannel();
            String exchangeName = "logs_direct";
            channel.exchangeDeclare(exchangeName,"direct");
            //临时队列
            String queue = channel.queueDeclare().getQueue();
            //绑定交换机  参数1:队列名,参数2:交换机名,参数3:routingKey
            channel.queueBind(queue,exchangeName,"error");
            channel.basicConsume(queue,true,new DefaultConsumer(channel){
                @Override
                public void handleDelivery(String consumerTag, Envelope envelope, AMQP.BasicProperties properties, byte[] body) throws IOException {
                    super.handleDelivery(consumerTag, envelope, properties, body);
                    System.out.println("消费者2:=====" + new String(body));
                }
            });
        }
    }
    

4.2 Topic

可以根据RoutingKey把消息路由到不同的队列,可以使用通配符。

多个单词之间以"."分割,例如:item.insert

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-ak2MgVQ6-1586753363539)(RabiitMQ.assets/image-20200412230317294.png)]

  • 通配符

    * (star) can substitute for exactly one word. 匹配不多不少1个单词

    # (hash) can substitute for zero or more words. 匹配一个或多个单词

  • 代码示例1

publisher

package rabbitmq;

import com.rabbitmq.client.Channel;
import com.rabbitmq.client.Connection;
import com.rabbitmq.client.MessageProperties;

import java.io.IOException;

import static rabbitmq.RabbitMqUtil.createConnection;

public class RoutingTopicPublisher {
    public static void main(String[] args) throws IOException {
        Connection connection = createConnection();
        //获取链接通道对象
        Channel channel = connection.createChannel();
        //参数1:交换机名称    参数2:交换机type
        String exchangeName = "logs_topic";
        channel.exchangeDeclare(exchangeName,"topic");
        // String routingKey = "user.#";
        String routingKey = "user.save.info";
        channel.basicPublish(exchangeName,routingKey, MessageProperties.PERSISTENT_TEXT_PLAIN,("基于direct模式的【"+ routingKey+"】的交换机发送消息:").getBytes());
        RabbitMqUtil.closeConnection(connection,channel);
    }
}

consumer1

package rabbitmq;

import com.rabbitmq.client.*;

import java.io.IOException;

import static rabbitmq.RabbitMqUtil.createConnection;

public class RoutingTopicConsumer2 {
    public static void main(String[] args) throws IOException {
        Connection connection = createConnection();
        Channel channel = connection.createChannel();
        String exchangeName = "logs_topic";
        channel.exchangeDeclare(exchangeName,"topic");
        //临时队列
        String queue = channel.queueDeclare().getQueue();
        //绑定交换机  参数1:队列名,参数2:交换机名,参数3:routingKey
        channel.queueBind(queue,exchangeName,"#");
        channel.basicConsume(queue,true,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));
            }
        });
    }
}

consumer2:

package rabbitmq;

import com.rabbitmq.client.*;

import java.io.IOException;

import static rabbitmq.RabbitMqUtil.createConnection;

public class RoutingTopicConsumer1 {
    public static void main(String[] args) throws IOException {
        Connection connection = createConnection();
        Channel channel = connection.createChannel();
        String exchangeName = "logs_topic";
        channel.exchangeDeclare(exchangeName,"topic");
        //临时队列
        String queue = channel.queueDeclare().getQueue();
        //绑定交换机  参数1:队列名,参数2:交换机名,参数3:routingKey
        channel.queueBind(queue,exchangeName,"user.*");
        channel.basicConsume(queue,true,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));
            }
        });
    }
}

四、SpringBoot整合RabbitMQ

SpringBoot支持AMQP协议

  • 依赖
  <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>2.1.13.RELEASE</version>
    </parent>
    <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>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <scope>test</scope>
        </dependency>
        <dependency>
            <groupId>org.springframework.amqp</groupId>
            <artifactId>spring-rabbit-test</artifactId>
            <scope>test</scope>
        </dependency>
    </dependencies>
spring:
  application:
    name: rabbitmq-springboot

  rabbitmq:
    host: 192.168.56.15
    port: 5672
    username: guest
    password: guest
    virtual-host: /
  • 代码示例1

publisher

package springboot.rabbitmq;

import com.springboot.rabbitmq.RabbitMqSpringBootApplication;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.amqp.rabbit.core.RabbitTemplate;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.test.context.junit4.SpringRunner;

@SpringBootTest(classes = RabbitMqSpringBootApplication.class)
@RunWith(SpringRunner.class)
public class Publisher {

    @Autowired
    private RabbitTemplate rabbitTemplate;

    @Test
    public void direct() {
        rabbitTemplate.convertAndSend("direct", "hello direct");
    }

    @Test
    public void work() {
        for (int i = 0; i < 10; i++) {
            //springboot work模式默认均分
            rabbitTemplate.convertAndSend("work", "hello work queue" + i);
        }
    }

    @Test
    public void fanout() {
        rabbitTemplate.convertAndSend("fanoutLog","", "hello fanout" );
    }

    @Test
    public void routingDirect() {
        rabbitTemplate.convertAndSend("user","user.info", "hello fanout" );
    }

    @Test
    public void routingTopic() {
        rabbitTemplate.convertAndSend("product","pro.info", "hello fanout" );
    }
}

DirectConsumer

package com.springboot.rabbitmq.consumer;

import org.springframework.amqp.rabbit.annotation.Queue;
import org.springframework.amqp.rabbit.annotation.RabbitHandler;
import org.springframework.amqp.rabbit.annotation.RabbitListener;
import org.springframework.stereotype.Component;

@Component
//默认 持久化,非独占,不自动删除
@RabbitListener(queuesToDeclare = @Queue(value = "direct"))
public class DirectConsumer {

    @RabbitHandler
    public void receive(String message){
        System.out.println("===message===" + message);
    }
}

consumer

@RabbitListener 可以写在方法上,此时不用使用@RabbitHandler

package com.springboot.rabbitmq.consumer;

import org.springframework.amqp.rabbit.annotation.Exchange;
import org.springframework.amqp.rabbit.annotation.Queue;
import org.springframework.amqp.rabbit.annotation.QueueBinding;
import org.springframework.amqp.rabbit.annotation.RabbitListener;
import org.springframework.stereotype.Component;

@Component
public class Consumer {

    @RabbitListener(queuesToDeclare = @Queue("work"))
    public void workQueue1(String message) {
        System.out.println("===message1===" + message);
    }


    @RabbitListener(queuesToDeclare = @Queue("work"))
    public void workQueue2(String message) {
        System.out.println("===message2===" + message);
    }


    @RabbitListener(bindings = {
            @QueueBinding(value = @Queue,//不声明名称即创建临时队列
                    exchange = @Exchange(value = "fanoutLog", type = "fanout"))
    })
    public void fanout1(String message) {
        System.out.println("===message1===" + message);
    }

    @RabbitListener(bindings = {
            @QueueBinding(value = @Queue,//不声明名称即创建临时队列
                    exchange = @Exchange(value = "fanoutLog", type = "fanout"))
    })
    public void fanout2(String message) {
        System.out.println("===message2===" + message);
    }


    @RabbitListener(bindings = {
            @QueueBinding(value = @Queue,
                    exchange = @Exchange(value = "user", type = "direct"),
                    key = {"user.error"})
    })
    public void routingDirect(String message) {
        System.out.println("===message1===" + message);
    }

    @RabbitListener(bindings = {
            @QueueBinding(value = @Queue,
                    exchange = @Exchange(value = "user", type = "direct"),
                    key = {"user.info"})
    })
    public void routingDirect2(String message) {
        System.out.println("===message2===" + message);
    }

    @RabbitListener(bindings = {
            @QueueBinding(value = @Queue,
                    exchange = @Exchange(value = "product", type = "topic"),
                    key = {"pro.*"})
    })
    public void routingTopic(String message) {
        System.out.println("===message1===" + message);
    }

    @RabbitListener(bindings = {
            @QueueBinding(value = @Queue,
                    exchange = @Exchange(value = "product", type = "topic"),
                    key = {"#"})
    })
    public void routingTopic2(String message) {
        System.out.println("===message2===" + message);
    }
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值