前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >Java整合RabbitMQ实现生产消费(7种通讯方式)

Java整合RabbitMQ实现生产消费(7种通讯方式)

原创
作者头像
王二蛋
修改2024-02-06 10:55:09
1770
修改2024-02-06 10:55:09
举报
文章被收录于专栏:消息中间件消息中间件

环境说明

  • RabbitMQ环境,参考RabbitMQ环境搭建
  • Java版本:JDK1.8
  • Maven版本:apache-maven-3.6.3
  • 开发工具:IntelliJ IDEA

工程搭建

  1. 创建maven项目
  2. pom.xml文件引入RabbitMQ依赖
代码语言:xml
复制
    <dependencies>
        <dependency>
            <groupId>com.rabbitmq</groupId>
            <artifactId>amqp-client</artifactId>
            <version>5.16.0</version>
        </dependency>
        <dependency>
            <groupId>junit</groupId>
            <artifactId>junit</artifactId>
            <version>4.13.2</version>
            <scope>compile</scope>
        </dependency>

    </dependencies>

连接RabbitMQ

代码语言:java
复制
import com.rabbitmq.client.Connection;
import com.rabbitmq.client.ConnectionFactory;

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

public class MQConnections {

    public static final String RABBITMQ_HOST = "127.0.0.1";
    public static final int RABBITMQ_PORT = 5672;
    public static final String RABBITMQ_USERNAME = "guest";
    public static final String RABBITMQ_PASSWORD = "guest";
    public static final String RABBITMQ_VIRTUAL_HOST = "/";

    /**
     * 构建RabbitMQ连接对象
     *
     * @return
     */
    public static Connection getConnection() throws IOException, TimeoutException {
        //1.创建Connection工厂
        ConnectionFactory factory = new ConnectionFactory();
        //2.设置Rabbitmq连接信息
        factory.setHost(RABBITMQ_HOST);
        factory.setPort(RABBITMQ_PORT);
        factory.setUsername(RABBITMQ_USERNAME);
        factory.setPassword(RABBITMQ_PASSWORD);
        factory.setVirtualHost(RABBITMQ_VIRTUAL_HOST);
        //3.返回连接对象
        return factory.newConnection();
    }

}

通讯模式

1.简单通讯

即一个生产者可以向一个队列发送消息,一个消费者可以尝试从一个队列接收数据。如下图:

代码语言:java
复制
    public final static String HELLO_QUEUE_NAME = "hello";
    @Test
    public void publish_hello() throws IOException, TimeoutException {
        //1.获取连接对象
        Connection connection = MQConnections.getConnection();
        //2.构建Channl
        Channel channel = connection.createChannel();
        //3.构建队列,queueDeclare("队列名称","是否持久化队列","是否只允许一个队列消费","长时间未使用是否删除","其他参数")
        channel.queueDeclare(HELLO_QUEUE_NAME, false, false, false, null);
        //4.发布消息
        String msg = "hello,world";
        channel.basicPublish("", HELLO_QUEUE_NAME, null, msg.getBytes());
    }
	@Test
    public void consume_hello() throws IOException, TimeoutException {
        //1.获取连接对象
        Connection connection = MQConnections.getConnection();
        //2.构建Channl
        Channel channel = connection.createChannel();
        //3.构建队列,queueDeclare("队列名称","是否持久化队列","是否只允许一个队列消费","长时间未使用是否删除","其他参数")
        channel.queueDeclare(HELLO_QUEUE_NAME, false, false, false, null);
        //4.监听消息
        DeliverCallback deliverCallback = (consumerTag, delivery) -> {
            String message = new String(delivery.getBody(), "UTF-8");
            System.out.println("Received '" + message + "'");
        };
        channel.basicConsume(HELLO_QUEUE_NAME, true, deliverCallback, consumerTag -> {
        });
        System.in.read();
    }

2.工作队列通讯

与简单通讯一样,当消费能力不足或想要提高吞吐时可添加多个消费者进行处理业务。如下图,队列中的消息会逐条被C1和C2消费。

代码语言:java
复制
	public final static String WORK_QUEUE_NAME = "work";

    @Test
    public void publish_work_queue() throws IOException, TimeoutException {
        //1.获取连接对象
        Connection connection = MQConnections.getConnection();
        //2.构建Channl
        Channel channel = connection.createChannel();
        //3.构建队列,queueDeclare("队列名称","是否持久化队列","是否只允许一个队列消费","长时间未使用是否删除","其他参数")
        channel.queueDeclare(WORK_QUEUE_NAME, false, false, false, null);
        //4.发布消息
        String msg = "hello,work queue";
        channel.basicPublish("", WORK_QUEUE_NAME, null, msg.getBytes());
    }
        @Test
    public void consume_work_queue1() throws IOException, TimeoutException {
        //1.获取连接对象
        Connection connection = MQConnections.getConnection();
        //2.构建Channl
        Channel channel = connection.createChannel();
        //3.构建队列,queueDeclare("队列名称","是否持久化队列","是否只允许一个队列消费","长时间未使用是否删除","其他参数")
        channel.queueDeclare(WORK_QUEUE_NAME, false, false, false, null);
        //4.监听消息
        DeliverCallback deliverCallback = (consumerTag, delivery) -> {
            String message = new String(delivery.getBody(), "UTF-8");
            System.out.println("consume1 Received '" + message + "'");
        };
        channel.basicConsume(WORK_QUEUE_NAME, true, deliverCallback, consumerTag -> {
        });
        System.in.read();
    }

    @Test
    public void consume_work_queue2() throws IOException, TimeoutException {
        //1.获取连接对象
        Connection connection = MQConnections.getConnection();
        //2.构建Channl
        Channel channel = connection.createChannel();
        //3.构建队列,queueDeclare("队列名称","是否持久化队列","是否只允许一个队列消费","长时间未使用是否删除","其他参数")
        channel.queueDeclare(WORK_QUEUE_NAME, false, false, false, null);
        //4.监听消息
        DeliverCallback deliverCallback = (consumerTag, delivery) -> {
            String message = new String(delivery.getBody(), "UTF-8");
            System.out.println("consume2 Received '" + message + "'");
        };
        channel.basicConsume(WORK_QUEUE_NAME, true, deliverCallback, consumerTag -> {
        });
        System.in.read();
    }

3.发布/订阅通讯

工作队列背后的假设是,每个任务只交付给一个消费者做同一件事。如果要交付给多个消费者做不同的事,需要引入交换机实现一个完整的消息传递模型,这种模式被称为“发布/订阅”。如下图,消息会发布到交换机中,交换机向绑定的队列同时发送消息,最终C1和C2会同时消费此条消息。

代码语言:java
复制
public final static String PUB_EXCHANGE_NAME = "pub-ex";
    public final static String PUB1_QUEUE_NAME = "pub-que1";
    public final static String PUB2_QUEUE_NAME = "pub-que2";

    @Test
    public void publish_pub_sub() throws IOException, TimeoutException {
        //1.获取连接对象
        Connection connection = MQConnections.getConnection();
        //2.构建Channl
        Channel channel = connection.createChannel();
        //3.构建交换机
        channel.exchangeDeclare(PUB_EXCHANGE_NAME, BuiltinExchangeType.FANOUT);
        //4.构建队列,queueDeclare("队列名称","是否持久化队列","是否只允许一个队列消费","长时间未使用是否删除","其他参数")
        channel.queueDeclare(PUB1_QUEUE_NAME, false, false, false, null);
        channel.queueDeclare(PUB2_QUEUE_NAME, false, false, false, null);
        //5.绑定队列
        channel.queueBind(PUB1_QUEUE_NAME, PUB_EXCHANGE_NAME, "");
        channel.queueBind(PUB2_QUEUE_NAME, PUB_EXCHANGE_NAME, "");
        //6.发布消息
        String msg = "hello,pub/sub";
        channel.basicPublish(PUB_EXCHANGE_NAME, "", null, msg.getBytes());
    }
    @Test
    public void consume_pub_sub1() throws IOException, TimeoutException {
        //1.获取连接对象
        Connection connection = MQConnections.getConnection();
        //2.构建Channl
        Channel channel = connection.createChannel();
        //3.构建队列,queueDeclare("队列名称","是否持久化队列","是否只允许一个队列消费","长时间未使用是否删除","其他参数")
        channel.queueDeclare(PUB1_QUEUE_NAME, false, false, false, null);
        //4.监听消息
        DeliverCallback deliverCallback = (consumerTag, delivery) -> {
            String message = new String(delivery.getBody(), "UTF-8");
            System.out.println("pub_sub1 Received '" + message + "'");
        };
        channel.basicConsume(PUB1_QUEUE_NAME, true, deliverCallback, consumerTag -> {
        });
        System.in.read();
    }

    @Test
    public void consume_pub_sub2() throws IOException, TimeoutException {
        //1.获取连接对象
        Connection connection = MQConnections.getConnection();
        //2.构建Channl
        Channel channel = connection.createChannel();
        //3.构建队列,queueDeclare("队列名称","是否持久化队列","是否只允许一个队列消费","长时间未使用是否删除","其他参数")
        channel.queueDeclare(PUB2_QUEUE_NAME, false, false, false, null);
        //4.监听消息
        DeliverCallback deliverCallback = (consumerTag, delivery) -> {
            String message = new String(delivery.getBody(), "UTF-8");
            System.out.println("pub_sub2 Received '" + message + "'");
        };
        channel.basicConsume(PUB2_QUEUE_NAME, true, deliverCallback, consumerTag -> {
        });
        System.in.read();
    }

4.路由通讯

发布/订阅模式是交换机将一条消息同时路由给多个队列,“路由”模式可以将消息通过交换机指定到某个队列中从而被消费。如下图,交换机将所有类型的日志路由到一个队列中,将error类型的日志路由到另一个队列中。

代码语言:java
复制
    public final static String ROUT_EXCHANGE_NAME = "rout-ex";
    public final static String ROUTALL_QUEUE_NAME = "rout-queall";
    public final static String ROUTONE_QUEUE_NAME = "rout-queone";

    @Test
    public void publish_routing() throws IOException, TimeoutException {
        //1.获取连接对象
        Connection connection = MQConnections.getConnection();
        //2.构建Channl
        Channel channel = connection.createChannel();
        //3.构建交换机
        channel.exchangeDeclare(ROUT_EXCHANGE_NAME, BuiltinExchangeType.DIRECT);
        //4.构建队列,queueDeclare("队列名称","是否持久化队列","是否只允许一个队列消费","长时间未使用是否删除","其他参数")
        channel.queueDeclare(ROUTALL_QUEUE_NAME, false, false, false, null);
        channel.queueDeclare(ROUTONE_QUEUE_NAME, false, false, false, null);
        //5.绑定队列
        channel.queueBind(ROUTALL_QUEUE_NAME, ROUT_EXCHANGE_NAME, "all");
        channel.queueBind(ROUTONE_QUEUE_NAME, ROUT_EXCHANGE_NAME, "one");
        //6.发布消息
        String msg1 = "hello,1-all";
        String msg2 = "hello,2-all";
        String msg3 = "hello,1-one";
        channel.basicPublish(ROUT_EXCHANGE_NAME, "all", null, msg1.getBytes());
        channel.basicPublish(ROUT_EXCHANGE_NAME, "all", null, msg2.getBytes());
        channel.basicPublish(ROUT_EXCHANGE_NAME, "one", null, msg3.getBytes());
    }
    @Test
    public void consume_routing_all() throws IOException, TimeoutException {
        //1.获取连接对象
        Connection connection = MQConnections.getConnection();
        //2.构建Channl
        Channel channel = connection.createChannel();
        //3.构建队列,queueDeclare("队列名称","是否持久化队列","是否只允许一个队列消费","长时间未使用是否删除","其他参数")
        channel.queueDeclare(ROUTALL_QUEUE_NAME, false, false, false, null);
        //4.监听消息
        DeliverCallback deliverCallback = (consumerTag, delivery) -> {
            String message = new String(delivery.getBody(), "UTF-8");
            System.out.println("consume_routing_all Received '" + message + "'");
        };
        channel.basicConsume(ROUTALL_QUEUE_NAME, true, deliverCallback, consumerTag -> {
        });
        System.in.read();
    }

    @Test
    public void consume_routing_one() throws IOException, TimeoutException {
        //1.获取连接对象
        Connection connection = MQConnections.getConnection();
        //2.构建Channl
        Channel channel = connection.createChannel();
        //3.构建队列,queueDeclare("队列名称","是否持久化队列","是否只允许一个队列消费","长时间未使用是否删除","其他参数")
        channel.queueDeclare(ROUTONE_QUEUE_NAME, false, false, false, null);
        //4.监听消息
        DeliverCallback deliverCallback = (consumerTag, delivery) -> {
            String message = new String(delivery.getBody(), "UTF-8");
            System.out.println("consume_routing_one Received '" + message + "'");
        };
        channel.basicConsume(ROUTONE_QUEUE_NAME, true, deliverCallback, consumerTag -> {
        });
        System.in.read();
    }

5.主题通讯

“路由”模式仍然有局限性——它不能基于多个标准进行路由。主题可以带来很大的灵活性,发送到主题交换的消息不能有任意的routing_key,它必须是一个用点分隔的单词列表,routing_key有两种重要的特殊情况:

  • *只能代替一个词。
  • #可以替换零个或多个单词。
在这里插入图片描述
在这里插入图片描述
代码语言:java
复制
    public final static String TOPIC_EXCHANGE_NAME = "topic-ex";
    public final static String TOPICALL_QUEUE_NAME = "topic-queall";
    public final static String TOPICONE_QUEUE_NAME = "topic-queone";

    @Test
    public void publish_topic() throws IOException, TimeoutException {
        //1.获取连接对象
        Connection connection = MQConnections.getConnection();
        //2.构建Channl
        Channel channel = connection.createChannel();
        //3.构建交换机
        channel.exchangeDeclare(TOPIC_EXCHANGE_NAME, BuiltinExchangeType.TOPIC);
        //4.构建队列,queueDeclare("队列名称","是否持久化队列","是否只允许一个队列消费","长时间未使用是否删除","其他参数")
        channel.queueDeclare(TOPICALL_QUEUE_NAME, false, false, false, null);
        channel.queueDeclare(TOPICONE_QUEUE_NAME, false, false, false, null);
        //5.绑定队列
        channel.queueBind(TOPICALL_QUEUE_NAME, TOPIC_EXCHANGE_NAME, "*.all.*");
        channel.queueBind(TOPICONE_QUEUE_NAME, TOPIC_EXCHANGE_NAME, "#.one");
        //6.发布消息
        String msg1 = "hello.all.world";
        String msg2 = "hello.world.one";
        channel.basicPublish(TOPIC_EXCHANGE_NAME, "hello.all.world", null, msg1.getBytes());
        channel.basicPublish(TOPIC_EXCHANGE_NAME, "hello.world.one", null, msg2.getBytes());
    }
        @Test
    public void consume_topic_all() throws IOException, TimeoutException {
        //1.获取连接对象
        Connection connection = MQConnections.getConnection();
        //2.构建Channl
        Channel channel = connection.createChannel();
        //3.构建队列,queueDeclare("队列名称","是否持久化队列","是否只允许一个队列消费","长时间未使用是否删除","其他参数")
        channel.queueDeclare(TOPICALL_QUEUE_NAME, false, false, false, null);
        //4.监听消息
        DeliverCallback deliverCallback = (consumerTag, delivery) -> {
            String message = new String(delivery.getBody(), "UTF-8");
            System.out.println("consume_topic_all Received '" + message + "'");
        };
        channel.basicConsume(TOPICALL_QUEUE_NAME, true, deliverCallback, consumerTag -> {
        });
        System.in.read();
    }

    @Test
    public void consume_topic_one() throws IOException, TimeoutException {
        //1.获取连接对象
        Connection connection = MQConnections.getConnection();
        //2.构建Channl
        Channel channel = connection.createChannel();
        //3.构建队列,queueDeclare("队列名称","是否持久化队列","是否只允许一个队列消费","长时间未使用是否删除","其他参数")
        channel.queueDeclare(TOPICONE_QUEUE_NAME, false, false, false, null);
        //4.监听消息
        DeliverCallback deliverCallback = (consumerTag, delivery) -> {
            String message = new String(delivery.getBody(), "UTF-8");
            System.out.println("consume_topic_one Received '" + message + "'");
        };
        channel.basicConsume(TOPICONE_QUEUE_NAME, true, deliverCallback, consumerTag -> {
        });
        System.in.read();
    }

6.RPC通讯

RabbitMQ作为消息中间件可以达到应用解耦效果,如果想达到RPC远程调用同步返回结果,RabbitMQ同样支持,其原理如下:

  • 发布者发送消息时指定一个回调队列和唯一id
  • 消费者处理完成后将结果发送到回调队列中
  • 发布者按照唯一id接收消息并处理

如下图

在这里插入图片描述
在这里插入图片描述
代码语言:java
复制
    public final static String RPC_QUEUE_NAME = "rpc-que";
    public final static String RPCCALLBACK_QUEUE_NAME = "rpc-callback-que";

    @Test
    public void publish_rpc() throws IOException, TimeoutException {
        //1.获取连接对象
        Connection connection = MQConnections.getConnection();
        //2.构建Channl
        Channel channel = connection.createChannel();
        //3.构建队列,queueDeclare("队列名称","是否持久化队列","是否只允许一个队列消费","长时间未使用是否删除","其他参数")
        channel.queueDeclare(RPC_QUEUE_NAME, false, false, false, null);
        // replyTo回调队列
        channel.queueDeclare(RPCCALLBACK_QUEUE_NAME, false, false, false, null);
        //4.发布消息
        String msg = "hello rpc";
        String correlationId = UUID.randomUUID().toString();
        /*AMQP 协议预先定义了一组与消息一起使用的14个属性。除了以下属性外,大多数属性很少使用:
        deliveryMode:将消息标记为持久(值为2)或瞬时(任何其他值)。
        contentType:用于描述编码的mime类型。例如,对于常用的JSON编码,最好将此属性设置为:application/JSON。
        replyTo:通常用于命名回调队列。
        correlationId:用于将RPC响应与请求关联。*/
        AMQP.BasicProperties basicProperties = new AMQP.BasicProperties.Builder().replyTo(RPCCALLBACK_QUEUE_NAME).correlationId(correlationId).build();
        //5.回调响应结果
        channel.basicPublish("", RPC_QUEUE_NAME, basicProperties, msg.getBytes());
        DeliverCallback deliverCallback = (consumerTag, delivery) -> {
            String message = new String(delivery.getBody(), "UTF-8");
            String recid = delivery.getProperties().getCorrelationId();
            if (correlationId.equalsIgnoreCase(recid)) System.out.println("rpc-callback-que   '" + message + "'");
        };
        channel.basicConsume(RPCCALLBACK_QUEUE_NAME, true, deliverCallback, consumerTag -> {
        });
        System.in.read();
    }
        @Test
    public void consume_rpc() throws IOException, TimeoutException {
        //1.获取连接对象
        Connection connection = MQConnections.getConnection();
        //2.构建Channl
        Channel channel = connection.createChannel();
        //3.构建队列,queueDeclare("队列名称","是否持久化队列","是否只允许一个队列消费","长时间未使用是否删除","其他参数")
        channel.queueDeclare(RPC_QUEUE_NAME, false, false, false, null);
        //4.监听消息
        DeliverCallback deliverCallback = (consumerTag, delivery) -> {
            String message = new String(delivery.getBody(), "UTF-8");
            System.out.println("consume_rpc Received '" + message + "'");
            String correlationId = delivery.getProperties().getCorrelationId();
            AMQP.BasicProperties basicProperties = new AMQP.BasicProperties.Builder().correlationId(correlationId).build();
            String replyTo = delivery.getProperties().getReplyTo();
            String callbackmsg = "rpc callback";
            channel.basicPublish("", replyTo, basicProperties, callbackmsg.getBytes());
        };
        channel.basicConsume(RPC_QUEUE_NAME, true, deliverCallback, consumerTag -> {
        });
        System.in.read();
    }

7.Publisher确认通讯

Publisher确认是RabbitMQ扩展以实现可靠发布。当在通道上启用发布者确认时,代理将异步确认客户端发布的消息,这意味着它们已在服务器端得到处理。

代码语言:java
复制
    public final static String CONFIRM_EXCHANGE_NAME = "confirm-ex";
    public final static String CONFIRM_QUEUE_NAME = "confirm-que";

    @Test
    public void publish_confirm() throws IOException, TimeoutException {
        //1.获取连接对象
        Connection connection = MQConnections.getConnection();
        //2.构建Channl
        Channel channel = connection.createChannel();
        //3.开启确认选项
        channel.confirmSelect();
        //4.构建交换机
        channel.exchangeDeclare(CONFIRM_EXCHANGE_NAME, BuiltinExchangeType.DIRECT);
        //5.构建队列,queueDeclare("队列名称","是否持久化队列","是否只允许一个队列消费","长时间未使用是否删除","其他参数")
        channel.queueDeclare(CONFIRM_QUEUE_NAME, true, false, false, null);
        //6.绑定队列
        String right_routing_key = "confirm";
        String error_routing_key = "confirm_err";
        channel.queueBind(CONFIRM_QUEUE_NAME, CONFIRM_EXCHANGE_NAME, right_routing_key);
        //7.消息到达交换机确认监听
        channel.addConfirmListener((sequenceNumber, multiple) -> {
            System.out.println("消息成功发送到交换机");
        }, (sequenceNumber, multiple) -> {
            System.err.println("消息未发送到交换机,补偿操作。");
        });
        //8.消息到达队列确认监听
        channel.addReturnListener((replyCode, replyText, exchange, routingKey, basicProperties, body) -> {
            System.err.format("消息 %s 未路由到指定队列: %s, replyText: %s,replyCode: %d%n", body, routingKey, replyText, replyCode);
        });
        //设置消息持久化
        AMQP.BasicProperties basicProperties = new AMQP.BasicProperties.Builder().deliveryMode(2).build();
        //7.发布消息
        String msg = "hello confirm";
        channel.basicPublish(CONFIRM_EXCHANGE_NAME, error_routing_key,true, basicProperties, msg.getBytes());
        System.in.read();
    }

	@Test
    public void consume_ack() throws IOException, TimeoutException {
        //1.获取连接对象
        Connection connection = MQConnections.getConnection();
        //2.构建Channl
        Channel channel = connection.createChannel();
        //3.构建队列,queueDeclare("队列名称","是否持久化队列","是否只允许一个队列消费","长时间未使用是否删除","其他参数")
        channel.queueDeclare(CONFIRM_QUEUE_NAME, true, false, false, null);
        //4.监听消息
        DeliverCallback deliverCallback = (consumerTag, delivery) -> {
            String message = new String(delivery.getBody(), "UTF-8");
            System.out.println("consume_routing_one Received '" + message + "'");
            //消息处理后手动ACK
            channel.basicAck(delivery.getEnvelope().getDeliveryTag(),false);
        };
        // ack为false
        channel.basicConsume(CONFIRM_QUEUE_NAME, false, deliverCallback, consumerTag -> {
        });
        System.in.read();
    }

代码仓库

https://gitee.com/codeWBG/learn_rabbitmq

我正在参与2024腾讯技术创作特训营第五期有奖征文,快来和我瓜分大奖!

原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。

如有侵权,请联系 cloudcommunity@tencent.com 删除。

原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。

如有侵权,请联系 cloudcommunity@tencent.com 删除。

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 环境说明
  • 工程搭建
  • 连接RabbitMQ
  • 通讯模式
    • 1.简单通讯
      • 2.工作队列通讯
        • 3.发布/订阅通讯
          • 4.路由通讯
            • 5.主题通讯
              • 6.RPC通讯
                • 7.Publisher确认通讯
                • 代码仓库
                相关产品与服务
                消息队列 TDMQ
                消息队列 TDMQ (Tencent Distributed Message Queue)是腾讯基于 Apache Pulsar 自研的一个云原生消息中间件系列,其中包含兼容Pulsar、RabbitMQ、RocketMQ 等协议的消息队列子产品,得益于其底层计算与存储分离的架构,TDMQ 具备良好的弹性伸缩以及故障恢复能力。
                领券
                问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档