消息队列 消息中间件 RabbitMq的五种模型实战详解 RabbitMq实战教程

目录

1、MQ引言

1.1 什么是MQ

1.2 MQ有哪些

1.3不同MQ的特点

2、RabbitMQ的引言

2.1 RabbitMQ

2.2 RabbitMQ的访问地址

 3、RabbitMQ的第一个程序

3.1 添加用户、虚拟主机并绑定

3.2添加新用户

3.3将用户绑定对应的虚拟机

3.4完成绑定

3.5 引入依赖

 4、第一种模型:直连

4.1 封装简单工具类

4.2 生产者Provider 

4.3 消费者Consumer 

 5、第二种模型:work queues

 5.1 生产者Provider

5.2 消费者Consumer1

5.3 消费者Consumer2

 5.4 消息自动确认机制

 5.5 改造消费者1、消费者2

5.6 结论

6 第三种模型:Fanout

6.1 生产者Provider

6.2 消费者Consumer

6.3 临时队列

 7、第四种模式:routing

7.1 生产者Provider

7.2 消费者Consumer

7.3 结论

8、第五种模型:Topic

8.1 生产者Provider

8.2消费者Consumer

8.3 结论


1、MQ引言

1.1 什么是MQ

MQ(Message Queue):翻译为消息队列,通过典型的生产者和消费者模型,生产者不断从消息队列中产生消息,消费者不断从消息队列中获取消息。因为消息的生产和消费都是异步的,而且只关心消息的发送和接收,没有业务逻辑的侵入,轻松的实现业务间的解耦。别名消息中间件通过利用高效可靠的消息传递机制进行平台无关的数据交流,并基于数据通信来进行分布式系统的集成。

1.2 MQ有哪些

当今市面上有很多主流的消息中间件,如老牌的ActiveMQRabbitMQ、炙手可热的Kafka、阿里巴巴自主开发RocketMQ等。

1.3不同MQ的特点

ActiveMQApache出品,最流行的,能力强劲的开源消息总线。他是一个完全支持JMS规范的消息中间件。丰富的API。多种集群架构模式让ActiveMQ在业界称为老牌的消息中间件,在中小企业颇受欢迎。
KafkaKafka是LinkedIn开源的分布式发布-订阅消息系统,目前归属于Apache顶级项目。Kafka主要特点是基于Pull的模式来处理消息消费,追求高吞吐量,一开始的目的就是用于日志收集和传输。0.8版本开始支持复制,不支持事务,对消息的重复,丢失、错误没有严格要求,适合产生大量数据的互联网服务的数据收集业务。
RocketMQRocketMQ是阿里开源的消息中间件,它是纯Java开发,具有高吞吐量、高可用性、适合大规模分布式系统应用的特点。RocketMQ思路起源于Kafka,但并不是Kafka的一个Copy,它对消息的可靠传输及事务性做了优化。目前在阿里集团被广泛应用于交易、充值、流计算、消息推送、日志流式处理、binglog分发等场景。
RabbitMQRabbitMQ是使用Erlang语言开发的开源消息队列系统,基于AMQP协议来实现。AMQP的主要特征是面向消息、队列、路由(包括点对点和发布/订阅)、可靠性、安全。AMQP协议更多用在企业系统内对数据一致性、稳定性和可靠性要求很高的场景、对性能和吞吐量的要求还在其次。

RabbitMQ 比 Kafka 可靠,Kafka 更适合 IO 高吞吐量的处理,一般应用在大数据日志处理或对实时性(少量延迟),可靠性(少量丢数据)要求稍低的场景使用,比如ELK日志收集。

2、RabbitMQ的引言

2.1 RabbitMQ

基于AMQP协议,erlang语言开发,是部署最广泛的开源消息中间件,是最受欢迎的开源消息中间件之一。

AMQP:即Advanced Message Queuing Protocol,一个提供统一消息服务的应用层标准高级消息队列协议,是应用层协议的一个开放标准,为面向消息的中间件设计。基于此协议的客户端与消息中间件可传递消息,并不受客户端/中间件不同产品,不同的开发语言等条件的限制。Erlang中的实现有RabbitMQ等。

2.2 RabbitMQ的访问地址

在安装(Linux不好装的话可以装在Windows里)并启动好RabbitMQ之后访问localhost:15672(账号和密码都是guest)

 3、RabbitMQ的第一个程序

3.1 添加用户、虚拟主机并绑定

注意:必须 / 开头

3.2添加新用户

3.3将用户绑定对应的虚拟机

3.4完成绑定

3.5 引入依赖

<dependencies>
        <!-- https://mvnrepository.com/artifact/com.rabbitmq/amqp-client -->
        <dependency>
            <groupId>com.rabbitmq</groupId>
            <artifactId>amqp-client</artifactId>
            <version>5.7.2</version>
        </dependency>
        <dependency>
            <groupId>junit</groupId>
            <artifactId>junit</artifactId>
            <version>4.13.2</version>
        </dependency>
    </dependencies>

 4、第一种模型:直连

4.1 封装简单工具类

package rabbitMq.utils;

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

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

public class RabbitMQUtils {
    private static ConnectionFactory connectionFactory;

    // 类加载时只执行一次
    static {
        connectionFactory = new ConnectionFactory();
    }

    // 定义提供连接对象的方法
    public static Connection getConnection(){
        // 连接mq主机
        connectionFactory.setHost("localhost");
        // 设置端口号
        connectionFactory.setPort(5672);
        // 设置连接哪个虚拟主机
        connectionFactory.setVirtualHost("/ems");
        // 设置访问虚拟主机的用户名和密码
        connectionFactory.setUsername("ems");
        connectionFactory.setPassword("123");

        // 通道绑定对应的消息队列
        try {
            return connectionFactory.newConnection();
        } catch (IOException | TimeoutException e) {
            e.printStackTrace();
        }
        return null;
    }

    // 关闭通道和关闭连的接方法
    public static void closeChanelAndConnection(Channel channel, Connection connection){
        try {
            if(channel != null){
                channel.close();
            }
            if(connection != null){
                connection.close();
            }
        } catch (IOException | TimeoutException e) {
            e.printStackTrace();
        }
    }

}

4.2 生产者Provider 

package rabbitMq;


import com.rabbitmq.client.Channel;
import com.rabbitmq.client.Connection;
import org.junit.Test;
import rabbitMq.utils.RabbitMQUtils;

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

public class Provider {
    @Test
    public void testSendMessage() throws IOException, TimeoutException {

        // // 获取连接
        Connection connection = RabbitMQUtils.getConnection();
        // 获取通道
        Channel channel = connection.createChannel();
        
        // 通道绑定对应的消息队列
        // 参数1:队列名称,如果队列不存在会自动创建
        // 参数2:用来定义队列特性是否要持久化
        // 参数3:是否独占队列,表示只有当前连接可用该队列
        // 参数4:是否在消费完成后删除队列
        // 参数5:额外附加参数

        channel.queueDeclare("hello", false, false, false, null );

        // 发布消息
        // 参数1:交换机名称
        // 参数2:队列名称
        // 参数3:传递消息额外设置
        // 参数4:消息的具体内容
        channel.basicPublish("","hello", null, "hello rabbitmq".getBytes());

        // // 关闭
        RabbitMQUtils.closeChanelAndConnection(channel,RabbitMQUtils.getConnection());
    }
}

 运行生产者:

查看消息详情

4.3 消费者Consumer 

package rabbitMq;

import com.rabbitmq.client.*;
import rabbitMq.utils.RabbitMQUtils;

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

public class Consumer {
    public static void main(String[] args) throws IOException, TimeoutException {

        // // 获取连接
        Connection connection = RabbitMQUtils.getConnection();
        // 获取通道
        Channel channel = connection.createChannel();

        // 通道绑定对应的消息队列
        // 参数1:队列名称,如果队列不存在会自动创建
        // 参数2:用来定义队列特性是否要持久化
        // 参数3:是否独占队列,表示只有当前连接可用该队列
        // 参数4:是否在消费完成后删除队列
        // 参数5:额外附加参数
        channel.queueDeclare("hello", false, false, false, null);

        // 消费消息
        // 参数1:消费哪个队列的消息
        // 参数2:开启消息的自动确认机制
        // 参数3:消费时回调的接口
        channel.basicConsume("hello", true, new DefaultConsumer(channel){
            // 参数:body消息队列里取出的消息
            @Override
            public void handleDelivery(String consumerTag, Envelope envelope, AMQP.BasicProperties properties, byte[] body) throws IOException {
                super.handleDelivery(consumerTag, envelope, properties, body);
                System.out.println("输出队列里的内容--->>>" + new String(body));
            }
        });

        // 如果没关闭 消费者会一直等待
        // 如果关闭 不会等待结果出来
        // RabbitMQUtils.closeChanelAndConnection(channel,RabbitMQUtils.getConnection());
    }
}

运行消费者

 成功消费刚刚生产的消息消息

回到地址查看

 Provider生产的消息确实被成功消费


 5、第二种模型:work queues

Work queues,也被称为(Task queues),任务模型。当消息处理比较耗时的时候,可能生产消息的速度会远远大于消息的消费速度。长此以往,消息就会堆积越来越多,无法及时处理。此时就可以使用work模型:让多个消费者绑定到一个队列,共同消费队列中的消息。队列中的消息一旦消费,就会消失,因此任务是不会被重复执行的 

 5.1 生产者Provider

package rabbitmq.workqueue;

import com.rabbitmq.client.Channel;
import com.rabbitmq.client.Connection;
import rabbitmq.utils.RabbitMQUtils;

import java.io.IOException;

public class Provider {
    public static void main(String[] args) throws IOException {
        // 获取连接
        Connection connection = RabbitMQUtils.getConnection();
        // 获取通道
        Channel channel = connection.createChannel();

        // 通过通道声明队列
        // 参数1:队列名称,如果队列不存在会自动创建
        // 参数2:用来定义队列特性是否要持久化
        // 参数3:是否独占队列,表示只有当前连接可用该队列
        // 参数4:是否在消费完成后删除队列
        // 参数5:额外附加参数
        channel.queueDeclare("work", true, false, false, null);

        for (int i = 1; i <= 20; i++) {
            // 生产消息
            String message = "hello work queue-" + i;
            // 参数1:交换机名称
            // 参数2:队列名称
            // 参数3:传递消息额外设置
            // 参数4:消息的具体内容
            channel.basicPublish("", "work", null, message.getBytes());
        }

        // 关闭
        RabbitMQUtils.closeChanelAndConnection(channel, connection);
    }
}

5.2 消费者Consumer1

package rabbitmq.workqueue;

import com.rabbitmq.client.*;
import rabbitmq.utils.RabbitMQUtils;

import java.io.IOException;

public class Consumer1 {
    public static void main(String[] args) throws IOException {
        // 获取连接
        Connection connection = RabbitMQUtils.getConnection();
        // 获取通道
        Channel channel = connection.createChannel();

        // 通过通道声明队列
        // 参数1:队列名称,如果队列不存在会自动创建
        // 参数2:用来定义队列特性是否要持久化
        // 参数3:是否独占队列,表示只有当前连接可用该队列
        // 参数4:是否在消费完成后删除队列
        // 参数5:额外附加参数
        channel.queueDeclare("work", true, false, false, null);

        // 消费消息
        channel.basicConsume("work", true, 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));
            }
        });
    }
}

5.3 消费者Consumer2

package rabbitmq.workqueue;

import com.rabbitmq.client.*;
import rabbitmq.utils.RabbitMQUtils;

import java.io.IOException;

public class Consumer2 {
    public static void main(String[] args) throws IOException {
        // 获取连接
        Connection connection = RabbitMQUtils.getConnection();
        // 获取通道
        Channel channel = connection.createChannel();

        // 通过通道声明队列
        channel.queueDeclare("work", true, false, false, null);

        // 消费消息
        channel.basicConsume("work", true, new DefaultConsumer(channel){
            @Override
            public void handleDelivery(String consumerTag, Envelope envelope, AMQP.BasicProperties properties, byte[] body) throws IOException {
                try {
                    Thread.sleep(2000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                System.out.println("消费者2---" + new String(body));
            }
        });


    }
}

运行两个消费者Consumer1、Consumer2,再运行消息的生产者Provider

 消费者1瞬间完成任务,消费者2要隔两秒完成一个任务,但任务是平均分配的,不满足我们日常应用,应该能者多劳。

 5.4 消息自动确认机制

完成一项任务可能需要几秒钟。 您可能想知道如果 一位消费者开始了一项漫长的任务,而死于其中的一部分。使用我们当前的代码,RabbitMQ一旦向消费者传递了一条消息 立即将其标记为删除。 在这种情况下,如果您杀死工人 我们将丢失正在处理的消息。我们也会失去所有 发送给该特定工作人员但未发送的消息 尚未处理。 但是我们不想丢失任何任务。 如果一个工人死了,我们希望 任务要交付给另一名工人。 为了确保消息永不丢失,RabbitMQ支持 消息确认 。 确认由主机发回。消费者告诉RabbitMQ已经收到了特定的消息,处理后,RabbitMQ可以自由删除它。

 5.5 改造消费者1、消费者2

// 每一次只能消费一个消息
channel.basicQos(1);
channel.basicConsume("work", false, 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));
        // 手动确认 参数1:手动确认消息标识  参数2:false每次确认一个
        channel.basicAck(envelope.getDeliveryTag(), false);
    }
});

如果我们将其中一个手动确认注释掉,观察MQ管理器会发生什么情况呢?

// channel.basicAck(envelope.getDeliveryTag(), false);

所以通常我们都会在消费者完成消费后,手动确认消息已消费。

5.6 结论

默认情况下,RabbitMQ会将每条消息按顺序发送给下一个使用者。 平均每个消费者将获得相同数量的消息。 这种分发消息的方式称为循环

如果我们想要做到能者多劳,就要改变消息自动确认机制,改为手动确认消息。


6、 第三种模型:Fanout

Fanout 也称为广播

RabbitMQ消息传递模型中的核心思想是生产者 从不直接将任何消息发送到队列。

在广播模式下,消息发送流程是这样的:

  • 可以有多个消费者
  • 每个消费者有自己的queue(队列)
  • 每个队列都要绑定到Exchange(交换机)
  • 生产者发送的消息,只能发送到交换机,交换机来决定要发给哪个队列,生产者无法决定
  • 交换机把消息发送给绑定过的所有队列
  • 队列的消费者都能拿到消息。实现一条消息被多个消费者消费

6.1 生产者Provider

package rabbitmq.fanout;

import com.rabbitmq.client.Channel;
import com.rabbitmq.client.Connection;
import rabbitmq.utils.RabbitMQUtils;

import java.io.IOException;

public class Provider {
    public static void main(String[] args) throws IOException {
        // 获取连接对象
        Connection connection = RabbitMQUtils.getConnection();
        // 获取通道
        Channel channel = connection.createChannel();

        // 将通道声明指定的交换机
        // 参数1:交换机名称
        // 参数2:交换机类型 fanout标识广播
        channel.exchangeDeclare("logs", "fanout");

        // 发送消息
        // 参数1:交换机名称
        String message = "fanout type message";
        channel.basicPublish("logs", "", null, message.getBytes());

        // 关闭
        RabbitMQUtils.closeChanelAndConnection(channel, connection);
    }
}

注意:交换机类型有几种可用: direct , topic , headers 和 fanout 。 我们将演示最后一个-fanout。

fanout交换非常简单。 您可能会从中猜到名称,它只是将收到的所有消息广播给所有知道的队列。

6.2 消费者Consumer

假设有三个相同的消费者1、2、3

package rabbitmq.fanout;

import com.rabbitmq.client.*;
import rabbitmq.utils.RabbitMQUtils;

import java.io.IOException;

public class Consumer1 {
    public static void main(String[] args) throws IOException {
        // 获取连接
        Connection connection = RabbitMQUtils.getConnection();
        // 获取通道
        Channel channel = connection.createChannel();

        // 通道绑定交换机
        channel.exchangeDeclare("logs", "fanout");

        // 临时队列
        String queueName = channel.queueDeclare().getQueue();

        // 绑定交换机和队列
        // 参数1:临时队列名
        // 参数2:交换机名称
        channel.queueBind(queueName, "logs", "");

        // 消费消息
        channel.basicConsume(queueName, true, 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));
                System.out.println(queueName);
            }
        });


    }
}

6.3 临时队列

首先,无论何时我们连接到Rabbit,我们都需要一个全新的空队列。 为此,我们可以使用随机名称创建一个队列,或者甚至更好-让服务器为我们选择一个随机队列名称。

其次,一旦我们断开与消费者的联系,队列应该是自动删除。

在Java客户端中,当我们不向提供任何参数时 queueDeclare() 我们使用生成的名称创建一个非持久的,排他的,自动删除队列:

String queueName = channel.queueDeclare().getQueue();

此时, queueName 包含一个随机队列名称。 


 7、第四种模型:routing

Routing之订阅模型-Direct(直连)

在Fanout(广播)模式中,一条消息,会被所有订阅的队列都消费。但是,在某些场景下,我们希望不同的消息被不同的队列消费。这时就要用到Direct类型的Exchange。

在Direct模型下:

  • 队列与交换机的绑定,不能是任意绑定了,而是要指定一个RoutingKey(路由key)
  • 消息的发送方在向Exchange发送消息时,也必须指定消息的RoutingKey
  • Exchange不再把消息交给每一个绑定的队列,而是根据消息的RoutingKey进行判断,只有队列的RoutingKey与消息的RoutingKey完全一致,才会接收到消息

7.1 生产者Provider

生产者需要通过通道声明交换机并指定交换机类型,通过routingKey决定消息发送到哪个消息队列

package rabbitmq.direct;

import com.rabbitmq.client.Channel;
import com.rabbitmq.client.Connection;
import rabbitmq.utils.RabbitMQUtils;

import java.io.IOException;

public class Provider {
    public static void main(String[] args) throws IOException {
        // 获取连接
        Connection connection = RabbitMQUtils.getConnection();
        // 获取通道
        Channel channel = connection.createChannel();

        String exchangeName = "logs_direct";

        // 通过通道声明交换机
        // 参数1:交换机名称
        // 参数2:交换机类型  direct表示路由模式
        channel.exchangeDeclare(exchangeName, "direct");

        // 发送消息
        String routingKey = "waring";
        String message = "这是direct模型发布的基于routerKey: [" + routingKey + "] 发送的消息";
        channel.basicPublish(exchangeName, routingKey, null, message.getBytes());

        // 关闭
        RabbitMQUtils.closeChanelAndConnection(channel, connection);
    }
}

7.2 消费者Consumer

消费者1:基于routerKey绑定队列和交换机,只接收error消息。

package rabbitmq.direct;

import com.rabbitmq.client.*;
import rabbitmq.utils.RabbitMQUtils;

import java.io.IOException;

public class Consumer1 {
    public static void main(String[] args) throws IOException {

        Connection connection = RabbitMQUtils.getConnection();
        Channel channel = connection.createChannel();

        String exchangeName = "logs_direct";

        // 通道声明交换机以及交换机的类型
        channel.exchangeDeclare(exchangeName, "direct");

        // 创建一个临时队列
        String queueName = channel.queueDeclare().getQueue();

        // 基于routerKey绑定队列和交换机
        channel.queueBind(queueName, exchangeName, "error");

        // 获取消息
        channel.basicConsume(queueName, true, 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));
            }
        });
    }
}

消费者2:基于routerKey绑定队列和交换机,接收info、error、waring消息。

package rabbitmq.direct;

import com.rabbitmq.client.*;
import rabbitmq.utils.RabbitMQUtils;

import java.io.IOException;

public class Consumer2 {
    public static void main(String[] args) throws IOException {

        Connection connection = RabbitMQUtils.getConnection();
        Channel channel = connection.createChannel();

        String exchangeName = "logs_direct";

        // 通道声明交换机以及交换机的类型
        channel.exchangeDeclare(exchangeName, "direct");

        // 创建一个临时队列
        String queueName = channel.queueDeclare().getQueue();

        // 基于routerKey绑定队列和交换机
        channel.queueBind(queueName, exchangeName, "info");
        channel.queueBind(queueName, exchangeName, "error");
        channel.queueBind(queueName, exchangeName, "warning");

        // 获取消息
        channel.basicConsume(queueName, true, 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));
            }
        });
    }
}

生产者发送三次消息,分别是info、error和warning消息

7.3 结论

可以看到,消费者1只关心error消息,不关心其他消息,而消费者2关心info、error和warning消息。假设发送的是debug,则两个消费者都不会关心。


8、第五种模型:Topic

Routing之订阅模型-Topic

Topic类型的Exchange与Direct相比,都是可以根据RoutingKey把消息路由到不同的队列。只不过Topic类型Exchange可以让队列在绑定RoutingKey的时候使用通配符。

这种类型RoutingKey一般都是由一个或多个单词组成,多个单词之间以“.”分隔,例如:item.insert

# 通配符:
  1. * (star) can substitute for exactly one word.   匹配一个词
  2. # (hash) can substitute for zero or more words. 匹配一个或多个词
  
# 例如:
  audit.*  只能匹配audit.irs
  audit.#  可以匹配audit.irs.corporate 或者 audit.irs 等

8.1 生产者Provider

生产者发送消息时,routerKey可以指定规则。

package rabbitmq.topic;

import com.rabbitmq.client.Channel;
import com.rabbitmq.client.Connection;
import rabbitmq.utils.RabbitMQUtils;
import java.io.IOException;

public class Provider {

    public static void main(String[] args) throws IOException {

        // 获取连接
        Connection connection = RabbitMQUtils.getConnection();

        // 获取通道
        Channel channel = connection.createChannel();

        // 声明交换机以及交换机类型
        channel.exchangeDeclare("topics", "topic");

        // 发布消息
        String routerKey = "user.save.findAll";
        //String routerKey = "user.save.findAll";
        String message = "这里是topic动态路由模型,routerKey: [" + routerKey + "] 发送的消息";
        channel.basicPublish("topics", routerKey, null, message.getBytes());

        // 关闭
        RabbitMQUtils.closeChanelAndConnection(channel, connection);
    }
}

8.2消费者Consumer

消费者1:使用“*”匹配,只能匹配一个词

package rabbitmq.topic;

import com.rabbitmq.client.*;
import rabbitmq.utils.RabbitMQUtils;
import java.io.IOException;

public class Consumer1 {
    public static void main(String[] args) throws IOException {

        // 获取连接
        Connection connection = RabbitMQUtils.getConnection();
        Channel channel = connection.createChannel();

        // 声明交换机以及交换机类型
        channel.exchangeDeclare("topics", "topic");

        // 创建一个临时队列
        String queueName = channel.queueDeclare().getQueue();

        // 绑定队列和交换机,基于动态通配符形式
        // * 只能匹配一个词
        channel.queueBind(queueName, "topics", "user.*");

        // 消费消息
        channel.basicConsume(queueName, true, 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));
            }
        });
    }
}

消费者2:使用“#”匹配,可以匹配一个或多个词

package rabbitmq.topic;

import com.rabbitmq.client.*;
import rabbitmq.utils.RabbitMQUtils;


import java.io.IOException;

public class Consumer2 {

    public static void main(String[] args) throws IOException {

        // 获取连接
        Connection connection = RabbitMQUtils.getConnection();
        Channel channel = connection.createChannel();

        // 声明交换机以及交换机类型
        channel.exchangeDeclare("topics", "topic");

        // 创建一个临时队列
        String queueName = channel.queueDeclare().getQueue();

        // 绑定队列和交换机,基于动态通配符形式
        // # 匹配一个或多个词
        channel.queueBind(queueName, "topics", "user.#");

        // 消费消息
        channel.basicConsume(queueName, true, 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));
            }
        });
    }
}

开启Consumer1、Consumer2并使用Provider发送user.save以及user.save.findAll

8.3 结论

可以看到,这相当于是第四种模型的增强版,通过不同的匹配规则,消费者1和消费者2接收的消息是不一样的。

  • 3
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
天猫商城是一个基于SSM框架的综合性B2C电商平台,需求设计主要参考天猫商城的购物流程:用户从注册开始,到完成登录,浏览商品,加入购物车,进行下单,确认收货,评价等一系列操作。 作为模拟天猫商城系统的核心组成部分之一,采用SSM框架的天猫数据管理后台包含商品管理,订单管理,类别管理,用户管理和交易额统计等模块,实现了对整个商城的一站式管理和维护。本课程是一门专业的Java微服架构开发实战课程,主要讲解了当下流行的SpringBoot框架、SpringCloud架构以及与第三方技术整合开发实战内容。通过本课程的学习,能够理解并掌握SpringBoot的基础知识,同时能够掌握SpringBoot与常用的第三方技术整合实现实际开发中的业务需求,包括实现Web开发、数据访问、缓存管理、安全管理、消息服务、任务管理等;了解并掌握SpringCloud微服务架构的基础知识及相关组件的应用,掌握微服务架构在企业级开发的实践,建立起微服架构思想。项目技术栈:采用SpringBoot简化商城系统的初始搭建以及开发过程采用SpringMVC+Spring+IBatis完成项目的整合采用Mysql作为数据库存储,Druid配置数据库连接池采用SpringCloud+Netflix 微服务技术栈的实战开发使用Redis完成缓存的数据存储,搭建Redis搭建主从、哨兵、集群应用,保证Redis的高可用使用ElasticSearch全文检索系统进行商品数据搜索,使用ElasticSearch搭建搜索服务的高可用使用Ngnix实现页面动静分离与负载均衡的配置采用FastDFS文件储存系统文件存储,完成广告图片、商品图片的上传和存储系统使用采用CAS+shiro单点登录系统实现用户认证使用ECharts根据后台查询数据生成图表使用POI实现了商城盈利状况的Excel表格导出。商品的详情页使用Thymeleaf完成页面静态化,减少页面数据展示延迟项目中使用SpringBoot下的Aop + 自定义注解完成用户行为记录,日志采集后台管理系统使用Shiro实现登录验证和权限管理(超级管理员、管理员、产品编辑员)项目整合微信完成订单的支付使用Redission完成分布式锁,生成订单的编号使用SpringCloud Alibaba Seat完成下订单模块的分布式事务(新增订单表,库存减少,库存超卖设计)使用RabbitMQ消息队列,完成订单未支付自动取消和模块直接的解耦合使用Quartz任务调度,完成缓存的定时刷新,保证缓存的一致性使用本地消息表机制完成消息然队列RabbitMQ消息可靠性传输订单支付模块使用微信扫码支付,并设置订单超时自动取消通过Jquery实现前端校验,通过基于Hibernate的Valida注解实现后端的校验功能使用Base64编码对Json数据传输进行编码和解码项目使用RESTful设计风格实现资源的访问,实现前后端分离项目使用聚合数据第三方短信平台完成用户的登陆功能项目使用SpringBoot整合JavaMail完成邮件的发送项目使用SpringBoot整合Swagger2生成接口文档使用PostMan完成接口的测试项目的测试:SpringTest、dbunit、EasyMock使用Docker 进行应用的自动化打包和发布、自动化测试和持续集成、部署和调整其他应用使用 PowerDesigner,完成数据库的建模项目使用禅道进行BUG管理环境采用Maven实施多模块项目构建,采用Git进行项目版本管理 架构解读:  项目部分截图:              讲义部分截图:          
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值