Java操作RabbitMQ队列

学习交流群:817080571

Java操作RabbitMQ队列

环境配置

maven配置

  <dependencies>
        <!-- 引入队列依赖 -->
        <dependency>
            <groupId>com.rabbitmq</groupId>
            <artifactId>amqp-client</artifactId>
            <version>4.0.2</version>
        </dependency>

        <dependency>
            <groupId>org.slf4j</groupId>
            <artifactId>slf4j-api</artifactId>
            <version>1.7.10</version>
        </dependency>

        <dependency>
            <groupId>org.slf4j</groupId>
            <artifactId>slf4j-log4j12</artifactId>
            <version>1.7.5</version>
        </dependency>

        <dependency>
            <groupId>log4j</groupId>
            <artifactId>log4j</artifactId>
            <version>1.2.17</version>
        </dependency>

        <dependency>
            <groupId>junit</groupId>
            <artifactId>junit</artifactId>
            <version>4.11</version>
        </dependency>
    </dependencies>

工具类RabbitMQConnectionUtils

链接RabbitMQ


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

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

public class ConnectionUtils {

    public static Connection getConnection() throws IOException, TimeoutException {
        //链接工厂
        ConnectionFactory factory = new ConnectionFactory();
        //设置服务地址
        factory.setHost("127.0.0.1");
        //设置端口
        factory.setPort(5672);
        //设置vhost
        factory.setVirtualHost("/vhost");
        //设置用户名
        factory.setUsername("victoria");
        //设置密码
        factory.setPassword("123");
        return factory.newConnection();

    }

}

简单队列(Simple Queue)

模型

即简单的点对点消息模型。开启mq服务,开启进程P 生产者向mq 写消息,进程C消费者监听mq,消费消息。
Java操作RabbitMQ队列_第1张图片

生产者

import com.rabbitmq.client.Channel;
import com.rabbitmq.client.Connection;
import com.victoria.rabbitmq.util.ConnectionUtils;

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

public class send {
    private static final String QUEUE_NAME = "test_simple_queue";
    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_str = "hello simple !";
        //发送消息
        channel.basicPublish("",QUEUE_NAME,null,msg_str.getBytes());
        System.out.println("--send simple"+msg_str);
        //关闭
        channel.close();
        connection.close();
    }
}

消费者

import com.rabbitmq.client.*;
import com.victoria.rabbitmq.util.ConnectionUtils;

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

public class recv {
    private static final String QUEUE_NAME = "test_simple_queue";

    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);
        //定义一个消费者
        DefaultConsumer consumer = new DefaultConsumer(channel) {
            @Override
            public void handleDelivery(String consumerTag, Envelope envelope, AMQP.BasicProperties properties, byte[] body) throws IOException {
                System.out.println("---new recv" + new String(body, "utf-8"));
            }
        };
        //监听队列
        channel.basicConsume(QUEUE_NAME,true,consumer);
    }
}

简单队列不足

耦合度高,生产者和消费者一一对应,如果想有多个消费者消费队列就不可能了。如果队列名变更,消费者和生产者需要同时变更

工作队列(Work Queue)

模型

一个生产者P,对应了多个消费者C。这些多个C,消费的消息各自不同,C1和C2 消费的消息,构成所有消息的一个全集。

可以开启C的消费竞争 channel.basicQos(1);C1和C2 能者多劳。
Java操作RabbitMQ队列_第2张图片

循环分发

无论消费者速度快与慢,消费者都只会完成它所负责的那部分,其它消息即使没有被消费,它也不会去消费

生产者

import com.rabbitmq.client.Channel;
import com.rabbitmq.client.Connection;
import com.victoria.rabbitmq.util.ConnectionUtils;
import java.io.IOException;
import java.util.concurrent.TimeoutException;

public class send {
    private static final String QUEUE_NAME = "test_work_queue";
    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);
        //生产者
        for(int i = 0 ; i<50;i++){
            String str_msg = "hello"+i;
            channel.basicPublish("",QUEUE_NAME,null,str_msg.getBytes());
            System.out.println(str_msg);
        }
        //关闭通道
        channel.close();
        //关闭链接
        connection.close();
    }
}

消费者1

import com.rabbitmq.client.*;
import com.victoria.rabbitmq.util.ConnectionUtils;
import java.io.IOException;
import java.util.concurrent.TimeoutException;

public class recv1 {
    private static final String QUEUE_NAME = "test_work_queue";
    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);
        //定义消费者
        Consumer consumer = new DefaultConsumer(channel) {
            @Override
            public void handleDelivery(String consumerTag, Envelope envelope, AMQP.BasicProperties properties, byte[] body) throws IOException {
                System.out.println("[recv1]+"+new String(body,"utf-8"));
                try {
                    Thread.sleep(500);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        };
        //监听队列
        channel.basicConsume(QUEUE_NAME,true,consumer);
    }
}

消费者2

import com.rabbitmq.client.*;
import com.victoria.rabbitmq.util.ConnectionUtils;
import java.io.IOException;
import java.util.concurrent.TimeoutException;

public class recv2 {
    private static final String QUEUE_NAME = "test_work_queue";
    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);
        //定义消费者
        Consumer consumer = new DefaultConsumer(channel) {
            @Override
            public void handleDelivery(String consumerTag, Envelope envelope, AMQP.BasicProperties properties, byte[] body) throws IOException {
                System.out.println("[recv2]+"+new String(body,"utf-8"));
                try {
                    Thread.sleep(200);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        };
        //监听队列
        channel.basicConsume(QUEUE_NAME,true,consumer);
    }
}

公平分发

解决了上面循环分发,完成固定消息的问题,解决消息速度快的将多解决消息,

生产者

一次只发布一条消息


import com.rabbitmq.client.Channel;
import com.rabbitmq.client.Connection;
import com.victoria.rabbitmq.util.ConnectionUtils;
import java.io.IOException;
import java.util.concurrent.TimeoutException;

public class send {
    private static final String QUEUE_NAME = "test_work_queue";
    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.basicQos(1);
        //生产者
        for(int i = 0 ; i<50;i++){
            String str_msg = "hello"+i;
            channel.basicPublish("",QUEUE_NAME,null,str_msg.getBytes());
            System.out.println(str_msg);
        }
        //关闭通道
        channel.close();
        //关闭链接
        connection.close();
    }
}

消费者

一次只接受一条消息,关闭了自动接受,改为手动接受,给与消息队列ACK值

import com.rabbitmq.client.*;
import com.victoria.rabbitmq.util.ConnectionUtils;
import java.io.IOException;
import java.util.concurrent.TimeoutException;

public class recv1 {
    private static final String QUEUE_NAME = "test_work_queue";
    public static void main(String[] args) throws IOException, TimeoutException {
        //获取链接
        Connection connection = ConnectionUtils.getConnection();
        //创建通道
        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("[recv1]+"+new String(body,"utf-8"));
                try {
                    Thread.sleep(500);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }finally {
                    channel.basicAck(envelope.getDeliveryTag(),false);
                }
            }
        };
        //监听队列
        channel.basicConsume(QUEUE_NAME,false,consumer);
    }
}

消费者2

import com.rabbitmq.client.*;
import com.victoria.rabbitmq.util.ConnectionUtils;
import java.io.IOException;
import java.util.concurrent.TimeoutException;

public class recv2 {
    private static final String QUEUE_NAME = "test_work_queue";
    public static void main(String[] args) throws IOException, TimeoutException {
        //获取链接
        Connection connection = ConnectionUtils.getConnection();
        //创建通道
        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("[recv2]+"+new String(body,"utf-8"));
                try {
                    Thread.sleep(200);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }finally {
                    channel.basicAck(envelope.getDeliveryTag(),false);
                }
            }
        };
        //监听队列
        channel.basicConsume(QUEUE_NAME,false,consumer);
    }
}

订阅模式

模型

Java操作RabbitMQ队列_第3张图片

  1. 一个生产者,多个消费者
  2. 每一个消费者都有自己的队列
  3. 生产者没有将消息直接放到队列,而是放到了交换机 转发器 (exchange)
  4. 每个队列都要绑定到交换机上
  5. 生产者发送的消息,经过交换机,到达队列,这样就能实现一个消息被多个消费者实现

生产者

import com.rabbitmq.client.Channel;
import com.rabbitmq.client.Connection;
import com.victoria.rabbitmq.util.ConnectionUtils;
import java.io.IOException;
import java.util.concurrent.TimeoutException;

public class send {
    private static final String EXCHANGE_NAME = "test_exchange_fanout";
    public static void main(String[] args) throws IOException, TimeoutException {
        //创建链接
        Connection connection = ConnectionUtils.getConnection();
        //获取通道
        Channel channel = connection.createChannel();
        //声明交换机
        channel.exchangeDeclare(EXCHANGE_NAME,"fanout");
        //一次只发一条数据
        channel.basicQos(1);
        //生产者
        String str_msg = "hello exchange";
        channel.basicPublish(EXCHANGE_NAME,"",null,str_msg.getBytes());
        System.out.println(str_msg);
        //关闭通道
        channel.close();
        //关闭链接
        connection.close();
    }
}

使用订阅模式,只启动生产者是看不到消息在哪里的在这里插入图片描述
因为交换机没有存储的能力,RabbitMQ中只有队列有存储的能力
这个时候还没有队列绑定到这个交换机

消费者1

import com.rabbitmq.client.*;
import com.victoria.rabbitmq.util.ConnectionUtils;
import java.io.IOException;
import java.util.concurrent.TimeoutException;

public class recv1 {
    private static final String QUEUE_NAME = "test_queue_email";
    private static final String EXCHANGE_NAME = "test_exchange_fanout";
    public static void main(String[] args) throws IOException, TimeoutException {
        //获取链接
        Connection connection = ConnectionUtils.getConnection();
        //创建通道
        final Channel channel = connection.createChannel();
        //定义队列
        channel.queueDeclare(QUEUE_NAME,false,false,false,null);
        //绑定交换机
        channel.queueBind(QUEUE_NAME,EXCHANGE_NAME,"");
        //一次只发一条数据
        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("[recv1]+"+new String(body,"utf-8"));
                try {
                    Thread.sleep(200);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }finally {
                    channel.basicAck(envelope.getDeliveryTag(),false);
                }
            }
        };
        //监听队列
        boolean ack = false;
        channel.basicConsume(QUEUE_NAME,ack,consumer);
    }
}

消费者2

import com.rabbitmq.client.*;
import com.victoria.rabbitmq.util.ConnectionUtils;
import java.io.IOException;
import java.util.concurrent.TimeoutException;

public class recv2 {
    private static final String QUEUE_NAME = "test_queue_cms";
    private static final String EXCHANGE_NAME = "test_exchange_fanout";
    public static void main(String[] args) throws IOException, TimeoutException {
        //获取链接
        Connection connection = ConnectionUtils.getConnection();
        //创建通道
        final Channel channel = connection.createChannel();
        //定义队列
        channel.queueDeclare(QUEUE_NAME,false,false,false,null);
        //绑定交换机
        channel.queueBind(QUEUE_NAME,EXCHANGE_NAME,"");
        //一次只发一条数据
        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("[recv2]+"+new String(body,"utf-8"));
                try {
                    Thread.sleep(200);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }finally {
                    channel.basicAck(envelope.getDeliveryTag(),false);
                }
            }
        };
        //监听队列
        boolean ack = false;
        channel.basicConsume(QUEUE_NAME,ack,consumer);
    }
}

运行完消费者后可以再交换机那里看到绑定的队列

Java操作RabbitMQ队列_第4张图片

路由模式

模式

Java操作RabbitMQ队列_第5张图片
在订阅模式的基础上添加了路由键,将type改为direct

生产者

import com.rabbitmq.client.Channel;
import com.rabbitmq.client.Connection;
import com.victoria.rabbitmq.util.ConnectionUtils;
import java.io.IOException;
import java.util.concurrent.TimeoutException;

public class send {
    private static final String EXCHANGE_NAME= "test_exchange_direct";
    public static void main(String[] args) throws IOException, TimeoutException {
        //获取链接
        Connection connection = ConnectionUtils.getConnection();
        //创建通道
        Channel channel = connection.createChannel();
        //定义交换机
        channel.exchangeDeclare(EXCHANGE_NAME,"direct");
        //一次只能发送一条数据
        channel.basicQos(1);
        //发送的消息
        String str_msg = "hello direct";
        //定义路由键
        String rount_key = "info";
        //生产者
        channel.basicPublish(EXCHANGE_NAME,rount_key,null,str_msg.getBytes());
        System.out.println(str_msg);
        //关闭
        channel.close();
        connection.close();
    }
}

消费者1

import com.rabbitmq.client.*;
import com.victoria.rabbitmq.util.ConnectionUtils;
import java.io.IOException;
import java.util.concurrent.TimeoutException;

public class recv1 {
    private static final String QUEUE_NAME = "test_queue_recv1";
    private static final String EXCHANGE_NAME= "test_exchange_direct";
    public static void main(String[] args) throws IOException, TimeoutException {
        //获取链接
        Connection connection = ConnectionUtils.getConnection();
        //创建通道
        final Channel channel = connection.createChannel();
        //定义队列
        channel.queueDeclare(QUEUE_NAME,false,false,false,null);
        //交换机绑定
        channel.queueBind(QUEUE_NAME,EXCHANGE_NAME,"error");
        //一次只接受一条消息
        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("[recv1]:" + new String(body, "utf-8"));
                try {
                    Thread.sleep(1000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                } finally {
                    //手动提交
                    channel.basicAck(envelope.getDeliveryTag(), false);
                }
            }
        };
        //监听队列
        //手动提交
        boolean ack = false;
        channel.basicConsume(QUEUE_NAME,ack,consumer);
    }
}

消费者2

import com.rabbitmq.client.*;
import com.victoria.rabbitmq.util.ConnectionUtils;
import java.io.IOException;
import java.util.concurrent.TimeoutException;

public class recv2 {
    private static final String QUEUE_NAME = "test_queue_recv2";
    private static final String EXCHANGE_NAME= "test_exchange_direct";
    public static void main(String[] args) throws IOException, TimeoutException {
        //获取链接
        Connection connection = ConnectionUtils.getConnection();
        //创建通道
        final 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);
        //消费者
        Consumer consumer = new DefaultConsumer(channel) {
            @Override
            public void handleDelivery(String consumerTag, Envelope envelope, AMQP.BasicProperties properties, byte[] body) throws IOException {
                System.out.println("[recv1]:" + new String(body, "utf-8"));
                try {
                    Thread.sleep(1000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                } finally {
                    //手动提交
                    channel.basicAck(envelope.getDeliveryTag(), false);
                }
            }
        };
        //监听队列
        //手动提交
        boolean ack = false;
        channel.basicConsume(QUEUE_NAME,ack,consumer);
    }
}

问题

路由模式,只能按照路由键来匹配队列,必须得路由键相同的队列才能获取到消息, 这样对多服务的项目不太友好

主题模式

模型

Java操作RabbitMQ队列_第6张图片
在路由模式的基础上添加了匹配。
交换机的type=topic
"#"代表一个和多个
“*”代表一个

生产者

import com.rabbitmq.client.Channel;
import com.rabbitmq.client.Connection;
import com.victoria.rabbitmq.util.ConnectionUtils;
import java.io.IOException;
import java.util.concurrent.TimeoutException;

public class send {
    private static final String EXCHANGE_NAME = "test_exchange_topic";
    public static void main(String[] args) throws IOException, TimeoutException {
        //获取链接
        Connection connection = ConnectionUtils.getConnection();
        //获取通道
        Channel channel = connection.createChannel();
        //定义交换机
        channel.exchangeDeclare(EXCHANGE_NAME,"topic");
        //一次只接受一条消息
        channel.basicQos(1);
        //消息
        String str_msg = "hello topic";
        //路由键
        String rount_key = "goods.delete";
        //生产者
        channel.basicPublish(EXCHANGE_NAME,rount_key,null,str_msg.getBytes());
        System.out.println(str_msg);
        //关闭
        channel.close();
        connection.close();
    }
}

消费者1

import com.rabbitmq.client.*;
import com.victoria.rabbitmq.util.ConnectionUtils;
import java.io.IOException;
import java.util.concurrent.TimeoutException;

public class recv1 {
    private static final String QUEUE_NAME = "test_queue_topic1" ;
    private static final String EXCHANGE_NAME = "test_exchange_topic";
    public static void main(String[] args) throws IOException, TimeoutException {
        //获取链接
        Connection connection = ConnectionUtils.getConnection();
        //创建通道
        final Channel channel = connection.createChannel();
        //定义队列
        channel.queueDeclare(QUEUE_NAME,false,false,false,null);
        //交换机绑定
        channel.queueBind(QUEUE_NAME,EXCHANGE_NAME,"goods.add");
        //一次只接受一条消息
        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("[recv1]:" + new String(body, "utf-8"));
                try {
                    Thread.sleep(1000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                } finally {
                    //手动提交
                    channel.basicAck(envelope.getDeliveryTag(), false);
                }
            }
        };
        //监听队列
        //手动提交
        boolean ack = false;
        channel.basicConsume(QUEUE_NAME,ack,consumer);
    }
}

消费者2

import com.rabbitmq.client.*;
import com.victoria.rabbitmq.util.ConnectionUtils;
import java.io.IOException;
import java.util.concurrent.TimeoutException;

public class recv2 {
    private static final String QUEUE_NAME = "test_queue_topic2";
    private static final String EXCHANGE_NAME = "test_exchange_topic";
    public static void main(String[] args) throws IOException, TimeoutException {
        //获取链接
        Connection connection = ConnectionUtils.getConnection();
        //创建通道
        final Channel channel = connection.createChannel();
        //定义队列
        channel.queueDeclare(QUEUE_NAME,false,false,false,null);
        //交换机绑定
        channel.queueBind(QUEUE_NAME,EXCHANGE_NAME,"goods.#");
        //一次只接受一条消息
        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("[recv2]:" + new String(body, "utf-8"));
                try {
                    Thread.sleep(1000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                } finally {
                    //手动提交
                    channel.basicAck(envelope.getDeliveryTag(), false);
                }
            }
        };
        //监听队列
        //手动提交
        boolean ack = false;
        channel.basicConsume(QUEUE_NAME,ack,consumer);
    }
}

消息应答与消息持久化

消息应答

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

ack = true;(自动应答模式)

一旦RabbitMQ将消息发送给消费者,RabbitMQ就会从内存中删除这个消息

问题

如果RabbitMQ发送给消费者的过程中,消费者被终止,则这个消息也会从内存中删除

ack = false;(手动应答模式)

解决了上面自动应答的问题,只有消费者自己给RabbitMQ应答时,RabbitMQ才会从内存中删除消息,如果RabbitMQ发送给消费者的过程中,消费者被终止,则这条消息将会传递给下一个消费者完成

消息持久化

问题

RabbitMQ发送消息的过程中,RabbitMQ挂掉了的问题

解决方法

可以在定义队列时,完成消息持久化

        //定义队列
        //消息持久化
        boolean durable = true;
        channel.queueDeclare(QUEUE_NAME,durable,false,false,null);

如果设置durable=true;则打开了消息持久化,为false则反之

注意

如果消息队列已经存在,则不能改变该队列消息持久化

交换机(exchange)

一边接受生产者的消息,一边向队列推送消息

交换机处理方式

  1. 匿名转发 “”
  2. fanout 不处理路由键
  3. direct 处理路由键

RabbitMQ的消息确认机制(事务+confirm)

在RabbitMQ中我们可以通过持久化数据,解决RabbitMQ服务器异常的数据丢失问题

问题:生产者讲消息发送出去后,消息到底到没到达RabbitMQ服务器,默认的情况是不知道的;

两种方式:
AMQP实现事务机制
Confirm模式

事务机制

txSelect:用户将当前channel设置成transation模式
txcommit:用于提交事务
txrollback:用户回滚事务

生产者

import com.rabbitmq.client.*;
import com.victoria.rabbitmq.util.ConnectionUtils;
import java.io.IOException;
import java.util.concurrent.TimeoutException;

public class txsend {
    private static final String QUEUE_NAME= "test_queue_tx";
    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.basicQos(1);
        try {
            //事务开始
            channel.txSelect();
            String str_msg = "send_tx";
            //生产者(发布消息)
            channel.basicPublish("",QUEUE_NAME,null,str_msg.getBytes());
            System.out.println(str_msg);
            //事务提交
            channel.txCommit();
        } catch (Exception e) {
            System.out.println("tx_rollback");
            //事务回滚
            channel.txRollback();
        } finally {
            //关闭链接
            channel.close();
            connection.close();
        }
    }
}

消费者

import com.rabbitmq.client.*;
import com.victoria.rabbitmq.util.ConnectionUtils;
import java.io.IOException;
import java.util.concurrent.TimeoutException;

public class txrecv {
    private static final String QUEUE_NAME= "test_queue_tx";
    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.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("[recv1]"+new String(body,"utf-8"));
            }
        };
        //监听
        channel.basicConsume(QUEUE_NAME,false,consumer);
    }
}

此模式很耗时,降低了RabbitMQ的消息吞吐量

Confirm模式

好处是能异步发送

开启Confirm模式

channel.confirmSelect();

编程模式

  1. 普通的 一次只发送一条消息 返回一个 waitForConfirm
  2. 批量的 批量的发送消息 返回批量的 waitForConfirm
  3. 异步confirm模式 :提供一个回调方法

单条

生产者
import com.rabbitmq.client.Channel;
import com.rabbitmq.client.Connection;
import com.victoria.rabbitmq.util.ConnectionUtils;
import java.io.IOException;
import java.util.concurrent.TimeoutException;

public class sendsingle {
    private static  final  String QUEUE_NAME = "test_queue_confirm1";

    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.basicQos(1);
        //开启confirm模式
        channel.confirmSelect();
        String str_msg = "hello confirm";
        //发布消息(生产者)
        channel.basicPublish("",QUEUE_NAME,null,str_msg.getBytes());
        //判断是否有waitForConfirms
        //有则代表发布消息成功,否则反之
        if(channel.waitForConfirms()){
            System.out.println("send confirm ok");
        }else{
            System.out.println("send confirm error");
        }
        //关闭链接
        channel.close();
        connection.close();
    }
}
消费者
import com.rabbitmq.client.*;
import com.victoria.rabbitmq.util.ConnectionUtils;
import java.io.IOException;
import java.util.concurrent.TimeoutException;

public class recvsingle {
    private static  final  String QUEUE_NAME = "test_queue_confirm1";
    public static void main(String[] args) throws IOException, TimeoutException {
        //获取链接
        Connection connection = ConnectionUtils.getConnection();
        //创建通道
        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("[recv1]->"+new String(body,"utf-8"));
            }
        };
        //监听
        channel.basicConsume(QUEUE_NAME,true,consumer);
    }
}

批量

生产者
import com.rabbitmq.client.Channel;
import com.rabbitmq.client.Connection;
import com.victoria.rabbitmq.util.ConnectionUtils;
import java.io.IOException;
import java.util.concurrent.TimeoutException;

public class sendbatch {
    private static  final  String QUEUE_NAME = "test_queue_confirm2";

    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.basicQos(1);
        //开启confirm模式
        channel.confirmSelect();
        String str_msg = "hello confirm batch!";
        //批量发布消息(生产者)
        for(int i = 0 ;i<10;i++){
            channel.basicPublish("",QUEUE_NAME,null,str_msg.getBytes());
        }
        //判断是否有waitForConfirms
        //有则代表发布消息成功,否则反之
        if(channel.waitForConfirms()){
            System.out.println("send confirm ok");
        }else{
            System.out.println("send confirm error");
        }
        //关闭链接
        channel.close();
        connection.close();
    }
}
消费者
import com.rabbitmq.client.*;
import com.victoria.rabbitmq.util.ConnectionUtils;
import java.io.IOException;
import java.util.concurrent.TimeoutException;

public class recvbatch {
    private static  final  String QUEUE_NAME = "test_queue_confirm2";
    public static void main(String[] args) throws IOException, TimeoutException {
        //获取链接
        Connection connection = ConnectionUtils.getConnection();
        //创建通道
        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("[recv1]->"+new String(body,"utf-8"));
            }
        };
        //监听
        channel.basicConsume(QUEUE_NAME,true,consumer);
    }
}

异步

Channel对象提供的ConfirmListener0回调方法只包含deliveryTag (当前Chanel发出的消息序号),我们需要自己为每一个Channel维护一个unconfirm的消息序号集合,每publish 一条数据,集合中元素加1,每回调一次handleAck方法,unconfirm集合删掉
相应的一条(multiple=false) 或多条(multiple=true) 记录。从程序运行效率上看,这个unconfirm集合最好采用有序集合SortedSet存储结构。

生产者
import com.rabbitmq.client.Channel;
import com.rabbitmq.client.ConfirmListener;
import com.rabbitmq.client.Connection;
import com.victoria.rabbitmq.util.ConnectionUtils;
import java.io.IOException;
import java.util.Collections;
import java.util.SortedSet;
import java.util.TreeSet;

public class sendasyn {
    private static final String QUEUE_NAME = "test_queue_confirm3";
    public static void main(String[] args) throws Exception {
        Connection connetion = ConnectionUtils.getConnection();
        Channel channel = connetion.createChannel();
        channel.queueDeclare(QUEUE_NAME, false, false, false, null);
        //生产者调用confirmselect将channel 设置为conf1xm模式
        //注意confirm模式跟事务机制不能在同一个队列中
        channel.confirmSelect();
        //未确认的消息标识
        final SortedSet<Long> confirmSet = Collections.synchronizedSortedSet(new TreeSet<Long>());
        channel.addConfirmListener(new ConfirmListener() {
            /*
             * 处理返回确认成功
             * @param deliveryTag 如果是多条,这个就是最后一条消息的tag
             * @param multiple 是否多条
             * @throws IOException
             */
            public void handleAck(long l, boolean b) throws IOException {
                if(b){
                    System.out.println("----handleAck----multip1e");
                    confirmSet.headSet(l+1).clear();
                }else {
                    System.out.println("----handleAck----multip1e false");
                    confirmSet.remove(l);
                }
            }
            /*
             * 处理返回确认失败
             * @param deliveryTag 如果是多条,这个就是最后一条消息的tag
             * @param multiple 是否多条
             * @throws IOException
             */
            public void handleNack(long l, boolean b) throws IOException {
                if(b){
                    System.out.println("----handleNack----multip1e");
                    confirmSet.headSet(l+1).clear();
                }else {
                    System.out.println("----handleNack----multip1e false");
                    confirmSet.remove(l);
                }
            }
        });
        String msg = "hello confirm message!";
        while (true){
            long seqNo = channel.getNextPublishSeqNo();
            channel.basicPublish("",QUEUE_NAME,null,msg.getBytes());
            confirmSet.add(seqNo);
        }
    }
}

消费者
import com.rabbitmq.client.*;
import com.victoria.rabbitmq.util.ConnectionUtils;
import java.io.IOException;
import java.util.concurrent.TimeoutException;

public class recvasyn {
    private static  final  String QUEUE_NAME = "test_queue_confirm3";
    public static void main(String[] args) throws IOException, TimeoutException {
        //获取链接
        Connection connection = ConnectionUtils.getConnection();
        //创建通道
        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("[recv3]->"+new String(body,"utf-8"));
            }
        };
        //监听
        channel.basicConsume(QUEUE_NAME,true,consumer);
    }
}

return消息机制

returnAPI

basicPublish方法的参数mandatory
如果设置mandatory 为true时,交换器无法根据自身的类型和路由键找到一个符合条件的队列,那么RabbitMQ 会调用Basic.Return 命令将消息返回给生产者。
当mandatory参数设置为false 时,出现上述情形,则消息直接被丢弃;

生产者

import com.rabbitmq.client.AMQP;
import com.rabbitmq.client.Channel;
import com.rabbitmq.client.Connection;
import com.rabbitmq.client.ReturnListener;
import com.victoria.rabbitmq.util.ConnectionUtils;
import java.io.IOException;
import java.util.concurrent.TimeoutException;

public class sendreturn {
    private static final String EXCHANGE_NAME = "test_exchange_return";
    public static void main(String[] args) throws IOException, TimeoutException {
        //获取链接
        Connection connection = ConnectionUtils.getConnection();
        //创建通道
        Channel channel = connection.createChannel();
        //一次只发送一条消息
        channel.basicQos(1);
        //路由键名
        String rount_key = "useaar.add";
        String str_msg = "Hello exchange return";
        //发送消息
        channel.basicPublish(EXCHANGE_NAME,rount_key,true,null,str_msg.getBytes());
        //return消息机制监听
        channel.addReturnListener(new ReturnListener() {
            //消息没到达执行的方法
            public void handleReturn(int i, String s, String s1, String s2, AMQP.BasicProperties basicProperties, byte[] bytes) throws IOException {
                System.out.println("----------return ---------");
                //参数一:相应码
                System.out.println(i);
                //参数二:文本
                System.out.println(s);
                //参数三:交换机名
                System.out.println(s1);
                //参数四:路由键名
                System.out.println(s2);
                //参数五:basicProperties
                System.out.println(basicProperties);
                //参数流:传递的消息
                System.out.println(new String(bytes));
            }
        });
    }
}

消费者

import com.rabbitmq.client.*;
import com.victoria.rabbitmq.util.ConnectionUtils;
import java.io.IOException;
import java.util.concurrent.TimeoutException;

public class recvreturn {
    private static final String QUEUE_NAME = "test_queue_return";
    private static final String EXCHANGE_NAME = "test_exchange_return";
    public static void main(String[] args) throws IOException, TimeoutException {
        //获取链接
        Connection connection = ConnectionUtils.getConnection();
        //创建通道
        Channel channel = connection.createChannel();
        //一次只接受一条消息
        channel.basicQos(1);
        //声明交换机
        channel.exchangeDeclare(EXCHANGE_NAME,"topic",true,false,null);
        //声明队列
        channel.queueDeclare(QUEUE_NAME,true,false,false,null);
        //路由键匹配
        String rount_key = "user.#";
        //队列绑定交换机
        channel.queueBind(QUEUE_NAME,EXCHANGE_NAME,rount_key);
        //消息接收(消费者)
        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));
            }
        };
        //监听
        channel.basicConsume(QUEUE_NAME,true,consumer);
    }
}

你可能感兴趣的:(java)