RabbitMQ

【RabbitMQ】

主要内容

  1. AMQP 简介
  2. RabbitMQ 简介
  3. RabbitMQ 原理
  4. Erlang 安装
  5. 安装 RabbitMQ
  6. RabbitMQ 账户管理
  7. 交换器
    学习目标

在这里插入图片描述

一、 AMQP 简介

1 AMQP 是什么?

AMQP(Advanced Message Queuing Protocol,高级消息队列协议)是进程之间传递异步消息的网络协议。

2 AMQP 工作过程

发布者(Publisher)发布消息(Message),经过交换机(Exchange),交换机根据路由规则将收到消息分发给交换机绑定的队列(Queue),最后 AMQP 代理会将消息投递给订阅了此队列的消费者,或者消费者按照需求自行获取。

在这里插入图片描述

3 队列

队列是数据结构中概念。数据存储在一个队列中,数据是有顺序的,先进的先出,后进后出。其中一侧负责进数据,另一次负责出数据。
MQ(消息队列)很多功能都是基于此队列结构实现的

在这里插入图片描述

二、 RabbitMQ 简介

1 RabbitMQ 介绍

RabbitMQ 是由 Erlang 语言编写的基于 AMQP 的消息中间件。而消息中间件作为分布式系统重要组件之一,可以解决应用耦合,异步消息,流量削峰等问题。

1.1 解决应用耦合
1.1.1 不使用 MQ 时

在这里插入图片描述

1.1.2 使用 MQ 解决耦合

在这里插入图片描述

2 RabbitMQ 适用场景

同步转异步,并发并行转并发串行

排队算法、秒杀活动、消息分发、异步处理、数据同步、处理耗时任务、流量销峰等。

三、 RabbitMQ 原理

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

四、 Erlang 安装

RabbitMQ 是使用 Erlang 语言编写的,所以需要先配置 Erlang

1 修改主机名

RabbitMQ 是通过主机名进行访问的,必须指定能访问的主机名。

vim /etc/sysconfig/network

在这里插入图片描述

在这里插入图片描述

vim /etc/hosts
新添加了一行,前面为服务器 ip,空格后面添加计算机主机名

IP地址和主机名之间要有一个以上的空格

在这里插入图片描述

在这里插入图片描述

2 安装依赖

yum -y install make gcc gcc-c++ kernel-devel m4 ncurses-devel openssl-devel unixODBC unixODBC-devel

在这里插入图片描述

在这里插入图片描述

BUG:

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

换源内容如下:

# CentOS-Base.repo
# 
# The mirror system uses the connecting IP address of the client and the
# update status of each mirror to pick mirrors that are updated to and
# geographically close to the client.  You should use this for CentOS updates
# unless you are manually picking other mirrors.
#
# If the mirrorlist= does not work for you, as a fall back you can try the
# remarked out baseurl= line instead.
#
#

[BaseOS]
name=CentOS-Base.repo
#
# The mirror system uses the connecting IP address of the client and the
# update status of each mirror to pick mirrors that are updated to and
# geographically close to the client.  You should use this for CentOS updates
# unless you are manually picking other mirrors.
#
# If the mirrorlist= does not work for you, as a fall back you can try the
# remarked out baseurl= line instead.
#
#



[BaseOS]
name=CentOS-$releasever - Base
baseurl=https://mirrors.tuna.tsinghua.edu.cn/centos/$releasever/BaseOS/$basearch/os/
#mirrorlist=http://mirrorlist.centos.org/?release=$releasever&arch=$basearch&repo=BaseOS&infra=$infra
enabled=1
gpgcheck=1
gpgkey=file:///etc/pki/rpm-gpg/RPM-GPG-KEY-centosofficial

[AppStream]
name=CentOS-$releasever - AppStream
baseurl=https://mirrors.tuna.tsinghua.edu.cn/centos/$releasever/AppStream/$basearch/os/
#mirrorlist=http://mirrorlist.centos.org/?release=$releasever&arch=$basearch&repo=AppStream&infra=$infra
enabled=1
gpgcheck=1
gpgkey=file:///etc/pki/rpm-gpg/RPM-GPG-KEY-centosofficial

[PowerTools]
name=CentOS-$releasever - PowerTools
baseurl=https://mirrors.tuna.tsinghua.edu.cn/centos/$releasever/PowerTools/$basearch/os/
#mirrorlist=http://mirrorlist.centos.org/?release=$releasever&arch=$basearch&repo=PowerTools&infra=$infra
enabled=0
gpgcheck=1
gpgkey=file:///etc/pki/rpm-gpg/RPM-GPG-KEY-centosofficial


#additional packages that may be useful
[extras]
name=CentOS-$releasever - Extras
baseurl=https://mirrors.tuna.tsinghua.edu.cn/centos/$releasever/extras/$basearch/os/
#mirrorlist=http://mirrorlist.centos.org/?release=$releasever&arch=$basearch&repo=extras
enabled=1
gpgcheck=1
gpgkey=file:///etc/pki/rpm-gpg/RPM-GPG-KEY-centosofficial



#additional packages that extend functionality of existing packages
[centosplus]
name=CentOS-$releasever - Plus
baseurl=https://mirrors.tuna.tsinghua.edu.cn/centos/$releasever/centosplus/$basearch/os/
#mirrorlist=http://mirrorlist.centos.org/?release=$releasever&arch=$basearch&repo=centosplus
gpgcheck=1
enabled=0
gpgkey=file:///etc/pki/rpm-gpg/RPM-GPG-KEY-centosofficial
mirrorlist=http://mirrorlist.centos.org/?release=$releasever&arch=$basearch&repo=BaseOS&infra=$infra
#baseurl=http://mirror.centos.org/$contentdir/$releasever/BaseOS/$basearch/os/
gpgcheck=1
enabled=1
gpgkey=file:///etc/pki/rpm-gpg/RPM-GPG-KEY-centosofficial

3 上传并解压

上传 otp_src_22.0.tar.gz 到/usr/local/tmp 目录中,进入目录并解压。
解压时注意,此压缩包不具有 gzip 属性,解压参数没有 z,只有 xf

cd /usr/local/upload

在这里插入图片描述

tar xf otp_src_22.0.tar.gz

在这里插入图片描述

在这里插入图片描述

4 配置参数

先新建/usr/local/erlang 文件夹,作为安装文件夹

mkdir -p /usr/local/erlang

-p 表示递归

在这里插入图片描述

配置参数

./configure --prefix=/usr/local/erlang --with-ssl --enable-threads --enable-smp-support --enable-kernel-poll --enable-hipe --without-javac

在这里插入图片描述

在这里插入图片描述

5 编译并安装

编译

make

安装

make install

&& :先执行命令,执行完之后立即执行下一个命令

先编译,编译成功之后立即安装

make && make install

在这里插入图片描述

在这里插入图片描述

查看安装结果

在这里插入图片描述

查看安装的版本

在这里插入图片描述

6 修改环境变量

修改/etc/profile 文件

vim /etc/profile
在文件中添加下面代码

export PATH=$PATH:/usr/local/erlang/bin

在这里插入图片描述

在这里插入图片描述

使配置文件生效

在这里插入图片描述

语言环境安装完成

在这里插入图片描述

五、 安装 RabbitMQ

1 上传并解压

上传 rabbitmq-server-generic-unix-3.7.17.tar.xz 到/usr/loca/upload 中

在这里插入图片描述

cd /usr/local/upload

tar -xf rabbitmq-server-generic-unix-3.7.17.tar.xz

在这里插入图片描述

2 复制到 local 下

复制解压文件到/usr/local 下,命名为 rabbitmq

cp -r rabbitmq_server-3.7.17 /usr/local/rabbitmq

在这里插入图片描述

在这里插入图片描述

3 配置环境变量

vim /etc/profile

在文件中添加

export PATH=$PATH:/usr/local/rabbitmq/sbin

解析文件

source /etc/profile

4 开启 web 管理插件

进入 rabbitmq/sbin 目录

cd /usr/local/rabbitmq/sbin

在这里插入图片描述

查看插件列表

./rabbitmq-plugins list

在这里插入图片描述

生效管理插件

./rabbitmq-plugins enable rabbitmq_management

在这里插入图片描述

如果安装的插件生效了会在rabbit安装目录的etc目录下生成一个配置文件 enable_plugins

在这里插入图片描述

5 后台运行

启动 rabbitmq。

./rabbitmq-server -detached

在这里插入图片描述

启动成功信息如下:

在这里插入图片描述

./rabbitmqctl stop_app

停止命令,如果无法停止,使用 kill -9 进程号进行关闭

在这里插入图片描述

BUG:

在这里插入图片描述

客户端代码和tcp协议开放的端口为5672 对http协议开放的端口为15672

在这里插入图片描述

5.1 启动错误解决

如果启动 RabbitMQ 发生下述错误,可以提供环境配置文件,解决。环境配置文件命
名为: rabbitmq-env.conf。所在位置是: $rabbitmq_home/etc/rabbitmq/目录。

内容是: HOSTNAME=主机名称

在这里插入图片描述

在这里插入图片描述

6 查看 web 管理界面

默认可以在安装 rabbitmq 的电脑上通过用户名:guest 密码 guest 进行访问 web 管理界面
端口号:15672(放行端口,或关闭防火墙)
在虚拟机浏览器中输入:

http://localhost:15672

在这里插入图片描述

六、 RabbitMq 账户管理

1 创建账户

语法:./rabbitmqctl add_user username password

cd /usr/local/rabbitmq/sbin

./rabbitmqctl add_user admin admin

在这里插入图片描述

2 给用户授予管理员角色

其中 smallming 为新建用户的用户名

./rabbitmqctl set_user_tags admin administrator

在这里插入图片描述

3 给用户授权

“/” 表示 RabbitMQ 根虚拟主机
admin 表示用户名
“." ".” “.*” 表示完整权限

./rabbitmqctl set_permissions -p “/” admin “." ".” “.*”

在这里插入图片描述

4 登录

使用新建账户和密码在 windows 中访问 rabbitmq 并登录
在浏览器地址栏输入:
http://ip:15672/
用户名:admin
密码:admin

在这里插入图片描述

七、 Exchange 交换器(交换机)

交换器负责接收客户端传递过来的消息,并转发到对应的队列中。在 RabbitMQ 中支持四种交换器

  1. Direct Exchange:直连交换器(默认)

  2. Fanout Exchange:扇形交换器

  3. Topic Exchange:主题交换器 可以实现所有功能

  4. Header Exchange:首部交换器。
    在 RabbitMq 的 Web 管理界面中 Exchanges 选项卡就可以看见这四个交换器。

在这里插入图片描述

1 direct 交换器

点对点,对应唯一一个Queue

在这里插入图片描述

如果有多个consumer消费同一个Queue默认使用轮询机制(公平调度)让所有的consumer依次消费消息,轮询轮的不是位置顺序而是下一个空闲消费者,哪个消费者先执行完则优先执行下一个消息

**direct 交换器是 RabbitMQ 默认交换器。**默认会进行公平调度。所有接受者依次从消息队列中获取值。Publisher 给哪个队列发消息,就一定是给哪个队列发送消息。对交换器绑定的其他队列没有任何影响。
(代码演示)一个队列需要绑定多个消费者
需要使用注解/API:
org.springframework.amqp.core.Queue:队列
AmqpTemplate:操作 RabbitMQ 的接口。负责发送或接收消息
@RabbitListener(queues = “”) 注解某个方法为接收消息方法

1.1 代码实现
1.1.1 新建项目 Publisher

在这里插入图片描述

amqp_rabbit/pm.xml

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>

    <groupId>com.bjsxt</groupId>
    <artifactId>amqp_rabbit</artifactId>
    <packaging>pom</packaging>
    <version>1.0-SNAPSHOT</version>
    <modules>
        <module>amqp_rabbit_consumer</module>
    </modules>

    <dependencyManagement>
        <dependencies>
            <dependency>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-dependencies</artifactId>
                <version>2.2.5.RELEASE</version>
                <scope>import</scope>
                <type>pom</type>
            </dependency>
        </dependencies>
    </dependencyManagement>


    <dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
        </dependency>
        <!--Springboot提供的关于AMQP协议实现的启动器,可以使用AMQP快速访问MQ消息中间件-->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-amqp</artifactId>
        </dependency>
    </dependencies>

</project>

在这里插入图片描述

创建子模块

在这里插入图片描述

在这里插入图片描述

package com.bjsxt.rabbit.consumer;

import org.springframework.amqp.rabbit.annotation.*;
import org.springframework.stereotype.Component;

/**
 * 日志消息消费者,只消费Info日志
 * 日志消息存储在队列 log-info-queue
 * 使用的交换器名称是 log-ex-direct
 * 交换器类型是 direct
 * 队列的路由键是 direct-rk-info
 * <p>
 * 以上名称都是自定义
 *
 * @RabbitListener - 监听注解。可以描述类型和方法
 *  类型 - 当前类型监听某个队列
 *  方法 - 当前方法监听某个队列
 *  属性 -
 *      bindings - @QueueBinding[]类型,代表这个类型或方法监听的队列,交换器,路由键的绑定方式
 *
 * @QueueBinding -
 *      属性 -
 *          value - 绑定的监听队列是什么
 *          exchange - 队列对应的交换器是什么
 *          key - 队列的路由键是什么
 *
 * @Queue : 描述一个队列
 *  属性 -
 *      value/name - 队列名称
 *      autoDelete - 是否自动删除。默认为 "" ,如果队列名称定义,不自动删除,,队列名称不定义,队列为自动删除队列,队列名称随机生成
 *          如果是自动删除,代表所有的consumer关闭后,队列自动删除
 *
 * @Exchange : 描述一个交换器
 *  属性 -
 *      value/name - 交换器名称
 *      type - 交换器的类型,可选 direct/fanout/topic,默认direct
 *      autoDelete - 是否自动删除,默认为"false",不自动删除
 */
	
@RabbitListener(bindings = {
    @QueueBinding(
            value = @Queue(value = "log-info-queue",autoDelete = "false"),
            exchange = @Exchange(value = "log-ex-direct",type = "direct",autoDelete = "false"),
            key = "direct-rk-info"
    )
})
@Component
public class InfoLogConsumer {
    /**
     * 消息消费方法。 当队列 log-info-queue中出现消息立刻消费
     * @param msg 消息内容
     *
     * @RabbitHandler - 配合类型上的RabbitListener注解,标记当前的方法,是一个监听消息队列,消费消息的方法
     *                  如果将 @RabbitListener 注解写到方法上可以不用写 @RabbitHandler 注解
     */
    @RabbitHandler
    public void onMessage(String msg) {
        System.out.println("InfoLogConsumer 消费消息:   " + msg);
    }

}

在这里插入图片描述

https://blog.csdn.net/dark868/article/details/105171660

启动类

在这里插入图片描述

package com.bjsxt;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;

@SpringBootApplication
public class RabbitConsumerApp {
    public static void main(String[] args) {
        SpringApplication.run(RabbitConsumerApp.class, args);
    }
}

配置文件

application.yml

spring:
  rabbitmq:
    host: 192.168.88.101  # RabbitMQ 服务的地址,默认Localhost
    port: 5672   #RabbitMQ 的端口默认 5672
    username: admin   # 用户名
    password: admin   # 访问RabbitMQ的密码
    virtual-host: /   # 访问RabbitMQ的哪一个虚拟主机,默认为  /

添加注解,启动项目

在这里插入图片描述

54654 是客户端连接端口

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

D : 长期持久化,即使Consumer关闭了,这个交换器还存在

在这里插入图片描述

D : 队列创建之后长期保存,不会删除 如果是 AD (AUTO——DELETE)当consumer全部关闭时自动删除

在这里插入图片描述

在这里插入图片描述

新建一个类 日志消费者

package com.bjsxt.rabbit.consumer;

import org.springframework.amqp.rabbit.annotation.Exchange;
import org.springframework.amqp.rabbit.annotation.Queue;
import org.springframework.amqp.rabbit.annotation.QueueBinding;
import org.springframework.amqp.rabbit.annotation.RabbitListener;
import org.springframework.stereotype.Component;

/**
 * 日志消息消费者,只消费error和warn日志
 * error 日志消息存储在队列 log-error-queue
 * warn 日志消息存储在队列 log-warn-queue
 * * 使用的交换器名称是 log-ex-direct
 * * 交换器类型是 direct
 * error 队列的路由键是 direct-rk-error
 * warn 队列的路由键是 direct-rk-warn
 */
@Component  //让spring容器做加载
public class LogConsumers {
    @RabbitListener(bindings = {
            @QueueBinding(
                    value = @Queue(value = "log-error-queue"),
                    exchange = @Exchange(value = "log-ex-direct"),
                    key = "direct-rk-error"
            )
    })
    public void onLogErrorMessage(String msg) {
        System.out.println("错误日志信息:" + msg);
    }

    @RabbitListener(bindings = {
            @QueueBinding(
                    value = @Queue(value = "log-warn-queue",autoDelete = "false"),
                    exchange = @Exchange(value = "log-ex-direct"),
                    key = "direct-rk-warn"
            )
    })
    public void onLogWarnMessage(String msg) {
        System.out.println("警告日志信息:" + msg);
    }
}

重启项目

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

新建一个子模块 Publisher

在这里插入图片描述

在这里插入图片描述

package com.bjsxt.rabbit.sender;

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

/**
 * 发送消息的类型
 * 把消息发送到RabbitMQ中
 * 在spring-boot-starter-amqp中,启动器自动创建初始化一个AMQPTemplate,
 * 作为访问AMQP消息服务器(MQ消息中间件)的客户端对象
 */
@Component //注册到Spring IOC容器中
public class LogMessageSender {

    @Autowired
    private AmqpTemplate amqpTemplate;

    /**
     * 发送消息的方法
     * amqpTemplate.convertAndSend(String exchange,String routingKey,Object message)
     * exchange - 交换器名称
     * routingKey - 路由键
     * message - 要发送的消息内容,就是传递的消息对象的消息体。
     */
    public void sendMessage(String exchange,String routingKey,String message) {
        this.amqpTemplate.convertAndSend(exchange, routingKey, message);
    }
}
package com.bjsxt;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;

@SpringBootApplication
public class RabbitPublisherApp {
    public static void main(String[] args) {
        SpringApplication.run(RabbitPublisherApp.class, args);
    }

}

配置文件:

application.yml

spring:
  rabbitmq:
    host: 192.168.88.101
    port: 5672
    username: admin
    password: admin

在这里插入图片描述

package com.bjsxt.test;

import com.bjsxt.RabbitConsumerApp;
import com.bjsxt.rabbit.sender.LogMessageSender;
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;

import java.util.Random;

/**
 * 消息发送者测试类型
 */
@SpringBootTest(classes = {RabbitPublisherApp.class})
@RunWith(SpringRunner.class)
public class TestPublisher {

    @Autowired
    private LogMessageSender sender;
    private String exchange = "log-ex-direct";
    private String rkInfo = "direct-rk-info";
    private String rkError = "direct-rk-error";
    private String rkWarn = "direct-rk-warn";


    @Test
    public void testSend() {
        Random r = new Random();

        //发送十条消息
        for (int i = 0; i < 10; i++) {
            // rInt%3 - 0:投递消息到 info  1:投递消息到 error 2: 投递消息到 warn
            int rInt = r.nextInt(100);
            if (rInt % 3 == 0) {
                this.sender.sendMessage(exchange, rkInfo, "发送Info日志消息 - index = " + i + "; rInt = " + rInt);
            } else if (rInt % 3 == 1) {
                this.sender.sendMessage(exchange, rkError, "发送Error日志消息 - index = " + i + "; rInt = " + rInt);
            } else if (rInt % 3 == 2) {
                this.sender.sendMessage(exchange, rkWarn, "发送Warn日志消息 - index = " + i + "; rInt = " + rInt);
            }
        }
    }

}

在这里插入图片描述

启动测试类之前得保证Consumer是启动的

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

创建Consumer集群,复制启动类都启动,自动搭建Consumer集群

在这里插入图片描述

队列里有两个Consumer了

在这里插入图片描述

两个connection

在这里插入图片描述

每个Connection对应3个消息队列

在这里插入图片描述

发送十条消息,两个消费者轮询处理

在这里插入图片描述

@Test
public void testSend2Consumers() {
    for (int i = 0; i < 10; i++) {
        this.sender.sendMessage(exchange, rkInfo, "Info消息" + i);
        this.sender.sendMessage(exchange, rkError, "Error消息" + i);
        this.sender.sendMessage(exchange, rkWarn, "Warn消息" + i);
    }
}

在这里插入图片描述

在这里插入图片描述

2 fanout 交换器

在这里插入图片描述

扇形交换器,实际上做的事情就是广播,fanout 会把消息发送给所有的绑定在当前交换器上的队列。 对应 Consumer 依然采用公平调度方式。
(代码演示)一个交换器需要绑定多个队列
需要使用注解/API:
FanoutExchange:fanout 交换器
Binding:绑定交换器和队列
BindingBuilder:Binding 的构建器
amq.fanout:内置 fanout 交换器名称

新建一个模块

在这里插入图片描述

在这里插入图片描述

package com.bjsxt.entity;

import java.io.Serializable;
import java.util.Objects;
//实体类型
public class User implements Serializable {
    // 定义一个序列化唯一ID
    public static final long serialVersionUID = 1L;
    private Long id;
    private String name;
    private int age;

    public User() {
    }

    @Override
    public String toString() {
        return "User{" +
                "id=" + id +
                ", name='" + name + '\'' +
                ", age=" + age +
                '}';
    }

    public Long getId() {
        return id;
    }

    public void setId(Long id) {
        this.id = id;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public int getAge() {
        return age;
    }

    public void setAge(int age) {
        this.age = age;
    }

    @Override
    public boolean equals(Object o) {
        if (this == o) return true;
        if (o == null || getClass() != o.getClass()) return false;
        User user = (User) o;
        return age == user.age &&
                Objects.equals(id, user.id) &&
                Objects.equals(name, user.name);
    }

    @Override
    public int hashCode() {
        return Objects.hash(id, name, age);
    }
}

导入依赖

在这里插入图片描述

package com.bjsxt.rabbit.fanout;

import com.bjsxt.entity.User;
import org.springframework.amqp.rabbit.annotation.Exchange;
import org.springframework.amqp.rabbit.annotation.Queue;
import org.springframework.amqp.rabbit.annotation.QueueBinding;
import org.springframework.amqp.rabbit.annotation.RabbitListener;
import org.springframework.stereotype.Component;

/**
 * 广播交换器,消费者。   不需要路由键
 */
@Component
public class FanoutConsumer {
    /**
     * 消费消息的方法
     * @param user
     *
     * autoDelete = "false"   为默认值可以不写
     */
    @RabbitListener(bindings = {
            @QueueBinding(
                    value = @Queue(value = "queue-user-1",autoDelete = "false"),
                    exchange = @Exchange(value = "ex-fanout",type = "fanout",autoDelete = "false")
            )
    })
    public void onMessage1(User user) {
        System.out.println("onMessage1 run :" + user);
    }
    @RabbitListener(bindings = {
            @QueueBinding(
                    value = @Queue(value = "queue-user-2",autoDelete = "false"),
                    exchange = @Exchange(value = "ex-fanout",type = "fanout")
            )
    })
    public void onMessage2(User user) {
        System.out.println("onMessage2 run :" + user);
    }
}

启动

在这里插入图片描述

在这里插入图片描述

3个direct 2个fanout

在这里插入图片描述

在这里插入图片描述

导入依赖

在这里插入图片描述

创建消息发送者

在这里插入图片描述

package com.bjsxt.rabbit.fanout.sender;

import com.bjsxt.entity.User;
import org.springframework.amqp.core.AmqpTemplate;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;

/**
 * 发送消息类型。消息发送到fanout交换器中
 * 交换器名称是: ex-fanout
 */
@Component
public class UserMessageSender {

    @Autowired
    private AmqpTemplate amqpTemplate;

    /**
     * 发送消息
     * @param user
     * exchange 接收到消息之后会消息投递到所有绑定好的队列,所以不需要路由键
     */
    public void sender(User user) {
        this.amqpTemplate.convertAndSend("ex-fanout","",user);
    }

}

在这里插入图片描述

package com.bjsxt.test;

import com.bjsxt.RabbitPublisherApp;
import com.bjsxt.entity.User;
import com.bjsxt.rabbit.fanout.sender.UserMessageSender;
import com.bjsxt.rabbit.sender.LogMessageSender;
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;

import java.util.Random;

/**
 * 消息发送者测试类型
 */
@SpringBootTest(classes = {RabbitPublisherApp.class})
@RunWith(SpringRunner.class)
public class TestPublisher {

    @Autowired
    private LogMessageSender sender;

    @Autowired
    private UserMessageSender userMessageSender;

    private String exchange = "log-ex-direct";
    private String rkInfo = "direct-rk-info";
    private String rkError = "direct-rk-error";
    private String rkWarn = "direct-rk-warn";

    @Test
    public void testSendUserMessage2Fanout() {
        for (int i = 0; i < 3; i++) {
            User user = new User();
            user.setId((long) i);
            user.setName("姓名 - " + i);
            user.setAge(20 + i);
            this.userMessageSender.sender(user);
        }
    }

    @Test
    public void testSend2Consumers() {
        for (int i = 0; i < 10; i++) {
            this.sender.sendMessage(exchange, rkInfo, "Info消息" + i);
            this.sender.sendMessage(exchange, rkError, "Error消息" + i);
            this.sender.sendMessage(exchange, rkWarn, "Warn消息" + i);
        }
    }

    @Test
    public void testSend() {
        Random r = new Random();

        //发送十条消息
        for (int i = 0; i < 10; i++) {
            // rInt%3 - 0:投递消息到 info  1:投递消息到 error 2: 投递消息到 warn
            int rInt = r.nextInt(100);
            if (rInt % 3 == 0) {
                this.sender.sendMessage(exchange, rkInfo, "发送Info日志消息 - index = " + i + "; rInt = " + rInt);
            } else if (rInt % 3 == 1) {
                this.sender.sendMessage(exchange, rkError, "发送Error日志消息 - index = " + i + "; rInt = " + rInt);
            } else if (rInt % 3 == 2) {
                this.sender.sendMessage(exchange, rkWarn, "发送Warn日志消息 - index = " + i + "; rInt = " + rInt);
            }
        }
    }

}

选中测试 testSendUserMessage2Fanout() 方法

队列是先进先出,两个消费者都是按照 0,1,2的顺序消费消息的,交叉在一起是因为System.out输出语句有缓存

在这里插入图片描述

3 topic 交换器

在这里插入图片描述

在这里插入图片描述

编写消费者

package com.bjsxt.rabbit.topic;

import org.springframework.amqp.rabbit.annotation.Exchange;
import org.springframework.amqp.rabbit.annotation.Queue;
import org.springframework.amqp.rabbit.annotation.QueueBinding;
import org.springframework.amqp.rabbit.annotation.RabbitListener;

/**
 * 主题消息消费者
 */
@Component  //被spring容器加载
public class TopicConsumers {
    /**
     * 短信消息消费者,对应的routingKey 是 user.rk.sms  |   order.rk.sms    |   pay.rk.sms  |  reg.rk.sms等.
     * 分别代表,用户登录短信|订单下订成功通知短信 | 支付成功通知短信 | 注册码通知短信 等。
     */
    @RabbitListener(bindings = {
            @QueueBinding(
                    value = @Queue(value = "queue-sms-topic",autoDelete = "false"),
                    exchange = @Exchange(value = "ex-topic",type = "topic"),
                    key = "*.rk.sms"
            )
    })
    public void onSMSMessage(String message) {
        System.out.println("用户短信内容消息是:" + message);
    }

    /**
     * 路由键包括:user.rk.email | reg.rk.email | reg.rk.email 等
     * @param message
     */
    @RabbitListener(bindings = {
            @QueueBinding(
                    value = @Queue(value = "queue-email-topic",autoDelete = "false"),
                    exchange = @Exchange(value = "ex-topic",type = "topic"),
                    key = "*.rk.email"
            )
    })
    public void onEmailMessage(String message) {
        System.out.println("用户邮件内容消息是:" + message);
    }

    /**
     * 所有的和rk 相关的消息,统一处理消费
     * 包括的路由键有: user.rk.sms | user.rk.email | reg.rk.sms | reg.rk.email 等
     * 不发短信,不发邮件,作为一个日志记录存在
     * @param message
     */
    @RabbitListener(bindings = {
            @QueueBinding(
                    value = @Queue(value = "queue-all-topic",autoDelete = "false"),
                    exchange = @Exchange(value = "ex-topic",type = "topic"),
                    key = "*.rk.*"
            )
    })
    public void onServiceMessage(String message) {
        System.out.println("执行的消息处理逻辑是:" + message);
    }

}

启动测试

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

编写Publisher

在这里插入图片描述

package com.bjsxt.rabbit.topicsender;

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

/**
 * 发送消息到主题交换器
 */
@Component	//让spring容器去加载
public class TopicMessageSender {

    @Autowired
    private AmqpTemplate amqpTemplate;

    /**
     * 发送消息的方法
     * @param exchange
     * @param routingKey
     * @param message
     */
    public void send(String exchange, String routingKey, String message) {
        this.amqpTemplate.convertAndSend(exchange, routingKey, message);
    }

}

在这里插入图片描述

package com.bjsxt.test;

import com.bjsxt.RabbitPublisherApp;
import com.bjsxt.entity.User;
import com.bjsxt.rabbit.fanout.sender.UserMessageSender;
import com.bjsxt.rabbit.sender.LogMessageSender;
import com.bjsxt.rabbit.topicsender.TopicMessageSender;
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;

import java.util.Random;

/**
 * 消息发送者测试类型
 */
@SpringBootTest(classes = {RabbitPublisherApp.class})
@RunWith(SpringRunner.class)
public class TestPublisher {

    @Autowired
    private LogMessageSender sender;

    @Autowired
    private UserMessageSender userMessageSender;

    @Autowired
    private TopicMessageSender topicMessageSender;

    private String exchange = "log-ex-direct";
    private String rkInfo = "direct-rk-info";
    private String rkError = "direct-rk-error";
    private String rkWarn = "direct-rk-warn";

    @Test
    public void sendMessage2Topic() {
        //随机数 %6
        //0 rk - user.rk.sms        *.rk.*    *.rk.sms
        //1 rk - user.rk.email      *.rk.*    *.rk.email
        //2 rk - order.rk.sms       *.rk.*    *.rk.sms
        //3 rk - order.rk.email     *.rk.*    *.rk.email
        //4 rk - reg.rk.sms   *.rk.*    *.rk.sms
        //5rk - reg.rk.qq     *.rk.*
        Random r = new Random();
        for (int i = 0; i < 10; i++) {
            int rInt = r.nextInt(100);
            if (rInt % 6 == 0) {
                this.topicMessageSender.send("ex-topic",
                        "user.rk.sms",
                        "用户登录验证码是:123456 - 发送短信");
            } else if (rInt % 6 == 1) {
                this.topicMessageSender.send("ex-topic",
                        "user.rk.email",
                        "用户登录验证码是:123456 - 发送到邮箱");
            } else if (rInt % 6 == 2) {
                this.topicMessageSender.send("ex-topic",
                        "order.rk.sms",
                        "订单下订成功 - 发送短信");
            }else if (rInt % 6 == 3) {
                this.topicMessageSender.send("ex-topic",
                        "order.rk.email",
                        "订单下订成功 - 发送到邮箱");
            }else if (rInt % 6 == 4) {
                this.topicMessageSender.send("ex-topic",
                        "reg.rk.sms",
                        "注册验证码是:654321 - 发送短信");
            }else if (rInt % 6 == 5) {
                this.topicMessageSender.send("ex-topic",
                        "reg.rk.qq",
                        "注册验证码是:654321 - 发送QQ");
            }
        }
    }

    @Test
    public void testSendUserMessage2Fanout() {
        for (int i = 0; i < 3; i++) {
            User user = new User();
            user.setId((long) i);
            user.setName("姓名 - " + i);
            user.setAge(20 + i);
            this.userMessageSender.sender(user);
        }
    }

    @Test
    public void testSend2Consumers() {
        for (int i = 0; i < 10; i++) {
            this.sender.sendMessage(exchange, rkInfo, "Info消息" + i);
            this.sender.sendMessage(exchange, rkError, "Error消息" + i);
            this.sender.sendMessage(exchange, rkWarn, "Warn消息" + i);
        }
    }

    @Test
    public void testSend() {
        Random r = new Random();

        //发送十条消息
        for (int i = 0; i < 10; i++) {
            // rInt%3 - 0:投递消息到 info  1:投递消息到 error 2: 投递消息到 warn
            int rInt = r.nextInt(100);
            if (rInt % 3 == 0) {
                this.sender.sendMessage(exchange, rkInfo, "发送Info日志消息 - index = " + i + "; rInt = " + rInt);
            } else if (rInt % 3 == 1) {
                this.sender.sendMessage(exchange, rkError, "发送Error日志消息 - index = " + i + "; rInt = " + rInt);
            } else if (rInt % 3 == 2) {
                this.sender.sendMessage(exchange, rkWarn, "发送Warn日志消息 - index = " + i + "; rInt = " + rInt);
            }
        }
    }

}

在这里插入图片描述

在这里插入图片描述

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值