SpringCloud----RabbitMQ高级消息队列

文章目录

  • 概述
  • 使用场景
  • 虚拟机配置
    • Docker
      • 下载docker离线包
      • 离线安装工具
      • 安装
      • 镜像加速
      • 测试
    • Docker运行RabbitMQ
      • RabbitMQ配置
  • rabbitmq六种工作模式
    • 简单模式
      • 准备工作
      • 生产者发送信息
        • 参数说明
        • 测试
      • 消费者接受消息
        • 参数说明
    • 工作模式
      • 生产者发送消息
      • 消费者接收消息
      • 测试一
      • 测试二
      • 测试三(手动确认回执)
        • 测试
      • 队列持久化
    • 发布订阅模式
      • 生产者
      • 消费者
      • 测试
    • 主题模式
      • 生产者
      • 消费者
      • 测试一
      • 测试二
  • virtual host 设置空间
      • 查看结果
  • Sleuth + Zipkin 链路跟踪
    • 添加依赖
    • Zipkin
      • 下载
      • 启动服务器
      • Zipkin访问地址
      • 添加依赖
      • 配置yml
  • 导入商城项目
  • 订单发送到 RabbitMQ
    • 添加依赖
    • 配置yml
    • 配置启动类
    • 修改 orderServiceImpl
  • 订单消费者
    • 新建消费者类: OrderConsumer

概述

RabbitMQ是一种消息中间件,用于处理来自客户端的异步消息。服务端将要发送的消息放入到队列池中。接收端可以根据RabbitMQ配置的转发机制接收服务端发来的消息。

RabbitMQ依据指定的转发规则进行消息的转发、缓冲和持久化操作,主要用在多服务器间或单服务器的子系统间进行通信,是分布式系统标准的配置。

使用场景

虚拟机配置

SpringCloud----RabbitMQ高级消息队列_第1张图片

  1. 解压出来
  2. 通过VMware打开
  3. 内容
    1. yun 安装源, 扩展源使用了阿里服务器
    2. 安装了Python, pip, ansible
    3. 添加了两个脚本文件, 方便配置IP地址
      1. IP-static- 配置固定IP
      2. IP-dhcp: 自动获取IP
  4. 使用./ip-dhcp命令自动获取IP
  5. 关机克隆设置名字为 Docker-base 用来安装docker
  6. ifconfig查看IP地址
  7. 使用MobaXterm连接虚拟机

Docker

下载docker离线包

https://download.docker.com/linux/static/stable/x86_64/docker-20.10.6.tgz

这个地址可以选择自己需要的版本进行下载:
https://download.docker.com/linux/static/stable/

离线安装工具

https://github.com/Jrohy/docker-install/

SpringCloud----RabbitMQ高级消息队列_第2张图片

安装

  1. 将前面下载的以下文件放入服务器的 /root/docker-install 文件夹下:

    - [docker-install]
    	- docker-20.10.6.tgz
    	- install.sh
    	- docker.bash
    
  2. 执行安装:

    # 进入 docker-install 文件夹
    cd docker-install
    
    # 为 docker-install 添加执行权限
    chmod +x install.sh
    
    # 安装
    ./install.sh -f docker-20.10.6.tgz
    

镜像加速

由于国内网络问题,需要配置加速器来加速。
修改配置文件 /etc/docker/daemon.json

下面命令直接生成文件 daemon.json

cat < /etc/docker/daemon.json
{
  "registry-mirrors": [
    "https://docker.mirrors.ustc.edu.cn",
    "http://hub-mirror.c.163.com"
  ],
  "max-concurrent-downloads": 10,
  "log-driver": "json-file",
  "log-level": "warn",
  "log-opts": {
    "max-size": "10m",
    "max-file": "3"
    },
  "data-root": "/var/lib/docker"
}
EOF

​ 重启服务

# 重新加载docker配置
sudo systemctl daemon-reload

#重启docker服务
sudo systemctl restart docker

测试

docker info

docker run hello-world

Docker运行RabbitMQ

  1. 克隆Docker虚拟机: RabbitMQ
  2. 设置IP地址
  3. MobaX连接虚拟机

RabbitMQ配置

  1. Rabbit镜像拖入root目录

SpringCloud----RabbitMQ高级消息队列_第3张图片

  1. 导入镜像docker load < rabbit-image.gz

  2. 关闭防火墙

    systemctl stop firewalld
    systemctl disable firewalld
     
    # 重启 docker 系统服务
    systemctl restart docker
    
  3. 配置管理员账号密码

    mkdir /etc/rabbitmq
    vim /etc/rabbitmq/rabbitmq.conf
    
    # 添加两行配置:
    default_user = admin
    default_pass = admin
    
  4. 启动Rabbitmq

    docker run -d --name rabbit \
    -p 5672:5672 \
    -p 15672:15672 \
    -v /etc/rabbitmq/rabbitmq.conf:/etc/rabbitmq/rabbitmq.conf \
    -e RABBITMQ_CONFIG_FILE=/etc/rabbitmq/rabbitmq.conf \
    --restart=always \
    rabbitmq:management
    
    
  5. 访问测试

访问管理控制台 http://192.168.64.4:15672
用户名密码是 admin

SpringCloud----RabbitMQ高级消息队列_第4张图片

rabbitmq六种工作模式

简单模式

准备工作

SpringCloud----RabbitMQ高级消息队列_第5张图片

  1. 新建空项目

  2. 添加依赖

        <dependencies>
            <dependency>
                <groupId>com.rabbitmqgroupId>
                <artifactId>amqp-clientartifactId>
                <version>5.4.3version>
            dependency>
        dependencies>
    

生产者发送信息

public class Producer {
    public static void main(String[] args) throws IOException, TimeoutException {
        /*连接*/
        ConnectionFactory f = new ConnectionFactory();
        f.setHost("192.168.64.4");
        f.setPort(5672);
        f.setUsername("admin");
        f.setPassword("admin");

        Connection con = f.newConnection();   // 创建连接
        Channel c = con.createChannel();  // 通信的通道

        /*在服务器上创建 helloworld 队列
        * 队列如果存在, 不会重复创建*/
        c.queueDeclare("helloworld",false,false,false,null); //队列定义/声明

        /*向队列发送消息*/
        c.basicPublish("","helloworld",null,"Hello World".getBytes());  // 2. 队列名

    }
}

  • setHost : 为 Rabbitmq服务虚拟机IP
  • setPort : Rabbitmq端口号
  • setUsername/setPassword : Rabbitmq配置的用户名密码

参数说明

image-20211213204356740

  1. 队列名

  2. 布尔值(是否是持久队列)持久: 重启服务器队列存在 (在磁盘储存队列信息)

  3. 是否是排他队列 (独占队列) (是否可以共享队列)

  4. 队列能不能被服务器自动删除, (没有消费者时服务器是否自动删除)

  5. 队列的其他参数属性

  6. 交换机,空串是默认交换机

  7. 队列名

  8. 消息的其他参数属性 (用键值对设置参数属性, 如果没有就用 null )

测试

消费者接受消息

public class Consumer {
    public static void main(String[] args) throws IOException, TimeoutException {
        /*建立连接*/
        ConnectionFactory f = new ConnectionFactory();
        f.setHost("192.168.64.4");
        f.setPort(5672);
        f.setUsername("admin");
        f.setPassword("admin");

        Connection con = f.newConnection();   // 创建连接
        Channel c = con.createChannel();  // 通信的通道

        /*创建队列*/
        c.queueDeclare("helloworld",false,false,false,null); //队列定义/声明

        /*创建回调对象*/
        DeliverCallback deliverCallback = new DeliverCallback() {
            public void handle(String s, Delivery delivery) throws IOException {
                byte[] a = delivery.getBody();
                String string = new String(a);
                System.out.println("收到:"+ string);
            }
        };  // 处理消息的回调
        CancelCallback cancelCallback = new CancelCallback() {
            public void handle(String s) throws IOException {

            }
        };  // 取消消息的回调

        /*从队列接收消息, 把消息传递到回调对象处理*/
//        c.basicConsume("helloworld", true, 处理消息的回调对象, 取消消息的回调对象);
        c.basicConsume("helloworld", true, deliverCallback, cancelCallback);
    }
}

参数说明

image-20211213152129190

  1. 确认方式 ACK – Acknowledgment

    • true 自动确认
    • false 手动确认

工作模式

多个消费者接收消息

生产者发送消息

public class Producer {
    public static void main(String[] args) throws IOException, TimeoutException {
        /*创建连接*/
        ConnectionFactory f = new ConnectionFactory();
        f.setHost("192.168.64.4");
        f.setPort(5672);
        f.setUsername("admin");
        f.setPassword("admin");

        Connection con = f.newConnection();   // 创建连接
        Channel c = con.createChannel();  // 通信的通道

        /*创建队列*/
        c.queueDeclare("helloworld",false,false,false,null); //队列定义/声明

        /*循环输入消息发送*/
        while (true) {
            System.out.println("请输入消息:");
            String s = new Scanner(System.in).nextLine();
            c.basicPublish("","helloworld",null,s.getBytes());
        }

    }
}

消费者接收消息

  • 遍历每一个数据
  • 当遇到" . " 时就暂停1秒
public class Consumer {
    public static void main(String[] args) throws IOException, TimeoutException {
        ConnectionFactory f = new ConnectionFactory();
        f.setHost("192.168.64.4");
        f.setPort(5672);
        f.setUsername("admin");
        f.setPassword("admin");

        Connection con = f.newConnection();   // 创建连接
        Channel c = con.createChannel();  // 通信的通道

        /*创建队列*/
        c.queueDeclare("helloworld",false,false,false,null); //队列定义/声明

        /* 回调对象*/
        DeliverCallback deliverCallback = (consumerTag, message) -> {
            String s = new String(message.getBody());
            System.out.println(s);
            // 遍历所有字符,遇到 '.' 暂停1秒
            for (int i = 0; i < s.length(); i++) {
                if (s.charAt(i) == '.') {
                    try {
                        Thread.sleep(1000);
                    } catch (InterruptedException e) {
                    }
                }
            }
            System.out.println("-------------------------------消息处理完成");
        };
        CancelCallback cancelCallback = consumerTag -> {};

        // 接收消息
        c.basicConsume("helloworld",true,deliverCallback,cancelCallback);
    }
}

测试一

  1. Producer发送消息

    SpringCloud----RabbitMQ高级消息队列_第6张图片

  2. Consumer接收消息

    SpringCloud----RabbitMQ高级消息队列_第7张图片

  3. 查看服务器

    SpringCloud----RabbitMQ高级消息队列_第8张图片

测试二

  • 启动多个消费者

  1. Producer 发送消息

  2. 查看两个Consumer接收

  1. 通过" . " 测试, 先发送一堆 " . "
  1. Consumer接收到 “…” 接着发送 1-9测试

SpringCloud----RabbitMQ高级消息队列_第9张图片

  • 接收到"…"的Consumer-2 进入等待

    image-20211213152328759

  • 另一个 Consumer 打印出一部分信息

    SpringCloud----RabbitMQ高级消息队列_第10张图片

  • Consumer-2 等待结束后, 打印出剩下的消息

    SpringCloud----RabbitMQ高级消息队列_第11张图片

测试三(手动确认回执)

修改成手动确认回执

  1. 修改Consumer 手动确认

image-20211213152358163

  1. 添加回执

    image-20211213152405668

  2. 添加Qos

    SpringCloud----RabbitMQ高级消息队列_第12张图片

测试

  1. produce 发送 “…” 和 1-9

  2. Consumer2接收后进入等待

  3. Consumer2接收后进入等待

  4. Consumer接收到所有信息

队列持久化

  1. 将Producer 持久化改成 true, 队列名改成"task_queue"

image-20211213152454325

  1. basicPublish其他参数属性改为 持久化属性

    SpringCloud----RabbitMQ高级消息队列_第13张图片

  2. 修改Consumer 创建队列参数

  3. 修改Consumer 接收信息队列名 (task_queue)

  4. 添加队列信息测试, 打开RabbitMQ 服务器查看队列信息, 会发现有helloworld 和 task_queue 两个队列

  5. 重启 RabbitMQ 服务, 再打开RabbitMQ 服务器查看, 没有持久化的helloworld就没有了, 做了持久化的task_queue 还存在

发布订阅模式

生产者

package m3;

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

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

/**
 * 生产商
 *
 * @author 刘杰
 * @date 2021/11/22 10:48:59
 */
public class Producer {
    public static void main(String[] args) throws IOException, TimeoutException {
        // 连接
        ConnectionFactory f = new ConnectionFactory();
        f.setHost("192.168.64.4"); // wht6.cn
        f.setPort(5672);
        f.setUsername("admin");
        f.setPassword("admin");
        Channel c = f.newConnection().createChannel();
        // 创建 Fanout 类型的交换机: logs
        // c.exchangeDeclare("logs", "fanout");
        c.exchangeDeclare("logs", BuiltinExchangeType.FANOUT);
        // 向 logs 交换机发送消息
        while (true) {
            System.out.print("输入消息:");
            String s = new Scanner(System.in).nextLine();
            // 第二个参数,对于fanout类型交换机无效
            c.basicPublish("logs", "", null, s.getBytes());
        }
    }
}

消费者

package m3;

import com.rabbitmq.client.*;

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

/**
 * 消费者
 *
 * @author 刘杰
 * @date 2021/11/22 10:08:32
 */
public class Consumer {
    public static void main(String[] args) throws IOException, TimeoutException {
        // 连接
        ConnectionFactory f = new ConnectionFactory();
        f.setHost("192.168.64.4"); // wht6.cn
        f.setPort(5672);
        f.setUsername("admin");
        f.setPassword("admin");
        Channel c = f.newConnection().createChannel();


        // 1.创建随机队列 2.创建交换机 3.绑定  手动命名
        String queue = UUID.randomUUID().toString();
        c.queueDeclare(queue,false,true,true,null);  //手动提供参数
        c.exchangeDeclare("logs", BuiltinExchangeType.FANOUT);
        // 第三个参数对 fanout 交换机无效
        c.queueBind(queue, "logs", "");

        // 正常的从随机队列接收处理消息
        DeliverCallback deliverCallback = (consumerTag, message) -> {
            String s = new String(message.getBody());
            System.out.println("收到:"+s);
        };
        CancelCallback cancelCallback = consumerTag -> {};
        c.basicConsume(queue,true,deliverCallback,cancelCallback);
    }
}

测试

主题模式

生产者

public class Producer {
    public static void main(String[] args) throws IOException, TimeoutException {
        ConnectionFactory f = new ConnectionFactory();
        f.setHost("192.168.64.4"); // wht6.cn
        f.setPort(5672);
        f.setUsername("admin");
        f.setPassword("admin");
        f.setVirtualHost("/");  //默认空间
        Channel c = f.newConnection().createChannel();

        c.exchangeDeclare("direct_logs", BuiltinExchangeType.DIRECT);

        while (true) {
            System.out.println("输入消息: ");
            String s = new Scanner(System.in).nextLine();
            System.out.println("输入路由键: ");
            String k = new Scanner(System.in).nextLine();
            /*2: 路由键关键词
            * 如果用默认交换机, 路由键就是队列名*/
            c.basicPublish("direct_logs", k,null, s.getBytes(StandardCharsets.UTF_8));

        }
    }
}

消费者

public class Consumer {
    public static void main(String[] args) throws IOException, TimeoutException {
        ConnectionFactory f = new ConnectionFactory();
        f.setHost("192.168.64.4"); // wht6.cn
        f.setPort(5672);
        f.setUsername("admin");
        f.setPassword("admin");
        Channel c = f.newConnection().createChannel();

        String queue = c.queueDeclare().getQueue();//服务器自动提供参数: 随机名,false,true,true  .getQueue(): 得到随机名(因为后面要用)
        c.exchangeDeclare("direct_logs", BuiltinExchangeType.DIRECT);
        System.out.println("输入绑定键, 用空格隔开: ");   // "aa  bb  cc"
        String s = new Scanner(System.in).nextLine();
        String[] a = s.split("\\s+");// \s是空白字符, +表示一到多个 (一个或多个都可以)
        for (String k : a) {
            c.queueBind(queue, "direct_logs", k);
        }
        DeliverCallback deliverCallback = (consumerTag, message) -> {
            java.lang.String ss = new java.lang.String(message.getBody());
            System.out.println("收到:" + ss);
        };
        CancelCallback cancelCallback = consumerTag -> {};
        c.basicConsume(queue,true, deliverCallback, cancelCallback);

    }
}

测试一

  • 消费者输入绑定键

  • 生产者输入消息 + 路由键

    SpringCloud----RabbitMQ高级消息队列_第14张图片

  • 消费者收到消息

    SpringCloud----RabbitMQ高级消息队列_第15张图片

测试二

  • 生产者随便输入

    SpringCloud----RabbitMQ高级消息队列_第16张图片

  • 消费者不会受到

virtual host 设置空间

在RabbitMQ中叫做虚拟消息服务器VirtualHost,每个VirtualHost相当于一个相对独立的RabbitMQ服务器,每个VirtualHost之间是相互隔离的。exchange、queue、message不能互通

  • 默认空间名为: " / "

查看结果

使用:

Sleuth + Zipkin 链路跟踪

随着系统规模越来越大,微服务之间调用关系变得错综复杂,一条调用链路中可能调用多个微服务,任何一个微服务不可用都可能造整个调用过程失败

spring cloud sleuth 可以跟踪调用链路,分析链路中每个节点的执行情况

添加依赖

        <dependency>
            <groupId>org.springframework.cloudgroupId>
            <artifactId>spring-cloud-starter-sleuthartifactId>
        dependency>

Zipkin

https://github.com/openzipkin/zipkin

下载

启动服务器

在根目录打开终端控制器

java -jar zipkin-server-2.23.5-exec.jar --zipkin.collector.rabbitmq.uri=amqp://admin:[email protected]:5672

如用自己的空间

java -jar zipkin-server-2.12.9-exec.jar --zipkin.collector.rabbitmq.uri=amqp://admin:[email protected]:5672 / 空间名

Zipkin访问地址

http://localhost:9411/zipkin

添加依赖

        <dependency>
            <groupId>org.springframework.cloudgroupId>
            <artifactId>spring-cloud-starter-zipkinartifactId>
        dependency>

        <dependency>
            <groupId>org.springframework.bootgroupId>
            <artifactId>spring-boot-starter-amqpartifactId>
        dependency>

配置yml

添加06网关, 三个消费服务

rabbitmq:
    port: 5672
    host: 192.168.64.4
    username: admin
    password: admin
    virtual-host: /
  zipkin:
    sender:
      type: rabbit   # zipkin 向 Rabbit 转发日志

导入商城项目

  1. 课前资料/elasticsearch/pd-商城项目案例.zip 里面的 pd-web 文件夹,解压到 rabbitmq 工程目录

  2. pom文件 SpringBoot版本改为2.3.2.RELEASE

  3. 配置连接数据库

  4. 执行启动类后配置启动类

  5. 在 working directory 中配置 pd-web 模块目录路径 然后重新启动

    SpringCloud----RabbitMQ高级消息队列_第17张图片

  6. 访问 http://localhost

订单发送到 RabbitMQ

添加依赖

		<dependency>
			<groupId>org.springframework.bootgroupId>
			<artifactId>spring-boot-starter-amqpartifactId>
		dependency>

配置yml

  rabbitmq:
    host: 192.168.64.4
    port: 5672
    username: amdin
    password: admin
    virtual-host: /

配置启动类

  1. 新建 spring 的 Queue 实例
  2. 封装队列的参数
  3. rabbitmq的自动配置类会自动发现这个Queue 实例
  4. 根据其中的参数自动在服务器上创建队列
	@Bean
	public Queue orderQueue() {
		return new Queue("orderQueue", true, false, false);
	}

队列参数解析

  1. 持久队列
  2. 独占队列
  3. 不自动删除

修改 orderServiceImpl

  1. 注释 64-89行

    SpringCloud----RabbitMQ高级消息队列_第18张图片

  2. 添加 代码

    • 注入对象: AmqpTemplate (用来封装发送消息代码的工具)

    SpringCloud----RabbitMQ高级消息队列_第19张图片

订单消费者

  1. 复制 pd-web 项目起名为 pd-web-consumer
  2. 修改pom文件

新建消费者类: OrderConsumer

  1. 添加注解

    • @RabbitListener 通过注解配置就可以接受消息
    • queues = “orderQueue” 队列名
  2. 创建方法

    SpringCloud----RabbitMQ高级消息队列_第20张图片

用注解配置接收消息
收到的订单通过原来的业务方法代码, 存储到数据库

你可能感兴趣的:(SpringCloud,rabbitmq,spring,cloud,分布式)