85-java-rabbitMQ(2)-rabbitMQ的javaAPI

7 篇文章 0 订阅
4 篇文章 0 订阅

本文按照两部分来说明

java连接rabbitMQ的工作模式


基础部分
(1) pom依赖

<dependencies>
        <!-- 这是java连接rabbitMQ的原生api依赖 -->
        <dependency>
            <groupId>com.rabbitmq</groupId>
            <artifactId>amqp-client</artifactId>
            <version>5.6.0</version>
        </dependency>
        <dependency>
            <groupId>org.slf4j</groupId>
            <artifactId>slf4j-api</artifactId>
            <version>1.7.28</version>
        </dependency>
        <dependency>
            <groupId>org.slf4j</groupId>
            <artifactId>slf4j-log4j12</artifactId>
            <version>1.7.25</version>
        </dependency>
        <dependency>
            <groupId>log4j</groupId>
            <artifactId>log4j</artifactId>
            <version>1.2.17</version>
        </dependency>
        <dependency>
            <groupId>junit</groupId>
            <artifactId>junit</artifactId>
            <version>4.13</version>
        </dependency>

    </dependencies>

(2) 基础连接对象

package com.gl.rabbitmq.original;

import com.rabbitmq.client.Connection;
import com.rabbitmq.client.ConnectionFactory;

import java.io.IOException;
import java.util.concurrent.TimeoutException;

/**
 * @author gl
 * @time 2020-06-21 15:53
 * @function : 这是我们创建连接到rabbitMQ的工具类
 * @step :
 */
public class ConnUtil {

    public static Connection getConnection() throws IOException, TimeoutException {
        //1.连接工厂
        ConnectionFactory factory = new ConnectionFactory();
        //2.设置主机
        factory.setHost("127.0.0.1");
        //3.设置端口
        factory.setPort(5672);
        //4.设置vhost也就是连接哪个库,这里使用我们自定义的虚拟机
            factory.setVirtualHost("/vhost_rabbitMQ");
        //5.设置用户名,使用我们自定义的用户
        factory.setUsername("rabbitMQ");
        //6.设置密码
        factory.setPassword("rabbitMQ");
        //7.获取连接
        Connection connection = factory.newConnection();

        return connection;
    }
}

1.work queues模式

在这里插入图片描述

1.1 架构
work queues模式是生产者生产消息发送到queue队列中,可以有一个或者多个消费者去读取这个queue,读取的方式可以为轮询等策略.注意,一条消息只会有一个消费者消费,不会同时发送给多个消费者.例如queue中有10条消息,以轮询的方式发送给两个消费者,则每个消费者5条消息.
1.2 应用场景
适用于处理复杂业务,相当于实现了一个消费端集群的模式,可以让多个消费者来处理共同的一项业务.
1.3 代码

package com.gl.rabbitmq.original;

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

import java.io.IOException;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.TimeoutException;

/**
 * @author gl
 * @time 2020-06-21 16:04
 * @function : 第一种模式: 工作队列
 * @step :
 */
public class WorkQueueDemo {
    private static final String QUEUE_NAME = "queue_test1";
    private CountDownLatch countDownLatch = new CountDownLatch(1);

    /**
     * 生产者
     * @throws IOException
     * @throws TimeoutException
     */
    @Test
    public void producer() throws IOException, TimeoutException {
        //1.通过util对象创建connection对象
        Connection connection = ConnUtil.getConnection();

        //2.获取channel
        Channel channel = connection.createChannel();
        //3.声明队列
        /**
         * queue:声明队列的名称
         * durable: 是否持久化
         * exclusive: 是否独占连接,如果连接关闭则自动删除,如果将此参数设置为true可用于临时队列的创建
         * autoDelete: 是否自动删除此队列,如果将自参数和exclusive都设置为true就可实现临时队列.
         * arguments: 可以设置一个队列的扩展参数,比如: 设置队列存活时间
         */
        channel.queueDeclare(QUEUE_NAME,true,false,false,null);
        //4.发送消息
        /**
         * String exchange, String routingKey, BasicProperties props, byte[] body
         * exchange: 交换机,如果传递""空串表示使用默认的交换机
         * routingKey: 路由key,交换机根据路由key来将消息转发到指定的队列,如果使用默认的交换机,routingKey设置队列的名字
         * props: 消息的属性
         * body: 消息的内容
         */
        String msg = "hello world";
        channel.basicPublish("",QUEUE_NAME,null,msg.getBytes());
        System.out.println("发送成功!!");
        channel.close();

    }

    /**
     * 消费者
     * @throws IOException
     * @throws TimeoutException
     * @throws InterruptedException
     */
    @Test
    public void consumer1() throws IOException, TimeoutException, InterruptedException {
        //1.通过util对象创建connection对象
        Connection connection = ConnUtil.getConnection();

        //2.获取channel
        Channel channel = connection.createChannel();
        //3.声明队列
        /**
         * 这里可以不用声明队列,但是要确定该队列一定是在mq中存在的,不然就会代码报错.
         * queue:声明队列的名称
         * durable: 是否持久化
         * exclusive: 是否独占连接,如果连接关闭则自动删除,如果将此参数设置为true可用于临时队列的创建
         * autoDelete: 是否自动删除此队列,如果将自参数和exclusive都设置为true就可实现临时队列.
         * arguments: 可以设置一个队列的扩展参数,比如: 设置队列存活时间
         */
        channel.queueDeclare(QUEUE_NAME,true,false,false,null);

        //声明一个消费者
        DefaultConsumer consumer = new DefaultConsumer(channel){
            /**
             * 接收到消息时,调用此方法
             * @param consumerTag 消费者标签
             * @param envelope  信封---其实就是mq对于此消息的封装的东西,比如消息id,exchange等
             * @param properties 消息属性
             * @param body 消息内容
             * @throws IOException
             */
            @Override
            public void handleDelivery(String consumerTag, Envelope envelope, AMQP.BasicProperties properties, byte[] body) throws IOException {
                //交换机
                java.lang.String exchange = envelope.getExchange();
                long deliveryTag = envelope.getDeliveryTag();
                System.out.println("exchange:" + exchange);
                System.out.println("deliveryTag:" + deliveryTag);

                //接收消息
                String msg = new String(body, "utf-8");
                System.out.println("receive msg: "+ msg);
                //使主线程执行,不阻塞了
                countDownLatch.countDown();
            }
        };

        //4.监听队列,消费消息
        /**
         * String queue, boolean autoAck, Consumer callback
         * 参数明细:
         * queue: 队列名
         * autoAck: 自动回复,当消费者受到消息之后,要告诉mq已接收到消息,true表示自动回复,false,表示不自动回复,需要代码回复
         * callback: 消费方法,当前消费者受到消息后要执行的方法.
         */
        channel.basicConsume(QUEUE_NAME,true,consumer);
        //这是模拟,客户端阻塞,因为不这样做,客户端程序直接执行完毕,则不能执行consumer中的handleDelivery回调方法.
        //阻塞当前线程
        countDownLatch.await();
        while (true){

        }
    }
}

2.Publish/Subscribe(发布/订阅)模式

在这里插入图片描述

2.1 架构
生产者先将消息发送给交换机,交换机在发送给绑定这个交换机的队列,然后消费者去读取不同的queue,就可实现一条消息发给不同的用户.
发布订阅模式完全可以实现工作队列模式的功能.
2.2 应用场景
适用于一条消息需要交给两个不同的业务去处理,并且每个业务互不影响
2.3 代码

package com.gl.rabbitmq.original;

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

import java.io.IOException;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.TimeoutException;

/**
 * @author gl
 * @time 2020-06-21 22:14
 * @function :第二种模式:   发布订阅模式
 * @step :  相对于workqueue多了exchange
 */
public class PublishSubscribeDemo {
    private static final String QUEUE_INFORM_EMAIL = "queue_inform_email";
    private static final String QUEUE_INFORM_SMS = "queue_inform_sms";
    private static final String EXCHANGE_FANOUT_INFORM = "exchange_fanout_inform";
    private CountDownLatch countDownLatch = new CountDownLatch(1);

    /**
     * 生产者
     * @throws IOException
     * @throws TimeoutException
     */
    @Test
    public void producer() throws IOException, TimeoutException {
        //1.通过util对象创建connection对象
        Connection connection = ConnUtil.getConnection();

        //2.获取channel
        Channel channel = connection.createChannel();
        //3.声明队列,这里就需要声明两个队列了
        /**
         * queue:声明队列的名称
         * durable: 是否持久化
         * exclusive: 是否独占连接,如果连接关闭则自动删除,如果将此参数设置为true可用于临时队列的创建
         * autoDelete: 是否自动删除此队列,如果将自参数和exclusive都设置为true就可实现临时队列.
         * arguments: 可以设置一个队列的扩展参数,比如: 设置队列存活时间
         */
        channel.queueDeclare(QUEUE_INFORM_EMAIL,true,false,false,null);
        channel.queueDeclare(QUEUE_INFORM_SMS,true,false,false,null);

        //4 声明交换机
        /**
         * 参数明细
         * 参数1: 交换机名称
         * 参数2: 交换机类型
         *      fanout: 对应发布订阅模式
         *      direct: 对应routing路由模式
         *      topic: 对应通配符模式
         *      header: 对应header模式
         */
        channel.exchangeDeclare(EXCHANGE_FANOUT_INFORM,BuiltinExchangeType.FANOUT);

        //5.将2个队列绑定在交换机上面
        /**
         * 参数1: 队列名字
         * 参数2: 交换机名字
         * 路由key: 在发布订阅模式中使用空串;在路由模式和通配符模式中会根据路由key将消息发送到指定的queue中去
         */
        channel.queueBind(QUEUE_INFORM_EMAIL,EXCHANGE_FANOUT_INFORM,"");
        channel.queueBind(QUEUE_INFORM_SMS,EXCHANGE_FANOUT_INFORM,"");

        //6.发送消息
        /**
         * channel.basicPublish(EXCHANGE_FANOUT_INFORM,"",null,msg.getBytes());
         * String exchange, String routingKey, BasicProperties props, byte[] body
         * exchange: 交换机,如果传递""空串表示使用默认的交换机
         * routingKey: 路由key,交换机根据路由key来将消息转发到指定的队列,如果使用默认的交换机,routingKey设置队列的名字
         * props: 消息的属性
         * body: 消息的内容
         */
        String msg = "send message 消费成功";
        for (int i = 0;i < 10 ; i ++){
            //因为这里采用的是发布订阅模式,我们把消息是发给交换机,交换机去发给queue,所以这里指定交换机及即可,queue为空串
            channel.basicPublish(EXCHANGE_FANOUT_INFORM,"",null,(msg + i).getBytes());
            System.out.println("发送成功!!消息内容:" + (msg + i));
        }

        channel.close();
    }


    /**
     * email消费者
     * @throws IOException
     * @throws TimeoutException
     * @throws InterruptedException
     * 这里可以同时开启两次测试
     */
    @Test
    public void consumerEmail() throws IOException, TimeoutException, InterruptedException {
       publishSuvscirbeConsume(QUEUE_INFORM_EMAIL,EXCHANGE_FANOUT_INFORM);
    }

    /**
     * SMS消费者
     * @throws IOException
     * @throws TimeoutException
     * @throws InterruptedException
     * 这里可以同时开启两次测试
     */
    @Test
    public void consumerSMS() throws IOException, TimeoutException, InterruptedException {
        publishSuvscirbeConsume(QUEUE_INFORM_SMS,EXCHANGE_FANOUT_INFORM);
    }

    /**
     * 这是提取公共的发布订阅的consumer方法
     */
    public void publishSuvscirbeConsume(String queue,String exchange) throws IOException, TimeoutException, InterruptedException {
        //1.通过util对象创建connection对象
        Connection connection = ConnUtil.getConnection();

        //2.获取channel
        Channel channel = connection.createChannel();
        //3.声明队列
        /**
         * 这里可以不用声明队列,但是要确定该队列一定是在mq中存在的,不然就会代码报错.
         * queue:声明队列的名称
         * durable: 是否持久化
         * exclusive: 是否独占连接,如果连接关闭则自动删除,如果将此参数设置为true可用于临时队列的创建
         * autoDelete: 是否自动删除此队列,如果将自参数和exclusive都设置为true就可实现临时队列.
         * arguments: 可以设置一个队列的扩展参数,比如: 设置队列存活时间
         */
        channel.queueDeclare(queue,true,false,false,null);

        //4.声明交换机
        channel.exchangeDeclare(exchange,BuiltinExchangeType.FANOUT);
        //5.绑定queue到exchange上
        channel.queueBind(queue,exchange,"");

        //声明一个消费者
        DefaultConsumer consumer = new DefaultConsumer(channel){
            /**
             * 接收到消息时,调用此方法
             * @param consumerTag 消费者标签
             * @param envelope  信封---其实就是mq对于此消息的封装的东西,比如消息id,exchange等
             * @param properties 消息属性
             * @param body 消息内容
             * @throws IOException
             */
            @Override
            public void handleDelivery(String consumerTag, Envelope envelope, AMQP.BasicProperties properties, byte[] body) throws IOException {
                //交换机
                java.lang.String exchange = envelope.getExchange();
                long deliveryTag = envelope.getDeliveryTag();
                System.out.println("exchange:" + exchange);
                System.out.println("deliveryTag:" + deliveryTag);

                //接收消息
                String msg = new String(body, "utf-8");
                System.out.println("receive msg: "+ msg);
                //使主线程执行,不阻塞了
                countDownLatch.countDown();
            }
        };

        //4.监听队列,消费消息
        /**
         * String queue, boolean autoAck, Consumer callback
         * 参数明细:
         * queue: 队列名
         * autoAck: 自动回复,当消费者受到消息之后,要告诉mq已接收到消息,true表示自动回复,false,表示不自动回复,需要代码回复
         * callback: 消费方法,当前消费者受到消息后要执行的方法.
         */
        channel.basicConsume(queue,true,consumer);
        //这是模拟,客户端阻塞,因为不这样做,客户端程序直接执行完毕,则不能执行consumer中的handleDelivery回调方法.
        //阻塞当前线程
        countDownLatch.await();
        while (true){

        }
    }
}

3.Routing(路由)模式

在这里插入图片描述

3.1 架构
生产者先将消息发送给交换机,交换机再根据路由key相等发送到不同的queue中,然后消费者去读取不同的queue,就可实现一条消息发给不同的用户.
路由模式完全可以实现发布订阅模式的功能
3.2 应用场景
适用于一条消息,需要发送给不同的业务,并且根据业务的不同还需要对消息做不同的发送策略.
3.3 代码

package com.gl.rabbitmq.original;

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

import java.io.IOException;
import java.util.concurrent.TimeoutException;

/**
 * @author gl
 * @time 2020-06-21 22:14
 * @function :第三种模式:   routing路由模式
 * @step : 相对于发布订阅模式多了routingKey
 */
public class RoutingDemo {
    private static final String QUEUE_INFORM_EMAIL = "queue_inform_email";
    private static final String QUEUE_INFORM_SMS = "queue_inform_sms";
    private static final String EXCHANGE_ROUTING_INFORM = "exchange_routing_inform";
    private static final String ROUTING_EMAIL = "inform_email";
    private static final String ROUTING_SMS = "inform_sms";

    /**
     * 生产者
     * @throws IOException
     * @throws TimeoutException
     */
    @Test
    public void producer() throws IOException, TimeoutException {
        //1.通过util对象创建connection对象
        Connection connection = ConnUtil.getConnection();

        //2.获取channel
        Channel channel = connection.createChannel();
        //3.声明队列,这里就需要声明两个队列了
        /**
         * queue:声明队列的名称
         * durable: 是否持久化
         * exclusive: 是否独占连接,如果连接关闭则自动删除,如果将此参数设置为true可用于临时队列的创建
         * autoDelete: 是否自动删除此队列,如果将自参数和exclusive都设置为true就可实现临时队列.
         * arguments: 可以设置一个队列的扩展参数,比如: 设置队列存活时间
         */
        channel.queueDeclare(QUEUE_INFORM_EMAIL,true,false,false,null);
        channel.queueDeclare(QUEUE_INFORM_SMS,true,false,false,null);

        //4 声明交换机
        /**
         * 参数明细
         * 参数1: 交换机名称
         * 参数2: 交换机类型
         *      fanout: 对应发布订阅模式
         *      direct: 对应routing路由模式
         *      topic: 对应通配符模式
         *      header: 对应header模式
         */
        channel.exchangeDeclare(EXCHANGE_ROUTING_INFORM,BuiltinExchangeType.DIRECT);

        //5.将2个队列绑定在交换机上面
        /**
         * 参数1: 队列名字
         * 参数2: 交换机名字
         * 路由key: 在发布订阅模式中使用空串;在路由模式和通配符模式中会根据路由key将消息发送到指定的queue中去
         */
        channel.queueBind(QUEUE_INFORM_EMAIL,EXCHANGE_ROUTING_INFORM,ROUTING_EMAIL);
        channel.queueBind(QUEUE_INFORM_SMS,EXCHANGE_ROUTING_INFORM,ROUTING_SMS);
        //同一个queue可以绑定多个routingKey
        //如果两个queue绑定相同的exchange交换机,相同的routingKey,则我们就可以实现发发布订阅的功能
        //只需要我们发送消息时,指定发送到这个交换机的这个routingKey下节课
        channel.queueBind(QUEUE_INFORM_EMAIL,EXCHANGE_ROUTING_INFORM,"inform");
        channel.queueBind(QUEUE_INFORM_SMS,EXCHANGE_ROUTING_INFORM,"inform");

        //6.发送消息
        /**
         * channel.basicPublish(EXCHANGE_FANOUT_INFORM,"",null,msg.getBytes());
         * String exchange, String routingKey, BasicProperties props, byte[] body
         * exchange: 交换机,如果传递""空串表示使用默认的交换机
         * routingKey: 路由key,交换机根据路由key来将消息转发到指定的队列,如果使用默认的交换机,routingKey设置队列的名字
         * props: 消息的属性
         * body: 消息的内容
         */
        for (int i = 0;i < 10 ; i ++){
            if(i % 2 == 0){
                String msg = "发送消息到email!!";
                //因为这里采用的是发布订阅模式,我们把消息是发给交换机,交换机去发给queue,所以这里指定交换机及即可,queue为空串
                channel.basicPublish(EXCHANGE_ROUTING_INFORM,ROUTING_EMAIL,null,(msg + i).getBytes());
                System.out.println("发送成功!!消息内容:" + (msg + i));
            }else{
                String msg = "发送消息到SMS!!";
                //因为这里采用的是发布订阅模式,我们把消息是发给交换机,交换机去发给queue,所以这里指定交换机及即可,queue为空串
                channel.basicPublish(EXCHANGE_ROUTING_INFORM,ROUTING_SMS,null,(msg + i).getBytes());
                System.out.println("发送成功!!消息内容:" + (msg + i));
            }
        }

        channel.close();
    }


    /**
     * email消费者
     * @throws IOException
     * @throws TimeoutException
     * @throws InterruptedException
     * 这里可以同时开启两次测试
     */
    @Test
    public void consumerEmail() throws IOException, TimeoutException, InterruptedException {
       publishSuvscirbeConsume(QUEUE_INFORM_EMAIL,EXCHANGE_ROUTING_INFORM,ROUTING_EMAIL);
    }

    /**
     * SMS消费者
     * @throws IOException
     * @throws TimeoutException
     * @throws InterruptedException
     * 这里可以同时开启两次测试
     */
    @Test
    public void consumerSMS() throws IOException, TimeoutException, InterruptedException {
        publishSuvscirbeConsume(QUEUE_INFORM_SMS,EXCHANGE_ROUTING_INFORM,ROUTING_SMS);
    }

    /**
     * 这是提取公共的发布订阅的consumer方法
     */
    public void publishSuvscirbeConsume(String queue,String exchange,String routingKey) throws IOException, TimeoutException, InterruptedException {
        //1.通过util对象创建connection对象
        Connection connection = ConnUtil.getConnection();

        //2.获取channel
        Channel channel = connection.createChannel();
        //3.声明队列
        /**
         * 这里可以不用声明队列,但是要确定该队列一定是在mq中存在的,不然就会代码报错.
         * queue:声明队列的名称
         * durable: 是否持久化
         * exclusive: 是否独占连接,如果连接关闭则自动删除,如果将此参数设置为true可用于临时队列的创建
         * autoDelete: 是否自动删除此队列,如果将自参数和exclusive都设置为true就可实现临时队列.
         * arguments: 可以设置一个队列的扩展参数,比如: 设置队列存活时间
         */
        channel.queueDeclare(queue,true,false,false,null);

        //4.声明交换机
        channel.exchangeDeclare(exchange,BuiltinExchangeType.DIRECT);
        //5.绑定queue到exchange上
        channel.queueBind(queue,exchange,routingKey);

        //声明一个消费者
        DefaultConsumer consumer = new DefaultConsumer(channel){
            /**
             * 接收到消息时,调用此方法
             * @param consumerTag 消费者标签
             * @param envelope  信封---其实就是mq对于此消息的封装的东西,比如消息id,exchange等
             * @param properties 消息属性
             * @param body 消息内容
             * @throws IOException
             */
            @Override
            public void handleDelivery(String consumerTag, Envelope envelope, AMQP.BasicProperties properties, byte[] body) throws IOException {
                //交换机
                String exchange = envelope.getExchange();
                long deliveryTag = envelope.getDeliveryTag();
                System.out.println("exchange:" + exchange);
                System.out.println("deliveryTag:" + deliveryTag);

                //接收消息
                String msg = new String(body, "utf-8");
                System.out.println("receive msg: "+ msg);
            }
        };

        //4.监听队列,消费消息
        /**
         * String queue, boolean autoAck, Consumer callback
         * 参数明细:
         * queue: 队列名
         * autoAck: 自动回复,当消费者受到消息之后,要告诉mq已接收到消息,true表示自动回复,false,表示不自动回复,需要代码回复
         * callback: 消费方法,当前消费者受到消息后要执行的方法.
         */
        channel.basicConsume(queue,true,consumer);
        //这是模拟,客户端阻塞,因为不这样做,客户端程序直接执行完毕,则不能执行consumer中的handleDelivery回调方法.
        //阻塞当前线程
        while (true){

        }
    }
}

4.Topics(通配符)模式

在这里插入图片描述

4.1 架构
生产者先将消息发送给交换机,交换机再根据路由key匹配发送到不同的queue中,然后消费者去读取不同的queue,就可实现一条消息发给不同的用户.
路由模式完全可以实现发布订阅模式的功能

4.2 应用场景
适用于一条消息,需要发送给不同的业务,并且根据业务的不同还需要对消息做不同的发送策略.

4.3 Topics与Routing模式的区别
在这里插入图片描述
4.4 代码

package com.gl.rabbitmq.original;

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

import java.io.IOException;
import java.util.concurrent.TimeoutException;

/**
 * @author gl
 * @time 2020-06-21 22:14
 * @function :第四种模式:   topic通配符模式
 * @step : 相对于路由模式,只是routingkey不是相等匹配了,而是通配符匹配了
 */
public class TopicDemo {
    private static final String QUEUE_INFORM_EMAIL = "queue_inform_email";
    private static final String QUEUE_INFORM_SMS = "queue_inform_sms";
    private static final String EXCHANGE_TOPIC_INFORM = "exchange_topic_inform";
    private static final String ROUTING_EMAIL = "inform.#.email.#";
    private static final String ROUTING_SMS = "inform.#.sms.#";

    /**
     * 生产者
     * @throws IOException
     * @throws TimeoutException
     */
    @Test
    public void producer() throws IOException, TimeoutException {
        //1.通过util对象创建connection对象
        Connection connection = ConnUtil.getConnection();

        //2.获取channel
        Channel channel = connection.createChannel();
        //3.声明队列,这里就需要声明两个队列了
        /**
         * queue:声明队列的名称
         * durable: 是否持久化
         * exclusive: 是否独占连接,如果连接关闭则自动删除,如果将此参数设置为true可用于临时队列的创建
         * autoDelete: 是否自动删除此队列,如果将自参数和exclusive都设置为true就可实现临时队列.
         * arguments: 可以设置一个队列的扩展参数,比如: 设置队列存活时间
         */
        channel.queueDeclare(QUEUE_INFORM_EMAIL,true,false,false,null);
        channel.queueDeclare(QUEUE_INFORM_SMS,true,false,false,null);

        //4 声明交换机
        /**
         * 参数明细
         * 参数1: 交换机名称
         * 参数2: 交换机类型
         *      fanout: 对应发布订阅模式
         *      direct: 对应routing路由模式
         *      topic: 对应通配符模式
         *      header: 对应header模式
         */
        channel.exchangeDeclare(EXCHANGE_TOPIC_INFORM,BuiltinExchangeType.TOPIC);

        //5.将2个队列绑定在交换机上面
        /**
         * 参数1: 队列名字
         * 参数2: 交换机名字
         * 路由key: 在发布订阅模式中使用空串;在路由模式和通配符模式中会根据路由key将消息发送到指定的queue中去
         */
        channel.queueBind(QUEUE_INFORM_EMAIL,EXCHANGE_TOPIC_INFORM,ROUTING_EMAIL);
        channel.queueBind(QUEUE_INFORM_SMS,EXCHANGE_TOPIC_INFORM,ROUTING_SMS);
        //同一个queue可以绑定多个routingKey
        //如果两个queue绑定相同的exchange交换机,相同的routingKey,则我们就可以实现发发布订阅的功能
        //只需要我们发送消息时,指定发送到这个交换机的这个routingKey下节课
        //channel.queueBind(QUEUE_INFORM_EMAIL,EXCHANGE_ROUTING_INFORM,"inform");
        //channel.queueBind(QUEUE_INFORM_SMS,EXCHANGE_ROUTING_INFORM,"inform");

        //6.发送消息
        /**
         * channel.basicPublish(EXCHANGE_FANOUT_INFORM,"",null,msg.getBytes());
         * String exchange, String routingKey, BasicProperties props, byte[] body
         * exchange: 交换机,如果传递""空串表示使用默认的交换机
         * routingKey: 路由key,交换机根据路由key来将消息转发到指定的队列,如果使用默认的交换机,routingKey设置队列的名字
         * props: 消息的属性
         * body: 消息的内容
         */
        for (int i = 0;i < 5 ; i ++){
                String msg = "发送消息到email!!";
                //因为这里采用的是发布订阅模式,我们把消息是发给交换机,交换机去发给queue,所以这里指定交换机及即可,queue为空串
                channel.basicPublish(EXCHANGE_TOPIC_INFORM,"inform.email",null,(msg + i).getBytes());
                System.out.println("发送成功!!消息内容:" + (msg + i));
        }
        for (int i = 0;i < 5 ; i ++){
            String msg = "发送消息到sms!!";
            //因为这里采用的是发布订阅模式,我们把消息是发给交换机,交换机去发给queue,所以这里指定交换机及即可,queue为空串
            channel.basicPublish(EXCHANGE_TOPIC_INFORM,"inform.sms",null,(msg + i).getBytes());
            System.out.println("发送成功!!消息内容:" + (msg + i));
        }
        for (int i = 0;i < 5 ; i ++){
            String msg = "发送消息到email和sms!!";
            //因为这里采用的是发布订阅模式,我们把消息是发给交换机,交换机去发给queue,所以这里指定交换机及即可,queue为空串
            channel.basicPublish(EXCHANGE_TOPIC_INFORM,"inform.email.sms",null,(msg + i).getBytes());
            System.out.println("发送成功!!消息内容:" + (msg + i));
        }

        channel.close();
    }


    /**
     * email消费者
     * @throws IOException
     * @throws TimeoutException
     * @throws InterruptedException
     * 这里可以同时开启两次测试
     */
    @Test
    public void consumerEmail() throws IOException, TimeoutException, InterruptedException {
       publishSuvscirbeConsume(QUEUE_INFORM_EMAIL,EXCHANGE_TOPIC_INFORM,ROUTING_EMAIL);
    }

    /**
     * SMS消费者
     * @throws IOException
     * @throws TimeoutException
     * @throws InterruptedException
     * 这里可以同时开启两次测试
     */
    @Test
    public void consumerSMS() throws IOException, TimeoutException, InterruptedException {
        publishSuvscirbeConsume(QUEUE_INFORM_SMS,EXCHANGE_TOPIC_INFORM,ROUTING_SMS);
    }

    /**
     * 这是提取公共的发布订阅的consumer方法
     */
    public void publishSuvscirbeConsume(String queue,String exchange,String routingKey) throws IOException, TimeoutException, InterruptedException {
        //1.通过util对象创建connection对象
        Connection connection = ConnUtil.getConnection();

        //2.获取channel
        Channel channel = connection.createChannel();
        //3.声明队列
        /**
         * 这里可以不用声明队列,但是要确定该队列一定是在mq中存在的,不然就会代码报错.
         * queue:声明队列的名称
         * durable: 是否持久化
         * exclusive: 是否独占连接,如果连接关闭则自动删除,如果将此参数设置为true可用于临时队列的创建
         * autoDelete: 是否自动删除此队列,如果将自参数和exclusive都设置为true就可实现临时队列.
         * arguments: 可以设置一个队列的扩展参数,比如: 设置队列存活时间
         */
        channel.queueDeclare(queue,true,false,false,null);

        //4.声明交换机
        channel.exchangeDeclare(exchange,BuiltinExchangeType.TOPIC);
        //5.绑定queue到exchange上
        channel.queueBind(queue,exchange,routingKey);

        //声明一个消费者
        DefaultConsumer consumer = new DefaultConsumer(channel){
            /**
             * 接收到消息时,调用此方法
             * @param consumerTag 消费者标签
             * @param envelope  信封---其实就是mq对于此消息的封装的东西,比如消息id,exchange等
             * @param properties 消息属性
             * @param body 消息内容
             * @throws IOException
             */
            @Override
            public void handleDelivery(String consumerTag, Envelope envelope, AMQP.BasicProperties properties, byte[] body) throws IOException {
                //交换机
                String exchange = envelope.getExchange();
                long deliveryTag = envelope.getDeliveryTag();
                System.out.println("exchange:" + exchange);
                System.out.println("deliveryTag:" + deliveryTag);

                //接收消息
                String msg = new String(body, "utf-8");
                System.out.println("receive msg: "+ msg);
            }
        };

        //4.监听队列,消费消息
        /**
         * String queue, boolean autoAck, Consumer callback
         * 参数明细:
         * queue: 队列名
         * autoAck: 自动回复,当消费者受到消息之后,要告诉mq已接收到消息,true表示自动回复,false,表示不自动回复,需要代码回复
         * callback: 消费方法,当前消费者受到消息后要执行的方法.
         */
        channel.basicConsume(queue,true,consumer);
        //这是模拟,客户端阻塞,因为不这样做,客户端程序直接执行完毕,则不能执行consumer中的handleDelivery回调方法.
        //阻塞当前线程
        while (true){

        }
    }
}

5.RPC模式

在这里插入图片描述
因为不太常用,故略.

6.Header模式

不常用:略

总结

生产者生产消息的步骤

  1. 获取connection对象
  2. 从connection对象中获取channel
  3. channel声明exchange对象
  4. channel声明queue对象
  5. channel将exchange对象和queue对象绑定在一起
  6. channel.basicPublish() 发送消息

消费者消费消息的步骤

  1. 获取connection对象
  2. 从connection对象中获取channel
  3. channel声明exchange对象
  4. channel声明queue对象
  5. channel将exchange对象和queue对象绑定在一起
  6. 声明consumer对象,并实现handleDelivery()回调方法,这个方法是收到queue中的消息,对这个消息做处理的方法.
  7. channel.basicConsume() 消费消息
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值