《自学SpringCloud微服务架构》之第 11 篇 —— 消息总线:SpringCloud Bus

 

问:什么是消息总线?

答:在微服务架构中,通常使用轻量级的消息代理来构建一个公用的消息主题,以便让系统中所有的微服务实例连接上来,该主题中产生的消息会被所有实例监听和消费,称之为消息总线。

我们经常用到消息代理的场景:

1、将消息路由到一个或者多个目的地

2、将消息转化成其它的表现方式

3、将消息聚集、分解,并将结果发送到目的地,然后重新组合响应返回给消息用户。

4、响应事件或错误

5、使用发布-订阅模式来提供内容或基于主题的消息路由。

目前 SpringCloud Bus 支持两款比较常用的中间件产品:RabbitMQ Kafka

RabbitMQ 实现消息总线

RabbitMQ 是一款实现了高级消息队列协议AMQP:Advanced Message Queuing Protocol)的开源消息代理软件,也称为面向消息的中间件。SpringCloud Bus 中包含了对 Rabbit 的自动化默认配置。

基本概念

1、Broker:消息队列服务器的实体,负责接收消息生产者的消息,然后将消息发送至接收者或其他 Broker。

2、Exchange:消息交换机,消息到达的第一个地方,消息通过它的路由规则,分发到不同的消息队列中去

3、Queue:消息队列,消息被发送和路由最终到达的地方,到达 Queue 的消息即进入逻辑上等待消费的状态

4、Binding:绑定,作用是将 Exchange 和 Queue 按照路由规则绑定起来,相当于虚拟连接。

5、Routing Key:路由关键字,Exchange 根据这个关键字进行消息投递。

6、Virtual host:虚拟主机,它是对 Broker 的虚拟划分,将消费者、生产者和协议等进行隔离,是为了安全考虑,可设置多个虚拟主机。

7、Connection:连接,代表生产者、消费者、Broker 之间进行通信的物理网络。

8、Channel:消息通道,用于连接生产者、消费者的逻辑结构。在客户端的每个连接里,可以建立多个 Channel,每个 Channel 代表一个会话任务,可以隔离同一个连接中不同的交互内容。

9、Producer:消息生产者,制造消息并发送消息的程序。

10、Consumer:消息消费者,接收消息并处理消息的程序。

消息投递到队列的整个过程大致如下:

1、客户端连接到消息队列服务器,打开一个通道 Channel

2、客户端声明一个 Exchange,并设置相关属性规则。

3、客户端声明一个消息队列 Queue,并设置相关属性。

4、客户端使用 Routing Key,在 Exchange 和 Queue 之间建立好绑定关系。

5、客户端投递消息到 Exchange。

6、Exchange 接收到消息后,根据消息的 Key 和已经设置的 Binding,进行消息路由,将消息投递到一个或者多个Queue。

Exchange 有几种类型:

1、Direct交换机:完全根据 Key 进行投递。比如绑定了 Routing Key 为 abc123,那么客户端提交的消息,只有设置了 Key 为 abc123 的才会被投递到队列。

2、Topic交换机:对 Key 进行模式匹配后进行投递,可以使用符号 # 匹配一个多个词,符号 * 匹配一个词。比如:abc.# 可以匹配 abc.edf.nice   abc.* 只能匹配 abc.beautiful 。注意是词,不是单个字符。

3、Fanout交换机:不需要任何 Key,采用广播式,一个消息进来时,投递到所有绑定的队列中。

RabbitMQ 支持消息持久化,也是将数据写到磁盘上。为了安全考虑,多数情况下都会选择持久化。包含3个部分:

1、Exchange 持久化,在声明时指定 durable =>1

2、Queue持久化,在声明时指定 durable => 1

3、消息持久化,在投递时指定 delivery_mode => 2(如果是1,则是非持久化)

如果 Exchange 和 Queue 都是持久化,则他们之间的 Binding 也是持久化的。如果有一个不是持久化,则不允许建立绑定。

 

OK,理论少说,我们直接动手。

1、先下载 Erlang 和  RabbitMQ。我们是在 windows 下学习的,所以安装 windows 版。

先下载 Erlang。打开链接:https://www.erlang.org/downloads  下载 OTP22.0 (一般电脑都是 64 位了,32位是老人机了吧?)。

 

 

安装 Erlang。注意安装路径不要起中文名字,这是做一个程序员最基本的要求(除非是国产的东西,才对中文客气)。

安装过程跟一般的软件一样,这里不赘述。如果是正常的安装,Erlang 会自动帮我们配置好环境变量:

然后,下载 RabbitMQ,打开链接:https://www.rabbitmq.com/news.html  下载 RabbitMQ 3.7.20 release 版本。它要求 Erlang 最低版本在 21.3,我们下载的是 22.0,兼容低版本。

 

接下来,安装 RabbitMQ。注意会弹出一个防火墙的访问设置,点击允许即可。

RabbitMQ Server 安装完成之后,会自动注册为服务,并且使用默认的配置。我们可以通过访问配置文件、Web页面进行Rabbit 管理。推荐使用 Web 进行管理。注意:RabbitMQ 默认监听的端口号是 5672,web端的端口是 15672

安装完毕之后,我们浏览器地址访问:http://localhost:15672/  发现无法访问。我们需要执行一段命令,来开启 Web 管理。

1、首先,我们找到 RabbitMQ 的安装路径,找到安装目录下的 sbin 文件夹,在 sbin 文件夹里找到 rabbitmq-queues.bat

2、对着文件  rabbitmq-queues.bat 鼠标右键,找到“属性”,把 rabbitmq-queues.bat 所在的路径复制出来,如截图:

 

3、打开电脑的菜单键——》输入cmd(然后按Enter键确定)——》然后按住键盘的 Shift + Insert 组合键,把刚才复制的路径粘贴到命令行中,然后敲一个空格(切记),再复制下面的命令(开启 Web 插件),粘贴到命令行中,然后回车。

enable rabbitmq_management

完整的形式如下(这时候一般会报错,因为 CMD 命令行不是管理员权限):

 

因为 CMD 不是管理员权限,我们需要做一些修改。具体方法如下:

也可以参考我的博客,里面有详细的方法:https://blog.csdn.net/BiandanLoveyou/article/details/84677787

解决办法:先关闭命令提示符,然后打开电脑的系统盘C盘。

打开路径:C:\Windows\System32   然后在右上角的搜索框输入cmd.exe

 

把这个 cmd.exe 可执行文件放到桌面快捷方式

 

然后,到桌面找到它,对着快捷方式,鼠标右键,选择属性。找到“快捷方式”,选择“高级”

 

勾选“用管理员身份运行”,确定。

然后双击打开桌面的快捷方式,再重试刚才的操作:先复制 rabbitmq-queues.bat 的路径,Shift + Insert 粘贴,再空格,再粘贴 enable rabbitmq_management。成功的截图如下:

接下来,我们重启一下 RabbitMQ Server 服务。在有管理员权限的命令行输入如下命令(复制,然后 Shift + Insert 粘贴):

net stop RabbitMQ && net start RabbitMQ

以后就可以使用命令 net start RabbitMQ 来开启 RabbitMQ Server 服务了。

OK,大功告成。浏览器地址栏输入:http://localhost:15672/  显示登录页面,账号、密码都是:guest

这时候,可以进入到 Web 管理页面了

可以注意到,顶部菜单选项卡正是我们之前介绍的专业术语

 

接下来,我们新增一个用户。点击 Admin 选项卡,找到“Add a user

我们新增一个用户,在 Tags 选项,我们直接选择 Admin(为方便学习)

说明:Tags 标签是 RabbitMQ 中的角色分类,有以下几种

1、None:不能访问 management plugin

2、Impersonator:演示者

3、Management:普通管理者,除了可以通过 AMQP 做的事外加如下内容

①列出自己可以通过 AMQP 登入的 virtual hosts

②查看自己的 virtual hosts 中的 queues、exchange和 bindings。

③查看和关闭自己的 channels 和 connections

④查看有关自己的 virtual hosts 的“全局”统计信息。

4、Policymaker:策略制定者,除了 management 可以做的任何事,还可以查看、创建和删除自己的 virtual hosts 所属的 policies 和 parameters。

5、Monitoring:监控者,除了可以做 management 任何事,还包括

①列出所有的 virtual hosts,包括它们不能登录的 virtual hosts

②查看其他用户的 connections 和 channels。

③查看节点级别的数据,如 clustering(集群) 和 memory(内存) 的 使用情况。

④查看真正的关于所有 virtual hosts 的全局的统计信息。

5、Admin:超级管理员,除了 policymaker 和 monitoring 可以做的事,还包括

①创建和删除 virtual hosts

②查看、创建、删除 users

③查看、创建、删除 permissions

④关闭其他用户的 connections。

 

接下来,我们学习如何整合 RabbitMQ 到 SpringCloud 架构中,实现一个简单的发送、接收消息例子。

首先,我们创建一个 SpringBoot 模块:rabbitmq-bus

pom.xml 配置文件中,增加依赖:spring-cloud-starter-bus-amqp,完整的配置如下

<?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">
    <parent>
        <artifactId>MyProject</artifactId>
        <groupId>com.study</groupId>
        <version>1.0-SNAPSHOT</version>
    </parent>
    <modelVersion>4.0.0</modelVersion>

    <artifactId>rabbitmq-bus</artifactId>

    <dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-bus-amqp</artifactId>
        </dependency>
        <!-- 添加 SpringBoot 测试类的依赖 -->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
        </dependency>
        <dependency>
            <groupId>junit</groupId>
            <artifactId>junit</artifactId>
            <version>4.12</version>
        </dependency>
        <dependency>
            <groupId>net.minidev</groupId>
            <artifactId>asm</artifactId>
            <version>1.0.2</version>
        </dependency>

    </dependencies>

</project>

 

新增 application.yml 配置文件,完整配置信息如下(这里的用户名、密码使用默认的 guest):
 

# 这是客户端服务的配置节点
server:
  port: 8050
  
# 服务的名字
spring:
  application:
    name: eureka-rabbitmq-bus
  # rabbitmq 的配置信息
  rabbitmq:
    host: localhost
    port: 5672
    username: guest
    password: guest

接下来,我们创建启动的主类:RabbitMQBusApplication

 

完整的 RabbitMQBusApplication 代码如下:

package com.study;

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

/**
 * @author biandan
 * @signature 让天下没有难写的代码
 * @create 2019-11-02 上午 11:53
 */
@SpringBootApplication
public class RabbitMQBusApplication {

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

接下来,我们创建消息生产者类:MessageSender,注入 AmqpTemplate 接口的实例来实现消息的发送。AmqpTemplate 接口定义了一套针对 AMQP 协议的基本操作。

先创建一个 package 用来存放 MessageSender 类

 

 

完整的 MessageSender 代码如下:

package com.study.sender;

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

import java.text.SimpleDateFormat;
import java.util.Date;

/**
 * @author biandan
 * @signature 让天下没有难写的代码
 * @create 2019-11-02 下午 12:05
 */
@Component
public class MessageSender {

    @Autowired
    private AmqpTemplate amqpTemplate;

    //格式化时间
    private static SimpleDateFormat SDF = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");

    /**
     * 发送消息的方法
     */
    public void sendMessage() {
        String message = "让天下没有难写的代码:" + SDF.format(new Date());
        System.out.println("将要发送的消息是:" + message);
        this.amqpTemplate.convertAndSend("messageKey", message);
    }
}

 

接下来,我们创建消息消费者类:MessageReceiver,通过 @RabbitListener 注解定义该类对 messageKey(即消息生产者中定义的队列 key) 队列的监听。并使用 @RabbitHandler 注解来指定对消息的处理方法。

先创建一个 package 包,receive

 

 

MessageReceiver 完整代码如下:

package com.study.receive;

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

/**
 * @author biandan
 * @signature 让天下没有难写的代码
 * @create 2019-11-02 下午 12:31
 */
@Component
@RabbitListener(queues = "messageKey")
public class MessageReceiver {

    @RabbitHandler
    public void receiveMessage(String message) {
        System.out.println("接收到的消息是:" + message);
    }
}

接下来,我们还要创建 RabbitMQ 的配置类 RabbitConfig(可以起别的自定义名字,用来配置队列、交换器、路由等高级信息。

完整的 RabbitConfig 代码如下(注意:Queue 导入的是 org.springframework.amqp.core.Queue):

package com.study.config;

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

/**
 * @author biandan
 * @signature 让天下没有难写的代码
 * @create 2019-11-02 下午 12:40
 */
@Configuration
public class RabbitConfig {

    @Bean
    public Queue queueConfig(){
        return new Queue("messageKey");
    }
    
}

接下来,我们创建单元测试类:RabbitMQTest。单元测试,是每个程序员都应该懂的技巧。

 

完整的 RabbitMQTest 代码如下:

 

package com.study.test;

import com.study.RabbitMQBusApplication;
import com.study.sender.MessageSender;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.SpringBootConfiguration;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.test.context.junit4.SpringRunner;

/**
 * @author biandan
 * @signature 让天下没有难写的代码
 * @create 2019-11-02 下午 12:49
 */
@RunWith(SpringRunner.class)
@SpringBootTest(classes = RabbitMQBusApplication.class)
@SpringBootConfiguration
public class RabbitMQTest {

    @Autowired
    private MessageSender messageSender;

    @Test
    public void sendTest(){
        messageSender.sendMessage();;
    }

}

注意:

1、SpringBoot 2.0 之后,对 Junit 测试类的 pom 依赖有所变化,请按照上面的代码添加依赖。

2、@SpringBootTest(classes = RabbitMQBusApplication.class):classes 使用的是启动类名

 

接下来,我们先保证 RabbitMQ Server 处于启动状态,然后运行启动类:SpringBootApplication

这时候,我们查看控制台的输出信息:

Created new connection: rabbitConnectionFactory#4eb166a1:0/SimpleConnection@8641b7d 
[delegate=amqp://guest@127.0.0.1:5672/, localPort= 51344]

说明程序创建了一个访问 127.0.0.1:5672(localhost)的 SpringCloud 连接,我们再去浏览器端查看 Connections 和 Channels:

已经有我们的连接条数。

接下来,运行测试类的 sendTest() 方法:对着方法名鼠标右键,Debug 运行

 

如果运行报错,会出现红色的信息,如果运行正常,会出现绿色信息:

说明消息生产者已经把消息发送到 RabbitMQ Server 的 messageKey 队列了,并且被监听器监听到,输出了消息内容。

在整个生产、消费的过程中,生产者和消费者是一个异步的操作,这也是在分布式系统中使用消息代理的重要原因,我们可以使用通信来解耦业务逻辑。

接下来,我们测试一下异步的操作:把 MessageReceiver 类的 @RabbitHandler 注解先注释掉,让消息无消费者消费。

然后,我们重复运行测试类的 sendTest() 方法 3 次,让消息生产者生产 3 条消息,无消费者消费。

我们再去 Web 端查看 Queues 选项卡,多了 3 条信息。

这时候,再把 MessageReceiver 类的 @RabbitHandler 注释去掉,再运行测试类的 sendTest() 方法。

我们观察到,消息监听器监听到之前有 3 条消息未被消费,直接去消费了。然后新增1条消息,也消费了。

再去 Web 端查看 Queues 选项卡,已经没有待消费的消息了。这就实现了消息的异步处理

 

 

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值