SpringBoot下RabbitMQ的简单使用(一对一)

1. 简介

在这之前先简单介绍一下什么是AMQP和消息中间件:

1.1 AMQP

AMQP,即Advanced message Queuing Protocol,高级消息队列协议,是应用层协议的一个开放标准,为面向消息的中间件设计。消息中间件主要用于组件之间的解耦,消息的发送者无需知道消息使用者的存在,反之亦然。
AMQP的主要特征是面向消息、队列、路由(包括点对点和发布/订阅)、可靠性、安全。

1.2 消息中间件

消息(Message)是指在应用间传送的数据,消息可以很简单,如:文本字符串、JSON等,也可以很复杂,如:内嵌对象等。

消息队列中间件(Message Queue Middleware,简称MQ)是指利用高效可靠的消息传递机制进行与平台无关的数据交流,并基于数据通信来进行分布式系统的集成,通过提供消息传递和消息排队模型,可以在分布式环境下扩展进程间的通信。

消息队列中间件,也可成为消息队列或者消息中间件,它一般有二种传递模式:

1.点对点(P2P,Point-to-Point)模式
点对点模式是基于队列的,消息生产者发送消息到队列,消息消费者从队列中接收消息,队列的存在使得消息的异步传输成为可能。

2.发布/订阅(Pub/Sub)模式
发布订阅模式定义了如何向一个内容节点发布和订阅消息,这个内容节点成为主题,主题可以认为是消息传递的中介,消息发布者将消息发布到某个主题,而消息的订阅者则从主题中订阅消息。主题使得消息的订阅和消息的发布者互相保持独立,不需要进行接触即可保证消息的传递,发布/订阅模式在消息的一对多广播时采用。

1.2.1 消息中间件的作用

消息中间件适用于需要可靠的数据传递的分布式环境,采用消息中间件的系统中,不同的对象之间通过传递消息来激活对方的事件,以完成相应的操作。发送者将消息发送给消息服务器,消息服务器将消息存放在若干个队列中,在合适的时候再将消息转发给接受者。消息中间件能在不同的平台上通信,它昌北用来屏蔽各种平台及协议之间的特性,实现应用程序之间的协同,其优点在与能够在客户和服务器之间提供同步和异步的连接,并且在任何时刻都可以将消息进行传送或者存储转发,这也是它比远程过程调用更进步的原因。

1.解耦:
在项目启动之初来预测将来会碰到什么需求是极其困难的。消息中间件在处理过程中间插入了一个隐含的、基于数据的接口层,两边的处理过程都要实现这一接口,这允许你独立地扩展或修改两边的处理过程,只要确保他们遵守同样的接口约束即可。

2.冗余(存储):
有些情况下,处理数据的过程会失败,消息中间件可以把数据进行持久化直到他们已经被完全处理了,通过这一方式规避了数据丢失的风险,在把一个消息从消息中间件中删除之前,需要你的处理系统明确的指出该消息已经被处理完成,从而确保你的数据被安全地保存直到你使用完毕。

3.扩展性:
因为消息中间件解耦了应用程序的处理过程,所以提高消息入队和处理的效率是很容易的,只要另外增加处理过程即可,不需要改变代码,也不需要调节参数。

4.削峰:
在访问量剧增的情况下,应用仍然需要继续发挥作用,但是这样的突发流量并不常见,如果以能处理这类峰值为标准而投入资源,无疑是巨大的浪费,使用消息中间件能够使关键组件支撑突发访问压力,不会因为突发的超负荷请求而完全崩溃。

5.可恢复性:
当系统一部分组件失效时,不会影响到整个系统,消息中间件降低了进程间的耦合度,所以即使一个处理消息的进程挂掉,加入消息中间件中的消息仍然可以在系统恢复后进行处理。

6.顺序保证:
在大多数使用场景下,数据处理的顺序很重要,大部分消息中间件支持一定程度上的顺序性。

7.缓冲:
在任何重要的系统中,都会存在需要不同处理时间的元素。消息中间件通过一个缓冲层来帮助任务最高效率的执行,写入消息中间件的处理会尽可能快速,该缓冲层有助于控制和优化数据流经过系统的速度。

8.异步通信:
在很多时候应用不想也不需要立即处理消息,消息中间件提供了异步处理机制,允许应用把一些消息放入消息中间件中,但并不立即处理它,在之后需要的时候再慢慢处理。

9.特点:
可靠性:使用一些机制来保证可靠性,如持久化、传输确认及发布确认等;
灵活的路由:在消息进入队列之前,通过交换器来路由消息。对于典型的路由功能,RabbitMQ已经提供了一些内置的交换器来实现;
扩展性:多个RabbitMQ节点可以组成一个集群,也可以根据实际业务情况动态地扩展集群中节点;
高可用性:队列可以在集群中的机器上设置镜像,使得在部分 节点出现问题的情况下队列仍然可用;
多种协议:RabbitMQ除了原生支持AMQP协议,还支持 STOMP、MQTT等多种消息中间件协议;
多语言客户端:RabbitMQ几乎支持所有常用语言,比如Java、 Python、Ruby、PHP、C#、JavaScript等;
管理界面:RabbitMQ提供了一个易用的用户界面,使得用户 可以监控和管理消息、集群中的节点等;
插件机制:RabbitMQ提供了许多插件,以实现从多方面进行 扩展,当然也可以编写自己的插件。

1.3 RabbitMQ简介

RabbitMQ是一个开源的AMQP实现,服务器端用Erlang语言编写,支持多种客户端,如:Python、Ruby、.NET、Java、JMS、C、PHP、ActionScript、XMPP、STOMP等,支持AJAX。用于在分布式系统中存储转发消息,在易用性、扩展性、高可用性等方面表现不俗。
MQ全称为Message Queue, 消息队列(MQ)是一种应用程序对应用程序的通信方法。应用程序通过读写出入队列的消息(针对应用程序的数据)来通信,而无需专用连接来链接它们。消息传递指的是程序之间通过在消息中发送数据进行通信,而不是通过直接调用彼此来通信,直接调用通常是用于诸如远程过程调用的技术。排队指的是应用程序通过 队列来通信。队列的使用除去了接收和发送应用程序同时执行的要求。其中较为成熟的MQ产品有IBM WEBSPHERE MQ等等。

RabbitMQ是目前非常热门的一款消息中间件,很多行业都在使用这个消息中间件,RabbitMQ凭借其高可靠、易扩展、高可用及丰富的功能特性收到很多人的青睐。

1.3.1 RabbitMQ系统架构

在这里插入图片描述
图来源于网络 侵删

说明:
Producer、Consumer
producer指的是消息生产者,consumer消息的消费者。
Queue
消息队列,提供了FIFO(先进先出)的处理机制,具有缓存消息的能力。rabbitmq中,队列消息可以设置为持久化,临时或者自动删除。

  1. 设置为持久化的队列,queue中的消息会在server本地硬盘存储一份,防止系统crash,数据丢失
  2. 设置为临时队列,queue中的数据在系统重启之后就会丢失
  3. 设置为自动删除的队列,当不存在用户连接到server,队列中的数据会被自动删除
    Exchange
    Exchange类似于数据通信网络中的交换机,提供消息路由策略。rabbitmq中,producer不是通过信道直接将消息发送给queue,而是先发送给Exchange。一个Exchange可以和多个Queue进行绑定,producer在传递消息的时候,会传递一个ROUTING_KEY,Exchange会根据这个ROUTING_KEY按照特定的路由算法,将消息路由给指定的queue。和Queue一样,Exchange也可设置为持久化,临时或者自动删除。

Exchange有4种类型:direct(默认),fanout, topic, 和headers,不同类型的Exchange转发消息的策略有所区别:

  1. Direct
    直接交换器,工作方式类似于单播,Exchange会将消息发送完全匹配ROUTING_KEY的Queue

  2. fanout
    广播是式交换器,不管消息的ROUTING_KEY设置为什么,Exchange都会将消息转发给所有绑定的Queue。

  3. topic
    主题交换器,工作方式类似于组播,Exchange会将消息转发和ROUTING_KEY匹配模式相同的所有队列,比如,ROUTING_KEY为user.stock的Message会转发给绑定匹配模式为 * .stock,user.stock, * . * 和#.user.stock.#的队列。( * 表是匹配一个任意词组,#表示匹配0个或多个词组)

  4. headers
    消息体的header匹配(ignore)
    Binding
    所谓绑定就是将一个特定的 Exchange 和一个特定的 Queue 绑定起来。Exchange 和Queue的绑定可以是多对多的关系。
    virtual host
    在rabbitmq server上可以创建多个虚拟的message broker,又叫做virtual hosts (vhosts)。每一个vhost本质上是一个mini-rabbitmq server,分别管理各自的exchange,和bindings。vhost相当于物理的server,可以为不同app提供边界隔离,使得应用安全的运行在不同的vhost实例上,相互之间不会干扰。producer和consumer连接rabbit server需要指定一个vhost。

1.3.2 通信过程

假设P1和C1注册了相同的Broker,Exchange和Queue。P1发送的消息最终会被C1消费。基本的通信流程大概如下所示:

P1生产消息,发送给服务器端的Exchange
Exchange收到消息,根据ROUTINKEY,将消息转发给匹配的Queue1
Queue1收到消息,将消息发送给订阅者C1
C1收到消息,发送ACK给队列确认收到消息
Queue1收到ACK,删除队列中缓存的此条消息
Consumer收到消息时需要显式的向rabbit broker发送basic.ack消息或者consumer订阅消息时设置auto_ack参数为true。在通信过程中,队列对ACK的处理有以下几种情况:

如果consumer接收了消息,发送ack,rabbitmq会删除队列中这个消息,发送另一条消息给consumer。
如果cosumer接受了消息, 但在发送ack之前断开连接,rabbitmq会认为这条消息没有被deliver,在consumer在次连接的时候,这条消息会被redeliver。
如果consumer接受了消息,但是程序中有bug,忘记了ack,rabbitmq不会重复发送消息。
rabbitmq2.0.0和之后的版本支持consumer reject某条(类)消息,可以通过设置requeue参数中的reject为true达到目地,那么rabbitmq将会把消息发送给下一个注册的consumer。
参考文章:RabbitMQ基本原理

2. SpringBoot集成RabbitMQ

2.1 新建工程,添加相关依赖

主要添加spring-boot-starter-amqp的支持

    <dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter</artifactId>
        </dependency>

        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-amqp</artifactId>
        </dependency>
        <dependency>
            <groupId>junit</groupId>
            <artifactId>junit</artifactId>
            <version>4.12</version>
        </dependency>
    </dependencies>

2.2 修改配置文件application.properties

spring.application.name=spirng-boot-rabbitmq

spring.rabbitmq.host=127.0.0.1
spring.rabbitmq.port=5672
spring.rabbitmq.username=guest
spring.rabbitmq.password=guest

2.3 队列配置

package com.eknaij.springbootrabbitmq.config;

import org.springframework.amqp.core.Queue;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

@Configuration
public class RabbitConfig {
    @Bean
    public Queue Queue() {
        return new Queue("hello");
    }
}

2.4 发送者

package com.eknaij.springbootrabbitmq;

import org.springframework.amqp.core.AmqpTemplate;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;

import java.util.Date;

@Component
public class Sender {
    @Autowired
    private AmqpTemplate rabbitTemplate = null;

    public void send() {
        String context = "hello " + new Date();
        System.out.println("Sender : " + context);
        this.rabbitTemplate.convertAndSend("hello", context);
    }
}


注意@Component这个注解

2.5 接受者

package com.eknaij.springbootrabbitmq;

import org.springframework.amqp.rabbit.annotation.RabbitHandler;
import org.springframework.amqp.rabbit.annotation.RabbitListener;
import org.springframework.stereotype.Component;

@Component
@RabbitListener(queues = "hello")
public class Receiver {
    @RabbitHandler
    public void process(String hello) {
        System.out.println("Receiver  : " + hello);
    }
}

注意,发送者和接收者的queue name必须一致,不然不能接收

2.6 单元测试

在test目录下的单元测试类中添加如下内容:
在这里插入图片描述
就是这个类下面添加,这是springboot自动生成的单元测试

package com.eknaij.springbootrabbitmq;

import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.test.context.junit4.SpringRunner;

@RunWith(SpringRunner.class)
@SpringBootTest
public class SpringbootrabbitmqApplicationTests {

    @Test
    public void contextLoads() {

    }
    @Autowired
    private Sender helloSender;
    @Test
    public void hello() throws Exception {
        helloSender.send();
    }

}

运行单元测试就可以看到效果了:
在这里插入图片描述

还有一对多(一个发送端多个接收端)、多对多(多个发送端多个接收端)等内容大家自行测试吧,在这个代码的基础上进行修改就好了。
源码地址

  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值