Bootstrap

基于AMQP的RabbitMQ整合SpringBoot

1. AMQP(Advanced Message Queue Protocol)

AMQP(Advanced Message Queue Protocol)高级消息队列协议。协议无非就是需要遵循一定的数据规范,是在线路层上建立的,也就是应用层协议的一个开放标准,定义了网络交互的数据格式,而不是API接口(例如JMS),这使得AMQP和JMS从本质上的区别。它天然就是跨平台的,就像SMTP、HTTP 等协议样,只要开发者按照规范的格式发送数据,任何平台都可以通过AMQP进行消息交互。像目前流行的 StormMQ、RabbitMQ 等都实现了 AMQP。

2.RabbitMQ简介:

RabbitMQ是一个实现了 AMQP 的开源消息中间件,使用高性能的 Erlang 编写。 RabbitMQ具有可靠性、支持多种协议、高可用、支持消息集群以及多语言客户端等特点,在分布式系统中存储转发消息,具有不错的性能表现。

2.1 RabbitMQ安装:

参考链接:https://www.helloweba.net/server/624.html

由于RabbitMQ使用Erlang编写,安装 RabbitMQ 之前要安装 Erlang,需要先到RabbitMQ官网看下版本对应关系。可以分别在Erlang的GithubRabbitMQ官网下载对应的版本的rpm包。下载步骤如下:

# 下载erlang
wget https://github.com/rabbitmq/erlang-rpm/releases/download/v23.2.4/erlang-23.2.4-1.el7.x86_64.rpm

# 下载rabbitmq
wget https://github.com/rabbitmq/rabbitmq-server/releases/download/v3.8.11/rabbitmq-server-3.8.11-1.el7.noarch.rpm

# 安装socat依赖,安装Erlang时需要
yum install -y socat

# 安装Erlang
rpm -ivh erlang-22.2-1.el7.x86_64.rpm

# 查看Erlang安装版本
erl -version

# 6.安装RabbitMQ
rpm -ivh rabbitmq-server-3.8.1-1.el7.noarch.rpm

# 启动rabbitmq
systemctl start rabbitmq-server 或 service rabbitmq-server start

# 查看状态
rabbitmqctl status

# 启用网页版后台管理插件
rabbitmq-plugins enable rabbitmq_management

# 重启rabbitmq
systemctl restart rabbitmq-server 或 service rabbitmq-server restart

#添加一个用户名为admin,密码为admin的用户
rabbitmqctl add_user admin admin

#设置admin用户的角色为管理员
rabbitmqctl set_user_tags admin administrator

#配置admin用户可以远程登录
rabbitmqctl set_permissions -p / admin ".*" ".*" ".*"

rabbitmq其他常用操作:

# 可以设置rabbitmq开机自启
systemctl enable rabbitmq-server

# 关闭服务
rabbitmqctl stop

插件管理:

#插件列表: 
rabbitmq-plugins list 

#启动插件: 
rabbitmq-plugins enable XXX   (XXX为插件名)

#停用插件: 
rabbitmq-plugins disable XXX

RabbitMQ启动成功后,默认有一个guest用户,但是该用户只能在本地登录,无法远程登录,因此本案例中添加了一个新的用户sang,也具有管理员身份,同时可以远程登录。当RabbitMQ启动成功后,在物理机浏览器上输入虚拟机地址: http://localhost:15672,15672是RabbitMQ的默认端口,如下:

登录admin如下:

2.2 整合SpringBoot

Spring Boot为AMQP提供了自动化配置依赖spring-boot-starter-amqp ,因此首先创建Spring Boot项目并添加该依赖,如下:

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

配置RabbitMQ连接信息如下:

spring:
  rabbitmq:
    # rabbitmq地址、端口、用户名、密码
    host: localhost
    # 注意:该端口不是15672
    port: 5672
    username: admin
    password: admin

由于所有的消息生产者提交的消息都会交由Exchange进行再分配,Exchange会根据不同的策略将消息分发到不同的Queue 中。RabbitMQ中一共提供了4种不同的Exchange策略,分别是 Direct、Fanout、Topic以及Header,这4种不同的策略中前3种的使用频率较高,第4种的使用频率较低,下面分别对这4种不同的ExchangeType予以介绍。

(1) Direct直接交换

Direct的策略是将消息队列绑定到DirectExchange上,即将消息转发到与该条消息key相同的Queue中,例如该队列的名称为"queue-name",监听的key为"queue-name"的消息会被该消息队列接收。RabbitMQDirectConfig如下:

@Configuration
public class RabbitMQDirectConfig {

    public final static String DIRECTNAME = "weiyh-direct";

    @Bean
    Queue queue() {
        // 队列名称,用于接收者监听
        return new Queue("queue-name");
    }

    @Bean
    DirectExchange directExchange() {
        // 消息名称,重启后是否有效,长期未用是否删除
        return new DirectExchange(DIRECTNAME, true, false);
    }

    @Bean
    Binding binding() {
        // 将队列以direct的方式进行绑定
        return BindingBuilder.bind(queue()).to(directExchange()).with("direct");
    }
}

消息消费者DirectReceiver如下:

@Component
public class DirectReceiver {
    // 监听的队列名称
    @RabbitListener(queues = "queue-name")
    public void handler(String msg) {
        System.out.println("DirectReceiver:" + msg);
    }
}

在测试类中注入RabbitTemplat 对象来进行消息发送,如下:

@SpringBootTest
class RabbitmqApplicationTests {
    @Autowired
    RabbitTemplate rabbitTemplate;

    @Test
    void contextLoads() {
        System.out.println("nihao");
        rabbitTemplate.convertAndSend("queue-name", "===========>hello direct");
    }
}

记得启动RabbitMQ,然后在启动测试类,成功输出如下:

RabbitMQ控制台可以看到:

(2) Fanout扇形交换

Fanout的策略是把所有到达FanoutExchange的消息转发给所有与它绑定的Queue,这个过程中key不起任何作用。创建两个Queue,将两个Queue绑定到FanoutExchange中,如下:

@Configuration
public class RabbitMQFanoutConfig {

    public final static String FANOUTNAME = "weiyh-fanout";

    @Bean
    FanoutExchange fanoutExchange() {
        return new FanoutExchange(FANOUTNAME, true, false);
    }

    @Bean
    Queue queueOne() {
        return new Queue("queue-one");
    }

    @Bean
    Queue queueTwo() {
        return new Queue("queue-two");
    }

    @Bean
    Binding bindingOne() {
        // 将队列queueOne绑定到fanoutExchange中
        return BindingBuilder.bind(queueOne()).to(fanoutExchange());
    }

    @Bean
    Binding bindingTwo() {
        // 将队列queueTwo绑定到fanoutExchange中
        return BindingBuilder.bind(queueTwo()).to(fanoutExchange());
    }
}

创建两个消费者如下:

@Component
public class FanoutReceiver {

    @RabbitListener(queues = "queue-one")
    public void handlerOne(String msg) {
        System.out.println("FanoutReceiver.handlerOne:" + msg);
    }

    @RabbitListener(queues = "queue-two")
    public void handlerTwo(String msg) {
        System.out.println("FanoutReceiver.handlerTwo:" + msg);
    }
}

测试:

@SpringBootTest
class RabbitmqApplicationTests {
    @Autowired
    RabbitTemplate rabbitTemplate;

    @Test
    void contextLoads() {
        // exchange策略名称,key为null,发送内容
        rabbitTemplate.convertAndSend(RabbitMQFanoutConfig.FANOUTNAME, null,
        	"========>hello fanout");
    }
}

注意:这里发送消息时不需要key,指定exchange即可,key为null。

一条消息发出后,所有与fanout绑定的Queue都接收到了消息,输出如下:

(3) Topic主题交换
  • 直连交换机的routing_key方法非常简单,如果希望将一条消息发送给多个队列,那么这个交换机需要绑定非常多的routing_key,这样的话消息的管理就会非常的困难。
  • 所以RabbitMQ提供了一种主题交换机,发送到主题交换机上的消息需要携带制定规则的routing_key,主题交换机会根据这个规则将数据发送到对应的队列上。
(4) Header首部交换

首部交换机是忽略routing_key的一种路由方式。路由器和交换机路由的规则是通过Headers信息来交换的,这个有点像HTTP请求中的请求头。将一个交换机声明成首部交换机,绑定一个队列的时候,定义一个Hash的数据结构,消息发送的时候,会携带一组hash数据结构的信息,当Hash内容匹配上的时候,消息就会被写入队列。

;