消息队列RabbitMQ相关使用记录

RabbitMQ是实现了高级消息队列协议(AMQP)的开源消息代理软件(亦称面向消息的中间件)。RabbitMQ服务器是用Erlang语言编写的,而集群和故障转移是构建在开放电信平台框架上的。所有主要的编程语言均有与代理接口通讯的客户端库。

安装RabbitMQ(Mac环境)

首先,想要使用RabbitMQ,需要先将RabbitMQ下载到本地,具体方式如下:

# 先检查brew的更新
brew update
# 安装RabbitMQ
brew install rabbitmq

接着需要配置一下RabbitMQ的环境变量:
export PATH=$PATH:/usr/local/opt/rabbitmq/sbin
接着我们就可以启动RabbitMQ了:

# 前台启动
rabbitmq-server
# 后台启动
brew service start rabbitmq
# 后台退出
rabbitmqctl stop

安装RabbitMQ(Ubuntu18.04环境)

步骤如下:

# 先安装RabbitMQ的语言环境,即需要安装Erlang
sudo apt-get install erlang
# 接着安装RabbitMQ
sudo apt-get install rabbitmq-server
# 最后安装RabbitMQ开机自启插件
sudo rabbitmq-plugins enable rabbitmq_management
# 安装完之后系统会默认启动RabbitMQ

常规RabbitMQ操作:

# 启动
sudo rabbitmq-server start
# 重启
sudo rabbitmq-server restart
# 后台退出
sudo rabbitmq-server stop

RabbitMQ的使用

RabbitMQ模式介绍

  • ①简单模式——Hello World
  • ②工作队列模式——Work Queue
  • ③订阅(广播)模式——Publish / subscribe
  • ④路由模式——Routing
  • ⑤通配符模式——Topic

交换机类型介绍(分别对应后三种模式)

  • ①fanout——广播类型,不需要使用RoutingKey
  • ②direct——路由类型,需要使用RoutingKey
  • ③topics——通配符类型,需要使用RoutingKey

在基础使用RabbitMQ时,需要现在项目中添加如下依赖:


    com.rabbitmq
    amqp-client
    5.7.3

  • ①②关于“简单模式”“工作队列模式”,这两个模式都是不包含交换机的,只需要有生产者(Producer)和消费者(Consumer)就可以了,其中工作队列采用轮询的机制,相关示例如下:
    image.png
//*****
//生产者Producer
//*****

public class RabbitProducer {

    public static void main(String[] args) throws IOException, TimeoutException {
        //创建RabbitMQ的连接
        ConnectionFactory connectionFactory = new ConnectionFactory();
        //设定RabbitMQ的服务主机地址,默认为localhost
        connectionFactory.setHost("localhost");
        //设定RabbitMQ的服务端口,默认为5672
        connectionFactory.setPort(5672);
        //设定RabbitMQ的虚拟主机,默认为"/"
        connectionFactory.setVirtualHost("/");
        //设定RabbitMQ的用户名,默认为guest
        connectionFactory.setUsername("guest");
        //设定RabbitMQ的密码,默认为guest
        connectionFactory.setPassword("guest");

        //创建连接,会抛出两个异常
        Connection connection = connectionFactory.newConnection();

        //创建频道,对要发送的信息进行进一步配置封装
        Channel channel = connection.createChannel();
        //声明队列
        /**
         * 参数1:队列的名称
         * 参数2:消息是否持久化,建议为true
         * 参数3:当前消息是否为当前连接对象独有,建议为false
         * 参数4:消息使用之后是否自动删除,建议为false
         * 参数5:额外参数,没有置为null
         */
        channel.queueDeclare("demo_queue",true,false,false,null);
        //创建消息
        String msg = "Hello RabbitMQ!";
        //消息的发送
        /**
         * 参数1:指定消息发送的交换机,默认为"",代表DefaultExchange
         * 参数2:当前消息的路由地址RoutingKey,在简单消息中可以写成跟队列名称一致
         * 参数3:附加参数,若在执行持久化操作时需要写上“MessageProperties.PERSISTENT_TEXT_PLAIN”
         * 参数4:消息对象,记得类型要转为字节数组
         */
        channel.basicPublish("","demo_queue",MessageProperties.PERSISTENT_TEXT_PLAIN,msg.getBytes());

        //关闭资源
        channel.close();
        connection.close();
    }
}
//*****
//消费者Consumer
//*****

public class RabbitConsumer {

    public static void main(String[] args) throws IOException, TimeoutException {
        //创建RabbitMQ的连接
        ConnectionFactory connectionFactory = new ConnectionFactory();
        //设定RabbitMQ的服务主机地址,默认为localhost
        connectionFactory.setHost("localhost");
        //设定RabbitMQ的服务端口,默认为5672
        connectionFactory.setPort(5672);
        //设定RabbitMQ的虚拟主机,默认为"/"
        connectionFactory.setVirtualHost("/");
        //设定RabbitMQ的用户名,默认为guest
        connectionFactory.setUsername("guest");
        //设定RabbitMQ的密码,默认为guest
        connectionFactory.setPassword("guest");

        //创建连接
        Connection connection = connectionFactory.newConnection();

        //创建频道,对要发送的信息进行进一步配置封装
        Channel channel = connection.createChannel();
        //创建消费者,对消息进行接收和处理
        DefaultConsumer defaultConsumer = new DefaultConsumer(channel){
            /**
             * 消息处理
             * @param consumerTag  :消费者标签,用来区分多个消费者
             * @param envelope     :消息相关信息的封装
             * @param properties   :额外参数
             * @param body         :消息本体
             * @throws IOException
             */
            @Override
            public void handleDelivery(String consumerTag,Envelope envelope,AMQP.BasicProperties properties,byte[] body) throws IOException {
                String msg = new String(body, "UTF-8");
                System.out.println("收到的信息内容为:" + msg);
            }
        };

        //执行消息的监听
        /**
         * 参数1:设置监听的队列名称
         * 参数2:设置信息应答模式,为true则代表该语句执行完之后消息自动进行确认,那么此时消息就会自动删除,也可以设置为false随后再执行手动删除
         * 参数3:消费者对象,处理信息
         */
        channel.basicConsume("demo_queue",false,defaultConsumer);

        //手动确认消息的方式如下,其中第二个参数是值是否批量操作
        channel.basicAck(envelope.getDeliveryTag(), false);

        //可以执行关闭资源,但是接收方一般不建议执行关闭
        //channel.close();
        //connection.close();
    }
}
  • ③在使用“订阅模式”时,就需要加入交换机(Exchange)了,且交换机的设定一般存在于生产者中
    image.png
//*****
//生产者Producer
//*****

public class RabbitProducer {

    public static void main(String[] args) throws IOException, TimeoutException {
        //创建RabbitMQ的连接
        ConnectionFactory connectionFactory = new ConnectionFactory();
        //设定RabbitMQ的服务主机地址,默认为localhost
        connectionFactory.setHost("localhost");
        //设定RabbitMQ的服务端口,默认为5672
        connectionFactory.setPort(5672);
        //设定RabbitMQ的虚拟主机,默认为"/"
        connectionFactory.setVirtualHost("/");
        //设定RabbitMQ的用户名,默认为guest
        connectionFactory.setUsername("guest");
        //设定RabbitMQ的密码,默认为guest
        connectionFactory.setPassword("guest");

        //创建连接,会抛出两个异常
        Connection connection = connectionFactory.newConnection();

        //创建频道,对要发送的信息进行进一步配置封装
        Channel channel = connection.createChannel();
        //声明交换机,第一个参数是指交换机名称,第二个参数是指交换机的类型
        channel.exchangeDeclare("my_exchange", BuiltinExchangeType.FANOUT);
        //声明队列
        channel.queueDeclare("demo_queue_1",true,false,false,null);
        channel.queueDeclare("demo_queue_2",true,false,false,null);
        //执行交换机和队列的绑定,第三个参数是RoutingKey,广播模式不需要填写
        channel.queueBind("demo_queue_1","my_exchange","");
        channel.queueBind("demo_queue_2","my_exchange","");
        //创建消息
        String msg = "Hello RabbitMQ!";
        //消息的发送
        channel.basicPublish("my_exchange","",MessageProperties.PERSISTENT_TEXT_PLAIN,msg.getBytes());

        //关闭资源
        channel.close();
        connection.close();
    }
}
  • ④在使用“路由模式”时,此时需要使用上RoutingKey这个参数,用于在原有广播模式的基础上再根据RoutingKey进行定向绑定队列
    image.png
//*****
//生产者Producer
//*****

public class RabbitProducer {

    public static void main(String[] args) throws IOException, TimeoutException {
        //创建RabbitMQ的连接
        ConnectionFactory connectionFactory = new ConnectionFactory();
        //设定RabbitMQ的服务主机地址,默认为localhost
        connectionFactory.setHost("localhost");
        //设定RabbitMQ的服务端口,默认为5672
        connectionFactory.setPort(5672);
        //设定RabbitMQ的虚拟主机,默认为"/"
        connectionFactory.setVirtualHost("/");
        //设定RabbitMQ的用户名,默认为guest
        connectionFactory.setUsername("guest");
        //设定RabbitMQ的密码,默认为guest
        connectionFactory.setPassword("guest");

        //创建连接,会抛出两个异常
        Connection connection = connectionFactory.newConnection();

        //创建频道,对要发送的信息进行进一步配置封装
        Channel channel = connection.createChannel();
        //声明交换机,此时交换机的类型为direct
        channel.exchangeDeclare("my_exchange", BuiltinExchangeType.DIRECT);
        //声明队列
        channel.queueDeclare("demo_queue_1",true,false,false,null);
        channel.queueDeclare("demo_queue_2",true,false,false,null);
        //执行交换机和队列的绑定,此时需要携带上特定的RoutingKey
        channel.queueBind("demo_queue_1","my_exchange","user.save");
        channel.queueBind("demo_queue_2","my_exchange","user.show");
        //创建消息
        String msg1 = "Hello RabbitMQ!_for_save";
        String msg2 = "Hello RabbitMQ!_for_show";
        //消息的发送,此时消息1只会发送给队列1,而消息2只会发送给队列2
        channel.basicPublish("my_exchange","user.save",MessageProperties.PERSISTENT_TEXT_PLAIN,msg1.getBytes());
        channel.basicPublish("my_exchange","user.show",MessageProperties.PERSISTENT_TEXT_PLAIN,msg2.getBytes());

        //关闭资源
        channel.close();
        connection.close();
    }
}
  • ⑤关于“通配符模式”时,它相当于路由模式的细分优化版本,通过特定的通配符对形式相近的RoutingKey达到统一操作的目的
    image.png
//*****
//生产者Producer
//*****

public class RabbitProducer {

    public static void main(String[] args) throws IOException, TimeoutException {
        //创建RabbitMQ的连接
        ConnectionFactory connectionFactory = new ConnectionFactory();
        //设定RabbitMQ的服务主机地址,默认为localhost
        connectionFactory.setHost("localhost");
        //设定RabbitMQ的服务端口,默认为5672
        connectionFactory.setPort(5672);
        //设定RabbitMQ的虚拟主机,默认为"/"
        connectionFactory.setVirtualHost("/");
        //设定RabbitMQ的用户名,默认为guest
        connectionFactory.setUsername("guest");
        //设定RabbitMQ的密码,默认为guest
        connectionFactory.setPassword("guest");

        //创建连接,会抛出两个异常
        Connection connection = connectionFactory.newConnection();

        //创建频道,对要发送的信息进行进一步配置封装
        Channel channel = connection.createChannel();
        //声明交换机,此时交换机的类型为topic
        channel.exchangeDeclare("my_exchange", BuiltinExchangeType.TOPIC);
        //声明队列
        channel.queueDeclare("demo_queue_1",true,false,false,null);
        channel.queueDeclare("demo_queue_2",true,false,false,null);
        channel.queueDeclare("demo_queue_3",true,false,false,null);
        //执行交换机和队列的绑定,此时需要携带上特定的RoutingKey
        channel.queueBind("demo_queue_1","my_exchange","user.save");
        channel.queueBind("demo_queue_2","my_exchange","user.show");
        channel.queueBind("demo_queue_3","my_exchange","user.show.age");
        //创建消息
        String msg1 = "Hello RabbitMQ!_one";
        String msg2 = "Hello RabbitMQ!_two";
        String msg3 = "Hello RabbitMQ!_three";
        String msg4 = "Hello RabbitMQ!_four";
        String msg5 = "Hello RabbitMQ!_five";
        //消息的发送,其中“#”代表后续任意层级(可以是0层),而“*”只代表后续一个层级
        channel.basicPublish("my_exchange","user.save",MessageProperties.PERSISTENT_TEXT_PLAIN,msg1.getBytes());
        channel.basicPublish("my_exchange","user.show",MessageProperties.PERSISTENT_TEXT_PLAIN,msg2.getBytes());
        channel.basicPublish("my_exchange","user.#",MessageProperties.PERSISTENT_TEXT_PLAIN,msg3.getBytes());
        channel.basicPublish("my_exchange","user.*",MessageProperties.PERSISTENT_TEXT_PLAIN,msg4.getBytes());
        channel.basicPublish("my_exchange","user.*.*",MessageProperties.PERSISTENT_TEXT_PLAIN,msg5.getBytes());
        /**
         * 上述语句结果为:
         * 消息1 -> demo_queue_1
         * 消息2 -> demo_queue_2
         * 消息3 -> demo_queue_1,demo_queue_2,demo_queue_3
         * 消息4 -> demo_queue_1,demo_queue_2
         * 消息5 -> demo_queue_3
         */

        //关闭资源
        channel.close();
        connection.close();
    }
}

RabbitMQ集成SpringBoot

1.在项目中导入相关的依赖


    org.springframework.boot
    spring-boot-starter-amqp
    2.1.7.RELEASE

2.在SpringBoot项目中基础使用RabbitMQ一般需要五个部分

  • ①在项目的配置文件application.yml中添加
spring:
  rabbitmq:
    host: localhost
    port: 5672
    username: guest
    password: guest
    virtual-host: /my_host
  • ②接着项目的启动类不需要修改,默认即可
@SpringBootApplication
public class RabbitSpringBootApplication {
    public static void main(String[] args) {
        SpringApplication.run(RabbitSpringBootApplication.class,args);
    }
}
  • ③随后我们创建一个配置类,这次使用Topics模式【记得“创建队列”、“创建交换机”、“执行绑定”这三个步骤方法都需要加上“@Bean”注解
@Configuration
public class RabbitConfiguration {

    public static final String QUEUE_A = "demo_queue_a";
    public static final String QUEUE_B = "demo_queue_b";
    public static final String QUEUE_C = "demo_queue_c";

    public static final String EXCHANGE_A = "demo_exchange_a";
    public static final String EXCHANGE_B = "demo_exchange_b";
    public static final String EXCHANGE_C = "demo_exchange_c";

    public static final String ROUTINGKEY_A = "demo_routingkey_a";
    public static final String ROUTINGKEY_B = "demo_routingkey_b";
    public static final String ROUTINGKEY_C = "demo_routingkey_c";

    //创建队列对象
    @Bean
    public Queue demoQueue() {
        //需要注意的是,此处的Queue简写方式的完整形式为"Queue(QUEUE_A,true,false,false,null)"
        //第二个参数表示是否持久化
        //第三个参数表示是当前消息是否为当前队列独有
        //第四个参数表示是否自动删除
        return new Queue(QUEUE_A,true);
    }

    //创建交换机
    @Bean
    public Exchange demoExchange() {
        //需要注意的是,这里的完整形式为"XxxExchange(EXCHANGE_A,true,false)"
        //第二个参数表示是否持久化
        //第三个参数表示是否自动删除
        return new TopicExchange(EXCHANGE_A);
        //return new FanoutExchange(EXCHANGE_A);
        //return new DirectExchange(EXCHANGE_A);

    }

    //进行队列和交换机的绑定
    @Bean
    public Binding itemBinding() {
        return BindingBuilder.bind(demoQueue()).to(demoExchange()).with(ROUTINGKEY_A).noargs();
    }
}
  • ④接下来我们开始创建生产者(Producer)
@Component
public class RabbitProducer {

    @Autowired
    private RabbitTemplate rabbitTemplate;

    public void sendMsg() {
        String msg = "Hello RabbitMQ!";
        //发送消息
        rabbitTemplate.convertAndSend(RabbitConfiguration.EXCHANGE_A,RabbitConfiguration.ROUTINGKEY_A,msg);
    }
}
  • ⑤最后是创建消费者(Consumer)
@Component
@RabbitListener(queues = RabbitConfiguration.QUEUE_A)
public class RabbitConsumer {

    @RabbitHandler
    public void getMsg(String msg) {
        System.out.println("收到的信息为:" + msg);
    }
}

整合微服务项目

  • ①导入相关依赖

    org.springframework.boot
    spring-boot-starter-amqp
    2.1.7.RELEASE

  • ②编写application.yml配置文件
spring:
  rabbitmq:
    host: 192.168.xx.xx # RabbitMQ服务端IP地址
    port: 5672 # RabbitMQ服务端端口号
    username: your_username # RabbitMQ的账号
    password: your_password # RabbitMQ的密码
    virtual-host: /your_vh_addr # RabbitMQ的虚拟主机名称
    publisher-confirms: true # 启用“发布确定”
    publisher-returns: true # 启用“返回确定”
    template: # 模板信息,接收方可以不写
        exchange: your_exchange_temp # 配置默认的交换机,格式一般为“xx.xx.xx”

接下来在需要发送消息的模块方法中先注入AmqpTemplate对象,接着定义一个发送消息的方法,需要的时候可以直接调用sendMsg这个方法发送消息。

@Autowired
private AmqpTemplate amqpTemplate;

public void sendMsg(String type,Long id) {
        try {
            // 发送消息,其中参数1代表RoutingKey的值,参数2代表发送的信息内容
            // 该方式发送消息默认消息就是持久化的,即“deliveryMode=2”
            this.amqpTemplate.convertAndSend("xx.xx."+type, id);
        } catch (AmqpException e) {
            e.printStackTrace();
        }
    }

此时作为接收方则可以创建一个监听类来处理业务

@Component
public class MsgListener {
    
    @Autowired
    private DemoService demoService;
    
    //此时在“topic”类型下,RabbitMQ会根据发送方提供的交换机名称和key值进行匹配,符合要求的就会发送到对应的队列中
    @RabbitListener(bindings = @QueueBinding(
            value = @Queue(value = "自定义队列名称",durable = "true"),
            exchange = @Exchange(value = "交换机名称",ignoreDeclarationExceptions = "true",type = "topic"),
            key = {"自定义key1","自定义key2"}
    ))
    public void save(Long id) throws IOException{
        if (id ==null) {
            return;
        }
        this.demoService.save(id);
    }
}

你可能感兴趣的:(消息队列RabbitMQ相关使用记录)