RabbitMq的基础知识

简单队列创建(一个生产者,一个队列,一个消费者)

1.创建mq连接

 public static Connection getConnection() throws IOException, TimeoutException {
        ConnectionFactory factory=new ConnectionFactory();
        factory.setHost("192.168.1.6");
        factory.setPort(5672);
        factory.setVirtualHost("/user_db");
        factory.setUsername("user");
        factory.setPassword("123");
        return factory.newConnection();
    }

2.创建生产者

public static void main(String[] args) throws IOException, TimeoutException {
        //获取链接
        Connection connection = ConnectionUtils.getConnection();
        //从链接中获取一个通道
        Channel channel = connection.createChannel();
        //创建队列
        channel.queueDeclare(QUEUE_NAME,false,false,false,null);
        String msg="hello____ssss--->";
        //发送消息
        channel.basicPublish("",QUEUE_NAME,null,msg.getBytes());

        //关闭通道链接
        channel.close();
        //关闭mq的链接
        connection.close();
    }

3.创建消费者

 public static void main(String[] args) throws IOException, TimeoutException, InterruptedException {
        Connection connection = ConnectionUtils.getConnection();
        Channel channel = connection.createChannel();
        channel.queueDeclare(QUEUE_NAME,false,false,false,null);

        //时间监听模式,如果有消息,就会通过channel,就会触发这个方法。获取消息
        DefaultConsumer consumer = new DefaultConsumer(channel) {
            @Override
            public void handleDelivery(String consumerTag, Envelope envelope, AMQP.BasicProperties properties, byte[] body) throws IOException {
                String mag=new String(body,"utf-8");
                System.out.println(mag);
            }
        };
        channel.basicConsume(QUEUE_NAME,true,consumer);
    }

多消费者(轮询分发。一个生产者、两个消费者)

1、生产者

public class SendWork {
    private  static final String QUEUE_NAME="test_work_queue";
    public static void main(String[] args) throws IOException, TimeoutException {
        //获取链接
         Connection connection = ConnectionUtils.getConnection();
        //获取channel
        Channel channel = connection.createChannel();
        //声明队列
        channel.queueDeclare(QUEUE_NAME,false,false,false,null);
        for (int i = 0; i < 50; i++) {
            String msg="---->"+i ;
            channel.basicPublish("",QUEUE_NAME,null,msg.getBytes());
        }

        channel.close();
        connection.close();
    }
}

2、第一个消费者(一秒消费一个消息)

public class RecvWork {
    private  static final String QUEUE_NAME="test_work_queue";
    public static void main(String[] args) throws IOException, TimeoutException {
        //获取链接
         Connection connection = ConnectionUtils.getConnection();
        //获取channel
        Channel channel = connection.createChannel();
        //声明队列
        channel.queueDeclare(QUEUE_NAME,false,false,false,null);

        Consumer consumer = new DefaultConsumer(channel) {
            @Override
            public void handleDelivery(String consumerTag, Envelope envelope, AMQP.BasicProperties properties, byte[] body) throws IOException {
                System.out.println("--->"+new String(body,"utf-8"));
                try {
                    Thread.sleep(1000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        };
         //开始ack自动应答
        boolean autoack=true;
        channel.basicConsume(QUEUE_NAME,autoack,consumer);
    }
}

3、第二个消费者(两秒消费一个消息)

public class RecvWork2 {
    private  static final String QUEUE_NAME="test_work_queue";

    public static void main(String[] args) throws IOException, TimeoutException {
         //获取链接
         Connection connection = ConnectionUtils.getConnection();
        //获取channel
        Channel channel = connection.createChannel();
        //声明队列
        channel.queueDeclare(QUEUE_NAME,false,false,false,null);

        Consumer consumer = new DefaultConsumer(channel) {
            @Override
            public void handleDelivery(String consumerTag, Envelope envelope, AMQP.BasicProperties properties, byte[] body) throws IOException {
                System.out.println("--->"+new String(body,"utf-8"));
                try {
                    Thread.sleep(2000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        };
        
        //开始ack自动应答
        boolean autoack=true;
        channel.basicConsume(QUEUE_NAME,autoack,consumer);
    }
}

总结:生产者一共发送50个消息。但是两个消费者消费的消息是一样的。

消费者1:消费奇数。 消费者2:消费偶数 。这种方式叫做轮询分发。结果是不管谁忙,谁清闲。都不会多给谁一个消息。任务消息总是你一个,我一个这么分发。

公平分发(生产者声明只有接收到回应消息,才会发下一条数据。)

1、生产者(声明只有接收到回应消息,才会发下一条数据)

public class SendWork {
    private  static final String QUEUE_NAME="test_work_queue";
    public static void main(String[] args) throws IOException, TimeoutException {
        //获取链接
        Connection connection = ConnectionUtils.getConnection();
        //获取channel
        Channel channel = connection.createChannel();
        //声明队列
        channel.queueDeclare(QUEUE_NAME,false,false,false,null);

        /**
         * 每个消费者发送确认“收到消息”这个标识之前,消息队列不会发送下一个消息到消费者。一次只处理一个消息
         * 限制消费者只能消费一条数据。不会出现消费积压、
         */
        int prefetCount=1;
        channel.basicQos(prefetCount);

        for (int i = 0; i < 50; i++) {
            String msg="---->"+i ;
            channel.basicPublish("",QUEUE_NAME,null,msg.getBytes());
        }

        channel.close();
        connection.close();
    }
}

2.消费者1(消费者声明只接收一个消息。并在消费完消息后,给生产者一个回应消息。消费者关闭自动应答改成手动)

public class RecvWork {
    private  static final String QUEUE_NAME="test_work_queue";
    public static void main(String[] args) throws IOException, TimeoutException {
        //获取链接
        Connection connection = ConnectionUtils.getConnection();
        //获取channel
        final Channel channel = connection.createChannel();
        //声明队列
        channel.queueDeclare(QUEUE_NAME,false,false,false,null);

        //保证只消费 一个消息
        channel.basicQos(1);

        Consumer consumer = new DefaultConsumer(channel) {
            @Override
            public void handleDelivery(String consumerTag, Envelope envelope, AMQP.BasicProperties properties, byte[] body) throws IOException {
                System.out.println("--->"+new String(body,"utf-8"));
                try {
                    Thread.sleep(2000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }finally {
                    //消费完消息后,告诉生产者。让生产者发送下一条消息
                    channel.basicAck(envelope.getDeliveryTag(),false);
                }
            }
        };
         //关闭自动应答,开启手动应答
        boolean autoack=false;
        channel.basicConsume(QUEUE_NAME,autoack,consumer);
    }
}

3、消费者2

public class RecvWork2 {
    private  static final String QUEUE_NAME="test_work_queue";

    public static void main(String[] args) throws IOException, TimeoutException {
        //获取链接
        Connection connection = ConnectionUtils.getConnection();
        //获取channel
        final Channel channel = connection.createChannel();
        //声明队列
        channel.queueDeclare(QUEUE_NAME,false,false,false,null);

        //保证只消费 一个消息
        channel.basicQos(1);

        Consumer consumer = new DefaultConsumer(channel) {
            @Override
            public void handleDelivery(String consumerTag, Envelope envelope, AMQP.BasicProperties properties, byte[] body) throws IOException {
                System.out.println("--->"+new String(body,"utf-8"));
                try {
                    Thread.sleep(1000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }finally {
                    //消费完消息后,告诉生产者。让生产者发送下一条消息
                    channel.basicAck(envelope.getDeliveryTag(),false);
                }
            }
        };

        //关闭自动应答,开启手动应答
        boolean autoack=false;
        channel.basicConsume(QUEUE_NAME,autoack,consumer);
    }
}

消息应答和消息持久化

消息应答:

boolean autoack=false;
channel.basicConsume(QUEUE_NAME,autoack,consumer);

boolean autoack=true;(自动确认)意思就是rabbitmq把消息发送给消费者后就直接将消息从内存中删除。并不会管你消费者是否真正的消费了这个消息。这样就会导致,在消费者宕机的那一刻,从rabbitmq发过来的消息并没有处理。造成消息丢失。

boolean autoack=false;(默认值为false)(手动模式)如果一个消费者宕机了,那么rabbitmq就会把消息分发给其他消费者。rabbitmq支持消息应答。消费者在处理完这个消息后,会给rabbitmq一个应答,告诉mq这个消息我处理了,你可以从内从中删除了。然后rabbitmq就会从内存中删除这个消息。

消息持久化:

  在声明队列的时候,第二个参数为false表示不持久化,改成true表示为持久化
   channel.queueDeclare(QUEUE_NAME,false,false,false,null);

但是如果定义了QUEUE_NAME,原来也为false。而且还被我们执行过,这样这个queue就会被定义成未持久化的queue。那么直接改成true是会报错的。因为rabbitmq不允许已经被定义好了的queue再进行修改。解决办法是可以重新写一个,如果不想重新写,也可以在rabbitmq的界面上删除这个queue,改成true后再执行。

订阅模式(Fanout

在声明交换机的时候,给定Fanout,表示分发。每个消费者都有

解读:

1.一个生产者,对应多个消费者

2.每一个消费者都有自己的队列

3.生产者没有直接把消息发送到队列中,而是发送到交换机(转发器)exchange上

4.每个队列都要绑定到交换机上

5.生产者发送消息经过交换机,到达队列就能实现一个消息被多个消费者消费。

生产者(在声明交换机的时候,第二个参数给fanout

public class SendPubic {
    //定义交换机名字
    private static final String EXCHANGE_NAME="test_exchange_fanout";

    /**
     * 此时生产者只是把消息发送到交换机上,交换机并没有存储消息的能力。
     * 如果没有消费者声明队列接收消息,那么消息就会丢失。
     * 首先执行一下,先将当前用户存在自己声明的交换机。否则先开消费者会报错。报找不到交换机
     * @param args
     * @throws IOException
     * @throws TimeoutException
     */
    public static void main(String[] args) throws IOException, TimeoutException {
        //获取链接
        Connection connection = ConnectionUtils.getConnection();
        //创建channel
        Channel channel = connection.createChannel();
        //声明交换机  fanout表示分发        ***************
        channel.exchangeDeclare(EXCHANGE_NAME,"fanout");

        channel.basicQos(1);

        for (int i = 0; i < 50; i++) {
            String msg="hello--->"+i;
            //发送消息
            channel.basicPublish(EXCHANGE_NAME,"",null,msg.getBytes());
        }
        channel.close();
        connection.close();
    }
}

消费者1

public class RecvPublic2 {

    //声明队列
    private static final String QUEUE_NAME="test_queue_fanout_aqs";
    //声明交换机
    private static final String EXCHANGE_NAME="test_exchange_fanout";

    /**
     * 第一步:获取链接
     * 第二步:获取channel
     * 第三步:声明队列
     * 第四步:将队列绑定到交换机上
     * 第五步:明确分发数量
     * 第六步:获取消息
     * 第七步:执行basicconsumer,开启应答方式
     * @param args
     * @throws IOException
     * @throws TimeoutException
     */
    public static void main(String[] args) throws IOException, TimeoutException {
        Connection connection = ConnectionUtils.getConnection();
        Channel channel = connection.createChannel();

        //声明队列
        channel.queueDeclare(QUEUE_NAME,false,false,false,null);

        //将队列绑定到交换机上
        channel.queueBind(QUEUE_NAME,EXCHANGE_NAME,"");

        //保证一次只分发一个
        channel.basicQos(1);

        DefaultConsumer consumer = 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,"utf-8"));

                //告诉生产者,发送下一个消息
                channel.basicAck(envelope.getDeliveryTag(),false);
            }
        };

        //关闭自动应答,开启手动应答
        boolean auack=false;
        channel.basicConsume(QUEUE_NAME,auack,consumer);

    }
}

消费者2(与消费者1不同的只有一点,队列名称不同。)

public class RecvPublic {

    //声明队列,与消费者1唯一不同的是队列名称
    private static final String QUEUE_NAME="test_queue_fanout_email";
    //声明交换机
    private static final String EXCHANGE_NAME="test_exchange_fanout";

    /**
     * 第一步:获取链接
     * 第二步:获取channel
     * 第三步:声明队列
     * 第四步:将队列绑定到交换机上
     * 第五步:明确分发数量
     * 第六步:获取消息
     * 第七步:执行basicconsumer,开启应答方式
     * @param args
     * @throws IOException
     * @throws TimeoutException
     */
    public static void main(String[] args) throws IOException, TimeoutException {
        Connection connection = ConnectionUtils.getConnection();
        Channel channel = connection.createChannel();

        //声明队列
        channel.queueDeclare(QUEUE_NAME,false,false,false,null);

        //将队列绑定到交换机上
        channel.queueBind(QUEUE_NAME,EXCHANGE_NAME,"");

        //保证一次只分发一个
        channel.basicQos(1);

        DefaultConsumer consumer = 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,"utf-8"));

                //告诉生产者,发送下一个消息
                channel.basicAck(envelope.getDeliveryTag(),false);
            }
        };

        //关闭自动应答,开启手动应答
        boolean auack=false;
        channel.basicConsume(QUEUE_NAME,auack,consumer);

    }
}

打开rabbitmq控制台,找到当前声明的交换机点进去,就会看到绑定的两个队列

 

路由模式(direct  根据rockingkey,匹配消息。并发送给相应的消费者)

下图是在声明交换机的时候,给定direct值。表示根据路由key,匹配消费者

生产者(在声明交换机时,第二个参数给direct。设置路由key,根据这个key才会分发指定的队列)

public class SendRock {

    //声明交换机名称
    private static final String EXCHANGE_NAME="test_rocket_direct";
    public static void main(String[] args) throws IOException, TimeoutException {
        //获取链接
        Connection connection = ConnectionUtils.getConnection();
        //获取channel
        Channel channel = connection.createChannel();

        //声明交换机
        channel.exchangeDeclare(EXCHANGE_NAME,"direct");

        //生产者只发送一个消息,必须等待消费者应答后才发送下一个
       // channel.basicQos(1);
        //定义消息。
        String msg="hello msg11111111";

        //交换机是根据routingkey来分发给指定的队列.
        String routingkey="info";
        channel.basicPublish(EXCHANGE_NAME,routingkey,null,msg.getBytes());

        channel.close();
        connection.close();
    }
}

消费者1(只接收error路由key的消息)

public class RecvRock1 {

    //声明交换机名称
    private static final String EXCHANGE_NAME="test_rocket_direct";
    //声明队列名称
    private static final String QUEUE_NAME="test_rocket_queue1_direct";

    public static void main(String[] args) throws IOException, TimeoutException {
        Connection connection = ConnectionUtils.getConnection();
        Channel channel = connection.createChannel();

        //声明队列
        channel.queueDeclare(QUEUE_NAME,false,false,false,null);
        //******将队列绑定到交换机上,并声明路由key*************
        channel.queueBind(QUEUE_NAME,EXCHANGE_NAME,"error");
        //声明只接收一个消息
        channel.basicQos(1);

        DefaultConsumer consumer = 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,"utf-8"));
                channel.basicAck(envelope.getDeliveryTag(),false);
            }
        };

        //关闭自动应答,开启手动应答
        boolean auack=false;
        channel.basicConsume(QUEUE_NAME,auack,consumer);
    }
}

消费者2(接收路由key中error、info、warning的消息)

public class RecvRock2 {
    //声明队列名称
    private static final String QUEUE_NAME="test_rocket_queue2_direct";
    //声明交换机名称
    private static final String EXCHANGE_NAME="test_rocket_direct";
    public static void main(String[] args) throws IOException, TimeoutException {
        Connection connection = ConnectionUtils.getConnection();
        Channel channel = connection.createChannel();

        //声明队列
        channel.queueDeclare(QUEUE_NAME,false,false,false,null);
        //***************将队列绑定到交换机上**************
        channel.queueBind(QUEUE_NAME,EXCHANGE_NAME,"error");
        channel.queueBind(QUEUE_NAME,EXCHANGE_NAME,"info");
        channel.queueBind(QUEUE_NAME,EXCHANGE_NAME,"warning");
        //声明只接收一个消息
        channel.basicQos(1);

        DefaultConsumer consumer = 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,"utf-8"));
                //发送给生产者应答
                channel.basicAck(envelope.getDeliveryTag(),false);
            }
        };

        channel.basicConsume(QUEUE_NAME,false,consumer);
    }
}

Topic主题模式(通配符模式)

#:匹配一个或者多个

*:只匹配一个

这里写图片描述

这里写图片描述

生产者(只需在定义交换机的时候,设置成topic模式,并设置好路由key)

public class SendTopic {

    private static final String EXCHANGE_NAME="test_topic";
    public static void main(String[] args) throws IOException, TimeoutException {
        Connection connection = ConnectionUtils.getConnection();
        Channel channel = connection.createChannel();

        //设置交换机,并指定topic主题模式**********************
        channel.exchangeDeclare(EXCHANGE_NAME,"topic");

        //每次只发送一个消息
        channel.basicQos(1);
        String  msg="你方法是毒奶粉是毒奶粉了";
        String rockkey="goods.delete";
        channel.basicPublish(EXCHANGE_NAME,rockkey,null,msg.getBytes());

        channel.close();
        connection.close();
    }
}

消费者1(只需在将队列绑定到交换机上的同时,指定只接收哪种消息)

public class RecvTopic {

    private static final String QUEUE_NAME="test_queue_topic1";
    private static final String EXCHANGE_NAME="test_topic";
    public static void main(String[] args) throws IOException, TimeoutException {
        Connection connection = ConnectionUtils.getConnection();
        Channel channel = connection.createChannel();
        //定义队列
        channel.queueDeclare(QUEUE_NAME,false,false,false,null);
        //将队列绑定到交换机上,并声明只接收add的消息********************
        channel.queueBind(QUEUE_NAME,EXCHANGE_NAME,"goods.add");
        //只接收一个消息
        channel.basicQos(1);
        DefaultConsumer consumer = 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,"utf-8"));
                //告诉生产者,回应应答
                channel.basicAck(envelope.getDeliveryTag(),false);
            }
        };
        //关闭自动应答
        channel.basicConsume(QUEUE_NAME,false,consumer);
    }
}

消费者2(消费者2,定义路由key的时候,用goods.#,表示接收任何消息)

public class RecvTopic2 {

    private static final String QUEUE_NAME="test_queue_topic2";
    private static final String EXCHANGE_NAME="test_topic";
    public static void main(String[] args) throws IOException, TimeoutException {
        Connection connection = ConnectionUtils.getConnection();
        Channel channel = connection.createChannel();
        //声明队列
        channel.queueDeclare(QUEUE_NAME,false,false,false,null);
        //绑定队列到交换机上,并设置路由key************
        channel.queueBind(QUEUE_NAME,EXCHANGE_NAME,"goods.#");
        //只接收一个消息
        channel.basicQos(1);
        DefaultConsumer consumer = 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,"utf-8"));
                //告诉生产者,回应应答
                channel.basicAck(envelope.getDeliveryTag(),false);
            }
        };
        //关闭自动应答
        channel.basicConsume(QUEUE_NAME,false,consumer);
    }
}

                                  

对消息进行属性赋值。(增加过期时间,编码格式等)

主要针对在生产者(AMQP.BasicProperties properties=new AMQP.BasicProperties())

public class SendTopic {

    private static final String EXCHANGE_NAME="muke_topic_exchange";
    public static void main(String[] args) throws IOException, TimeoutException {
        Connection connection = ConnectionUtils.getConnection();
        Channel channel = connection.createChannel();

        String msg="这是topic模式发送的消息";
        String rocketkey="muke_send";
        //给消息设置属性****************************
        AMQP.BasicProperties properties=new AMQP.BasicProperties().builder()
                .deliveryMode(2)   //持久化,设置2后,就算重启服务,没被消费的消息依旧存在
                .expiration("10000")  //消息过期时间,超过10秒这个消息没被消费,那么就会自动删除
                .contentEncoding("UTF-8")  //设置消息的字符集格式
                .build();
            channel.basicPublish("",rocketkey,properties,msg.getBytes());

        channel.close();
        connection.close();
    }
}
发布了11 篇原创文章 · 获赞 1 · 访问量 1948

猜你喜欢

转载自blog.csdn.net/yisuyanyu/article/details/105638599