SpringBoot 整合 rabbitMQ 入门

springboot 是目前以Java 语言开发的主流框架了,rabbitmq 是常用的消息队列中的一种,因为其功能比较完备且社区活跃,可以解决开发过程中遇到的bug,结合erlang 语言的并发优势,性能较好,所以适合中小公司的企业级项目使用。下面我们结合其他几个常用的消息队列一起了解一下这个中间件:

使用MQ 的优劣

优势

应用解耦:消息队列较少服务之间的耦合性,不同的服务可以通过消息队列进行通信,而不用关心彼此的实现细节,提高了系统的容错性和可维护性。

异步处理:消息队列本身是异步的,只要保存自己的数据并且将请求发送到MQ 就可以快速的给予反馈信息,极大的提升了系统的吞吐量。

流量削峰:当上下游系统处理能力存在差距的时候,利用消息队列做一个通用的“载体”,在下游有能力处理的时候,再进行分发与处理。

日志处理:日志处理是指将消息队列用在日志处理中,比如kafka 的应用,解决大量日志传输的问题。

消息通讯:消息队列一般的哦内置了高效的通信机制,因此也可以用在纯消息通讯的地方,比如实现点对点消息队列,或者聊天室等。

劣势

系统可用性降低:MQ一旦故障了,系统A就没法发送消息给MQ啦,然后系统BCD也没法消费到消息。整个系统就崩溃了,没有办法正常运转啦。

系统复杂性变高:硬生生加了个MQ,你怎么保证消费没有重复消费?怎么处理消息丢失的情况?怎么保证消息传递的顺序性。问题一大堆,痛苦不已。

系统一致性问题:系统ABC执行成功了,但是系统D执行失败了。但是给用户返回执行成功了,结果后台逻辑实际上是差了一点。完全没有执行完。

以上就是使用消息队列带来的优劣了,不过劣势可以通过技术减低,例如可用性问题可以通过搭建MQ 的高可用环境,进而提升MQ 的可用性等。

RabbitMQ 介绍

RabbitMQ 是一个由Erlang 语言开发的 AMQP(Advanved Message Queue Protocol,高级消息队列协议。它是应用层协议的一个开放标准,为面向消息的中间件设计,基于此协议的客户端与消息中间件可传递消息,并不受产品、开发语言等条件的限制。)的开源实现,也就是消息服务中间件,本质上是个队列,遵循FIFO 先进先出原则。多用于分布式系统之间进行通信,用来提升系统的异步通信、扩展解耦能力。再来看下rabbitmq 的架构图:

​组成的部分主要由:producer(生产者)、 broker(消息接收和发送服务器)、 consumer(消费者)组成,一起来看下其他关键节点代表的含义:

  • Producer:消息生产者。

  • Consumer:消息消费者。

  • Broker:接收和分发消息的应用,RabbitMQ Server,服务器实体

  • Connection(连接):Producer 和 Consumer 通过TCP 连接到 RabbitMQ Server。

  • Channel(信道):如果每一次访问RabbitMQ 就建立一个Connection,在数据量大的时候开销就很大,效率较低。Channel 是在 connection 内部建立的逻辑连接,如果应用程序支持多线程,通常每个thread 创建单独的channel 进行通信。AMQP method 包含了channel id 帮助客户端和message broker 识别channel,所以channel 之间是完全隔离的。Channel 作为轻量级的Connnection极大减少了操作系统建立TCP connection 的开销。

  • Exchange(交换器):生产者将消息发送到 Exchange(交换器),由 Exchange 将消息路由到一个或多个 Queue 中(或者丢弃);Exchange 并不存储消息;Exchange Types 常用有 Fanout、Direct、Topic 三种类型,每种类型对应不同的路由规则。

  • Queue(队列):是 RabbitMQ 的内部对象,用于存储消息;消息消费者就是通过订阅队列来获取消息的,RabbitMQ 中的消息都只能存储在 Queue 中,生产者生产消息并最终投递到 Queue 中,消费者可以从 Queue 中获取消息并消费;多个消费者可以订阅同一个 Queue,这时 Queue 中的消息会被平均分摊给多个消费者进行处理,而不是每个消费者都收到所有的消息并处理。

  • Binding(绑定):是 Exchange(交换器)将消息路由给 Queue 所需遵循的规则。

  • Routing Key(路由键):消息发送给 Exchange(交换器)时,消息将拥有一个路由键(默认为空), Exchange(交换器)根据这个路由键将消息发送到匹配的队列中。

  • Binding Key(绑定键):指定当前 Exchange(交换器)下,什么样的 Routing Key(路由键)会被下派到当前绑定的 Queue 中。

  • Virtual host:出于多租户和安全因素设计的,把AMQP 的基本组件划分到一个虚拟的分组中,类似于网络中的namespace 概念。当多个不同的用户使用同一个RabbitMQ server 提供的服务时,可以划分出多个vhost,每个用户在自己的vhost 创建exchange / queue 等。

另外,再说下 Exchange Types(交换器类型)的三种常用类型:

  • Direct:完全匹配,消息路由到那些 Routing Key 与 Binding Key 完全匹配的 Queue 中。比如 Routing Key 为cleint-key,只会转发cleint-key,不会转发cleint-key.1,也不会转发cleint-key.1.2。

  • Topic:模式匹配,Exchange 会把消息发送到一个或者多个满足通配符规则的 routing-key 的 Queue。其中*表号匹配一个 word,#匹配多个 word 和路径,路径之间通过.隔开。如满足a.*.c的 routing-key 有a.hello.c;满足#.hello的 routing-key 有a.b.c.helo。

  • Fanout:忽略匹配,把所有发送到该 Exchange 的消息路由到所有与它绑定 的Queue 中。

RabbitMQ 工作模式

简单模式、work queues、Publish/Subscribe 发布与订阅模式、Routing 路由模式、Topic 主题模式、RPC远程调用模式(不太算MQ)。

这边整合大概写下简单,发布订阅和在主题模式,配置安装这里暂时不讲,先上代码:

创建springboot 的项目,选址需要的依赖

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-amqp</artifactId>
</dependency>
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-test</artifactId>
    <scope>test</scope>
</dependency>
<dependency>
    <groupId>org.springframework.amqp</groupId>
    <artifactId>spring-rabbit-test</artifactId>
    <scope>test</scope>
</dependency>
<dependency>
    <groupId>junit</groupId>
    <artifactId>junit</artifactId>
    <version>4.13.1</version>
    <scope>test</scope>
</dependency>

在application.yml 文件中进行相应配置

spring:
  rabbitmq:
    addresses: 127.0.0.1 # mq 地址
    username: guest # 默认为guest
    password: guest # 默认为guest
    virtual-host: /mohen # 默认为/

简单模式

生产端配置类:用于队列 ,交换机的定义及绑定关系

@Configuration
public class RabbitConfig {

    public static final String SIMPLE_QUEUE_NAME = "simple_queue";

    @Bean
    public Queue simpleQueue() {
        /**
         durable(): 是否持久化,当mq 重启之后还在
         exclusive(): 是否独占:只能有一个消费者监听这个队列,当Connection 关闭时,是否删除队列
         autoDelete(): 是否自动删除,当没有Consumer 监听时,自动删除
         withArgument(): 参数
         */
        return QueueBuilder.durable(SIMPLE_QUEUE_NAME).build();
    }
}

生产端测试类:

@SpringBootTest
@RunWith(SpringRunner.class)
class SbmqProducerApplicationTests {

    @Autowired
    private RabbitTemplate rabbitTemplate;

    @Test
    void simpleQueue() {
        // 简单模式下,路由key 默认为队列名称,交换机为默认
        rabbitTemplate.convertAndSend(RabbitConfig.SIMPLE_QUEUE_NAME, "hello world");
        /**源码
            public void convertAndSend(String routingKey, Object object) throws AmqpException {
                this.convertAndSend(this.exchange, routingKey, object, (CorrelationData)null);
            }
         */
    }
}

消费端监听类:

@Component
public class SimpleListener {

    @RabbitListener(queues = "simple_queue")
    public void simpleListener(Message message) {
        System.out.println(message);
        System.out.println(new String(message.getBody()));
    }
}

发布订阅

依赖和配置不用变,主要是队列这块新增了交换机组件,我们来看下代码:

@Configuration
public class RabbitConfig {

    public static final String FANOUT_EXCHANGE_NAME = "fanout_exchange";

    public static final String PS_QUEUE_NAME = "ps_queue";
    public static final String PS_QUEUE_NAME02 = "ps_queue02";

    @Bean("fanout_exchange")
    public Exchange fanoutExchange() {
        /**
         * 交换机类型:
         * DIRECT: 定向
         * FANOUT: 扇形(广播):发送消费给每一个与之绑定的队列
         * TOPIC: 通配符
         * HEADERS: 参数
         *
         * durable: 是否持久化
         * autoDelete: 自动删除
         * internal: 内部使用
         * arguments: 参数
         */
        return ExchangeBuilder.fanoutExchange(FANOUT_EXCHANGE_NAME).durable(true).build();
    }

    @Bean("ps_queue")
    public Queue psQueue01() {
        return QueueBuilder.durable(PS_QUEUE_NAME).build();
    }
    @Bean("ps_queue02")
    public Queue psQueue02() {
        return QueueBuilder.durable(PS_QUEUE_NAME02).build();
    }

    @Bean
    public Binding bindQueueExchange01(@Qualifier("ps_queue") Queue queue, @Qualifier("fanout_exchange") Exchange exchange) {
        // 如果交换机类型为fanout, routingKey(路由键,绑定规则) 设置为""
        return BindingBuilder.bind(queue).to(exchange).with("").noargs();
    }
    @Bean
    public Binding bindQueueExchange02(@Qualifier("ps_queue02") Queue queue, @Qualifier("fanout_exchange") Exchange exchange) {
        // 如果交换机类型为fanout, routingKey(路由键,绑定规则) 设置为""
        return BindingBuilder.bind(queue).to(exchange).with("").noargs();
    }
}

生产端测试类:

@SpringBootTest
@RunWith(SpringRunner.class)
class SbmqProducerApplicationTests {

    @Autowired
    private RabbitTemplate rabbitTemplate;

    @Test
    void psQueue() {
        rabbitTemplate.convertAndSend(RabbitConfig.FANOUT_EXCHANGE_NAME, "", "ps");
    }
}

消费端监听类:

@Component
public class PsListener {

    @RabbitListener(queues = "ps_queue")
    public void psListener(Message message) {
        System.out.println("队列1: "+ new String(message.getBody()));
    }

    @RabbitListener(queues = "ps_queue02")
    public void ps02Listener(Message message) {
        System.out.println("队列2: "+ new String(message.getBody()));
    }
}

主题模式

直接上代码:

@Configuration
public class RabbitConfig {

    public static final String TOPIC_EXCHANGE_NAME = "topic_exchange";

    public static final String TOPIC_QUEUE_NAME = "topic_queue";
    public static final String TOPIC_QUEUE_NAME02 = "topic_queue02";

    @Bean("topic_exchange")
    public Exchange topicExchange() {
        return ExchangeBuilder.topicExchange(TOPIC_EXCHANGE_NAME).durable(true).build();
    }

    @Bean("topic_queue")
    public Queue topicQueue() {
        return QueueBuilder.durable(TOPIC_QUEUE_NAME).build();
    }
    @Bean("topic_queue02")
    public Queue topicQueue02() {
        return QueueBuilder.durable(TOPIC_QUEUE_NAME02).build();
    }

    @Bean
    public Binding bindQueueExchange05(@Qualifier("topic_queue") Queue queue, @Qualifier("topic_exchange") Exchange exchange) {
        //*: 代表单个单词;  #: 代表单个或多个单词
        return BindingBuilder.bind(queue).to(exchange).with("order.*").noargs();
    }
    @Bean
    public Binding bindQueueExchange06(@Qualifier("topic_queue02") Queue queue, @Qualifier("topic_exchange") Exchange exchange) {
        return BindingBuilder.bind(queue).to(exchange).with("#.error").noargs();
    }
}

生产端测试类:

@SpringBootTest
@RunWith(SpringRunner.class)
class SbmqProducerApplicationTests {

    @Autowired
    private RabbitTemplate rabbitTemplate;

    @Test
    void topicQueue() {
        rabbitTemplate.convertAndSend(RabbitConfig.TOPIC_EXCHANGE_NAME, "log.info", "topic_info");// 队列1
        rabbitTemplate.convertAndSend(RabbitConfig.TOPIC_EXCHANGE_NAME, "log.error", "topic_error");// 队列2
        rabbitTemplate.convertAndSend(RabbitConfig.TOPIC_EXCHANGE_NAME, "order.error", "topic_order_error");// 队列2
    }
}

消费端监听类:

@Component
public class TopicListener {

    @RabbitListener(queues = "topic_queue")
    public void topicListener(Message message) {
        System.out.println("队列1: "+ new String(message.getBody()));
    }

    @RabbitListener(queues = "topic_queue02")
    public void topic02Listener(Message message) {
        System.out.println("队列2: "+ new String(message.getBody()));
    }
}

以上就是springboot 整合rabbitmq 的入门介绍,下一篇再深入介绍下rabbitmq 的高级用法。

RabbitMQ 高级特性icon-default.png?t=M4ADhttp://t.csdn.cn/69fqy

 如有问题,欢迎道友指正,感谢!

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值