RabbitMQ之基本使用

一、信息队列

1、Message queue 释义

服务之间最常见的通信方式是直接调用彼此来通信,消息从一端发出后立即就可以达到另一端,称为即时消息通讯(同步通信)
消息从某一端发出后,首先进入一个容器进行临时存储,当达到某种条件后,再由这个容器发送给另一端,称为延迟消息通讯(异步通信)

2、问题思考

假设我们在淘宝下了一笔订单后,淘宝后台需要做这些事情:
        1. 消息通知系统:通知商家,你有一笔新的订单,请及时发货
        2. 推荐系统:更新用户画像,重新给用户推荐他可能感兴趣的商品
        3. 会员系统:更新用户的积分和等级信息

createOrder(...) {
  // 完成订单服务
  doCreateOrder(...);
  // 调用其他服务接口
  sendMsg(...);
  updateUserInterestedGoods(...);
  updateMemberCreditInfo(...);
}

存在问题 :

过度耦合:如果后面创建订单时,需要触发新的动作,那就得去改代码,在原有的创建订单函数末尾,再追加一行代码
缺少缓冲:如果创建订单时,会员系统恰好处于非常忙碌或者宕机的状态,那这时更新会员信息就会失败,我们需要一个地方,来暂时存放无法被消费的消息

优化方案:

我们需要一个消息中间件,来实现解耦和缓冲的功能

 3、消息队列特点

1. 解耦:每个成员不必受其他成员影响,可以更独立自主,只通过一个简单的容器来联系.
2. 提速:小红选只要做一个放书的动作,为自己节省了大量时间.
3. 广播:小红只需要劳动一次,就可以让多个小伙伴有书可读,这大大地节省了她的时间,也让新的小伙伴的加入成本很低.
4. 错峰与流控:小红给书的频率不稳定,如果今明两天连给了五本,之后隔三个月才又给一本,那小明只要在三个月内从书架上陆续取走五本书读完就行了,压力就不那么大了.

二、rabbitMQ结构初始

1、消息队列相关

AMQP:

一个提供统一消息服务的应用层标准高级消息队列协议,是一个通用的应用层协议
消息发送与接受的双方遵守这个协议可以实现异步通讯.这个协议约定了消息的格式和工作方式.

2、技术选型

 3、RabbitMq

RabbitMQ是一个实现了AMQP(Advanced Message Queuing Protocol)高级消息队列协议的消息队列服务,用Erlang语言.

Server(Broker):接收客户端连接,实现AMQP协议的消息队列和路由功能的进程.
Virtual Host:虚拟主机的概念,类似权限控制组,一个Virtual Host里可以有多个Exchange和Queue.
Exchange:交换机,接收生产者发送的消息,并根据Routing Key将消息路由到服务器中的队列Queue.
ExchangeType:交换机类型决定了路由消息行为,RabbitMQ中有三种类型Exchange,分别是fanout、direct、topic.
Message Queue:消息队列,用于存储还未被消费者消费的消息.
Message:由Header和body组成,Header是由生产者添加的各种属性的集合,包括Message是否被持久化、优先级是多少、由哪个Message Queue接收等.body是真正需要发送的数据内
容.
BindingKey:绑定关键字,将一个特定的Exchange和一个特定的Queue绑定起来. 

三、Docker安装部署RabbitMQ

1、拉取镜像:

注意获取镜像的时候要获取management版本的,不要获取last版本的,management版本的才带有管理界面

docker pull rabbitmq:management

2、运行rabbitmq

新建文件夹rabbitmq:

 运行:

docker run -d \
--name my-rabbitmq \
-p 5672:5672 -p 15672:15672 \
-v /home/rabbitmq:/var/lib/rabbitmq \
--hostname my-rabbitmq-host \
-e RABBITMQ_DEFAULT_VHOST=my_vhost \
-e RABBITMQ_DEFAULT_USER=admin \
-e RABBITMQ_DEFAULT_PASS=admin \
--restart=always \
rabbitmq:management

--hostname:主机名(RabbitMQ的一个重要注意事项是它根据所谓的 “节点名称” 存储数据,默认为主机名)
-e:指定环境变量:
RABBITMQ_DEFAULT_VHOST:默认虚拟机名
RABBITMQ_DEFAULT_USER:默认的用户名
RABBITMQ_DEFAULT_PASS:默认用户名的密码

 查看是否运行起:docker ps -a

访问成功:密码和用户名都是admin

 进入管理员界面,新建一个账户

给新建的账户分配能够操纵的主机:点击所建账号——》点解permissions权限

 拥有了操作权限:

 四、搭建rabbitMQ项目

1、新建idea项目:父项目

 pom文件:

<?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>org.example</groupId>
    <artifactId>rabbitMQ</artifactId>
    <version>1.0-SNAPSHOT</version>
    <packaging>pom</packaging>
    <modules>
        <module>provider</module>
        <module>consumer</module>
    </modules>

    <properties>
        <java.version>1.8</java.version>
        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
        <project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
        <spring-boot.version>2.4.1</spring-boot.version>
    </properties>

    <dependencies>
        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
            <optional>true</optional>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <scope>test</scope>
        </dependency>
<!--        amqp-->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-amqp</artifactId>
        </dependency>
    </dependencies>

    <dependencyManagement>
        <dependencies>
            <dependency>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-dependencies</artifactId>
                <version>${spring-boot.version}</version>
                <type>pom</type>
                <scope>import</scope>
            </dependency>
        </dependencies>
    </dependencyManagement>


</project>

2、子项目:新建发布者,生产者

pom文件:

<?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 https://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>
    <groupId>com.lv</groupId>
    <artifactId>code</artifactId>
    <version>0.0.1-SNAPSHOT</version>
    <name>provider</name>
    <description>Demo project for Spring Boot</description>
    <parent>
        <artifactId>rabbitMQ</artifactId>
        <groupId>org.example</groupId>
        <version>1.0-SNAPSHOT</version>
    </parent>

    <build>
        <plugins>
            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-compiler-plugin</artifactId>
                <version>3.8.1</version>
                <configuration>
                    <source>1.8</source>
                    <target>1.8</target>
                    <encoding>UTF-8</encoding>
                </configuration>
            </plugin>
            <plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
                <version>2.4.1</version>
                <configuration>
                    <mainClass>com.lv.code.ProviderApplication</mainClass>
                </configuration>
                <executions>
                    <execution>
                        <id>repackage</id>
                        <goals>
                            <goal>repackage</goal>
                        </goals>
                    </execution>
                </executions>
            </plugin>
        </plugins>
    </build>

</project>

3、子项目:新建发布者,消费者

  pom文件:

<?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 https://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>
    <groupId>com.lv</groupId>
    <artifactId>code</artifactId>
    <version>0.0.1-SNAPSHOT</version>
    <name>consumer</name>
    <description>Demo project for Spring Boot</description>
    <parent>
        <artifactId>rabbitMQ</artifactId>
        <groupId>org.example</groupId>
        <version>1.0-SNAPSHOT</version>
    </parent>

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

    <build>
        <plugins>
            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-compiler-plugin</artifactId>
                <version>3.8.1</version>
                <configuration>
                    <source>1.8</source>
                    <target>1.8</target>
                    <encoding>UTF-8</encoding>
                </configuration>
            </plugin>
            <plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
                <version>2.4.1</version>
                <configuration>
                    <mainClass>com.lv.code.ConsumerApplication</mainClass>
                </configuration>
                <executions>
                    <execution>
                        <id>repackage</id>
                        <goals>
                            <goal>repackage</goal>
                        </goals>
                    </execution>
                </executions>
            </plugin>
        </plugins>
    </build>

</project>

4、连接消息队列

在生产者中导入yml文件:

server:
  port: 8080
spring:
  application:
    name: xx
  rabbitmq:
    host: 192.168.218.130
    password: 123456
    port: 5672
    username: springboot
    virtual-host: my_vhost

生产者中新建文件RabbitConfig:此类与容器一起启动的时候,会产生firstQueue队列

package com.lv.code;

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

@Configuration
@SuppressWarnings("all")
public class RabbitConfig {
    @Bean
    public Queue firstQueue() {
        return new Queue("firstQueue");
    }
}

生产者中新建发送类:sender

package com.lv.code;

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

@Component
@SuppressWarnings("all")
public class Sender {

    @Autowired
    private AmqpTemplate rabbitTemplate;
        public void sendFirst() {
        rabbitTemplate.convertAndSend("firstQueue", "Hello World");
    }
}

生产者测试类运行:

package com.lv.code;

import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;

@SpringBootTest
class ProviderApplicationTests {

    @Autowired
    private Sender sender;

    @Test
    void contextLoads() {
        sender.sendFirst();
    }

}

出现一个队列:ready那显示1,表示具有一条信息没有被接收

 5、消费者接收信息

消费者内导入yml文件:

server:
  port: 8081
spring:
  application:
    name: consumer
  rabbitmq:
    host: 192.168.218.130
    password: 123456
    port: 5672
    username: springboot
    virtual-host: my_vhost

写一个接收者:Receiver

package com.lv.code;

import lombok.extern.slf4j.Slf4j;
import org.springframework.amqp.rabbit.annotation.RabbitHandler;
import org.springframework.amqp.rabbit.annotation.RabbitListener;
import org.springframework.stereotype.Component;

@Component
@SuppressWarnings("all")
@Slf4j
//类监听
@RabbitListener(queues = "firstQueue")
public class Receiver {
    @RabbitHandler
    public void process(String msg) {
        log.warn("接收到:" + msg);
    }
}

运行启动类,接收到信息:

6、完成自定义数据发送

 创建一个实体类:

package com.lv.code;

import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;

@SuppressWarnings("all")
@Data
@AllArgsConstructor
@NoArgsConstructor
public class User{
        private String username;
        private String userpwd;
    }

在发送类新增个方法:Sender

    public void sendFirst(User user) {
        rabbitTemplate.convertAndSend("firstQueue", user);
    }

测试类中的方法:

    @Test
    void contextLoads() {
        sender.sendFirst(new User("aa","bb"));
    }

生产者发送不了:简单的消息转换器只支持string类型,byte数组类型以及序列化的数据,但是user类不是序列化的。

所以给user实现接口,使它序列化:

 消费者接受不了:由于消费者接收的是string类

将user实体类导入到消费者内,并修改接收类的方法,接收对象:

 在此处发送的东西不仅字段要相同,包名也要相同,

解决方案:①、可以使用DTO来发送;

                 、或者可以将对象先转换为json数据,发到消费者后再转换为对象

我们使用第二种方案:

发送类新增方法:

    public void sendFirst(String json) {
        rabbitTemplate.convertAndSend("firstQueue", json);
    }

生产者测试类:

package com.lv.code;

import com.fasterxml.jackson.databind.ObjectMapper;
import lombok.SneakyThrows;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;

@SpringBootTest
class ProviderApplicationTests {

    @Autowired
    private Sender sender;

    @Test
    @SneakyThrows
    void contextLoads() {
        User user=new User("aa","bb");
        ObjectMapper mapper=new ObjectMapper();
        sender.sendFirst(mapper.writeValueAsString(user));
    }

}

消费者接收类:

package com.lv.code;

import com.fasterxml.jackson.databind.ObjectMapper;
import lombok.SneakyThrows;
import lombok.extern.slf4j.Slf4j;
import org.springframework.amqp.rabbit.annotation.RabbitHandler;
import org.springframework.amqp.rabbit.annotation.RabbitListener;
import org.springframework.stereotype.Component;

@Component
@SuppressWarnings("all")
@Slf4j
//类监听
@RabbitListener(queues = "firstQueue")
public class Receiver {
    @RabbitHandler
    @SneakyThrows
    public void process(String json) {
        log.warn("接收到:" + json);
        ObjectMapper mapper=new ObjectMapper();

        log.warn("接收到:" + mapper.readValue(json,User.class));
    }
}

运行接收成功:

本期内容结束~~~~~

猜你喜欢

转载自blog.csdn.net/weixin_60389087/article/details/123120255