RabbitMQ简要入门

一、什么是消息队列

消息 指的是两个应用间传递的数据。数据的类型有很多种形式,可能只包含文本字符串,也可能包含嵌入对象。

“消息队列(Message Queue)”是在消息的传输过程中保存消息的容器 。在消息队列中,通常有生产者和消费者两个角色。生产者只负责发送数据到消息队列,谁从消息队列中取出数据处理,他不管。消费者只负责从消息队列中取出数据处理,他不管这是谁发送的数据,也不会影响发信人做其它事情。目前常见的MQ有ActiveMQ、kafka、RabbitMQ、zeroMQ、RocketMQ等。

选择MQ根据具体的场景来选:一般低吞吐量的可以选择ActiveMQ,高吞吐量和大数据量的场景可以选择RocketMQ和kafka

二、rabbitMQ

RabbitMQ 即一个消息队列,主要是用来实现应用程序的异步和解耦,同时也能起到消息缓冲,消息分发的作用。

消息中间件最主要的作用是解耦,中间件最标准的用法是生产者生产消息传送到队列,消费者从队列中拿取消息并处理,生产者不用关心是谁来消费,消费者不用关心谁在生产消息,从而达到解耦的目的。在分布式的系统中,消息队列也会被用在很多其它的方面,比如:分布式事务的支持(分布式事务问题还可以用分布式锁来解决)。

1.解耦

双11是购物狂节,用户下单后,订单系统需要通知库存系统,传统的做法就是订单系统调用库存系统的接口.

这种做法有一个缺点:

当库存系统出现故障时,订单就会失败。(这样马云将少赚好多好多钱^ ^)

订单系统和库存系统高耦合.

引入消息队列

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-hNSjR6Gt-1671455235737)(assets/image-20220228190749-d0fxww3.png)]

2.异步

如图所示。一个客户端请求发送进来,系统A会调用系统B、C、D三个系统,同步请求的话,响应时间就是系统A、B、C、D的总和,也就是800ms。如果使用MQ,系统A发送数据到MQ,然后就可以返回响应给客户端,不需要再等待系统B、C、D的响应,可以大大地提高性能 。对于一些非必要的业务,比如发送短信,发送邮件等等,就可以采用MQ。

3.削峰

如图所示。这其实是MQ一个很重要的应用。假设系统A在某一段时间请求数暴增,有5000个请求发送过来,系统A这时就会发送5000条SQL进入MySQL进行执行,MySQL对于如此庞大的请求当然处理不过来,MySQL就会崩溃,导致系统瘫痪。如果使用MQ,系统A不再是直接发送SQL到数据库,而是把数据发送到MQ,MQ短时间积压数据是可以接受的,然后由消费者每次拉取2000条进行处理,防止在请求峰值时期大量的请求直接发送到MySQL导致系统崩溃

场景:秒杀活动,一般会因为流量过大,导致应用挂掉,为了解决这个问题,一般在应用前端加入消息队列。

作用:

1.可以控制活动人数,超过此一定阀值的订单直接丢弃(我为什么秒杀一次都没有成功过呢^^)

2.可以缓解短时间的高流量压垮应用(应用程序按自己的最大处理能力获取订单)

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-HHmxeg0s-1671455235739)(file:///C:\Users\26552\AppData\Local\Temp\ksohtml7508\wps2.jpg)]

1.用户的请求,服务器收到之后,首先写入消息队列,加入消息队列长度超过最大值,则直接抛弃用户请求或跳转到错误页面.

2.秒杀业务根据消息队列中的请求信息,再做后续处理.

这里要强调一下,削峰现在基本都用redis来做了。

三、RabbitMQ的特点

RabbitMQ是一款使用Erlang语言开发的,实现AMQP(高级消息队列协议)的开源消息中间件。首先要知道一些RabbitMQ的特点,官网可查:

  • 可靠性。支持持久化,传输确认,发布确认等保证了MQ的可靠性。
  • 灵活的分发消息策略。这应该是RabbitMQ的一大特点。在消息进入MQ前由Exchange(交换机)进行路由消息。分发消息策略有:简单模式、工作队列模式、发布订阅模式、路由模式、通配符模式。
  • 支持集群。多台RabbitMQ服务器可以组成一个集群,形成一个逻辑Broker。
  • 多种协议。RabbitMQ支持多种消息队列协议,比如 STOMP、MQTT 等等。
  • 支持多种语言客户端。RabbitMQ几乎支持所有常用编程语言,包括 Java、.NET、Ruby 等等。
  • 可视化管理界面。RabbitMQ提供了一个易用的用户界面,使得用户可以监控和管理消息 Broker。
  • 插件机制。RabbitMQ提供了许多插件,可以通过插件进行扩展,也可以编写自己的插件。

四、RabbitMQ初の体验

4.1 安装RabbitMQ (Win10系统)

由于只是学习需要,所以安装在win10系统,就懒得开虚拟机。如果用Linux系统安装的话,我建议用Docker拉一个RabbitMQ的镜像下来,这样会方便一点。

4.1.1 安装erLang语言,配置环境变量

首先到erlang官网下载win10版安装包。

下载完之后,就得到这个东西:

接着双击安装,一直点next(下一步)就行了,安装完之后,配置环境变量。

4.1.2 安装RabbitMQ服务端

在RabbitMQ的gitHub项目中,下载window版本的服务端安装包。

下载后,就得到这个东西:

接着到双击安装,一直点下一步安装即可,安装完成后,找到安装路径,配好这两个环境变量

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-ZpQ9ZBJI-1671455235739)(assets/image-20220228191550-v9ngoh3.png)]

此处注意,设计到环境变量的路径不要带中文,不要带中文,不要带中文!

我安装的这个3.7版本安装后默认是开启的,需要先运行RabbitMQ Service - start命令关闭,找不到该命令的话直接搜索即可.

然后在RabbitMQ目录下的sbin目录下打开cmd命令,输入rabbitmq-plugins enable rabbitmq_management命令安装管理页面的插件

然后双击rabbitmq-server.bat启动脚本,然后打开服务管理可以看到RabbitMQ正在运行:

这时,打开浏览器输入http://localhost:15672,账号密码默认是:guest/guest

到这一步,安装就大功告成了!

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-Cm6VKlQg-1671455235740)(assets/image-20220228192336-0cukndh.png)]

然后添加用户信息,设置权限为管理员权限,注意,添加后并没有完

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-fT2d1g47-1671455235741)(assets/image-20220228192610-l7lvc9d.png)]

点击用户名

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-01Kw2PQR-1671455235742)(assets/image-20220228192638-nbpvhuy.png)]

然后set一下才算完

五,Hello Word

服务端搭建好了之后肯定要用客户端去操作,接下来就用Java做一个简单的HelloWord演示。

创建一个子模块:Provider

因为我用的是SpringBoot, 加入对应的starter依赖即可:


    org.springframework.boot
    spring-boot-starter-amqp

首先在application.yml文件加上RabbitMQ的配置信息:

spring:
    rabbitmq:
        host: 127.0.0.1
        port: 5672  #应该是15672,省略掉1
        username: user
        password: user

创建一个公共模块,里面创建一个公共配置类。共享一些配置,比如队列主题,交换机名称,路由匹配键名称等等。

* 公共配置类
 */
public class config {

    /**
     * RabbitMQ的列队主题名称
     */
    public static final String EABBITMQ_DEMO_TOPIC = "rabbitmqDemoTopic";

    /**
     * RabbitMQ的交换机名称
     */
    public static final String EABBITMQ_DEMO_TOPIC_EXCHANGE = "rabbitmqDemoTopicExchange";

    /**
     * RabbitMQ的交换机和列队绑定的匹配键
     */
    public static final String EABBITMQ_DEMO_TOPIC_ROUTING = "rabbitmqDemoTopicRouting";

在生产者和消费者模块中导入公共模块的坐标

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-gjuVAubo-1671455235743)(assets/image-20220228201904-xqlz47e.png)]

在生产者模块中导入公共模块的maven依赖,然后创建一个Direct交换机以及队列的配置类:

package com.jz.rabbitTest;

import com.jz.config.RabbitMQConfig;
import org.springframework.amqp.core.Binding;
import org.springframework.amqp.core.BindingBuilder;
import org.springframework.amqp.core.DirectExchange;
import org.springframework.amqp.core.Queue;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

//import java.util.Queue;

/**
 * Create by longxiaogao on 2022/2/28 20:21
 */
@Configuration
public class DirectRabbitConfig {
    @Bean
    public Queue rabbitmqDemoDirectQueue() {
        /**
         * 1、name:    队列名称
         * 2、durable: 是否持久化
         * 3、exclusive: 是否独享、排外的。如果设置为true,定义为排他队列。则只有创建者可以使用此队列。也就是private私有的。
         * 4、autoDelete: 是否自动删除。也就是临时队列。当最后一个消费者断开连接后,会自动删除。
         * */
        return new Queue(RabbitMQConfig.RABBITMQ_DEMO_TOPIC, true, false, false);
    }

    @Bean
    public DirectExchange rabbitmqDemoDirectExchange() {
        //Direct交换机
        return new DirectExchange(RabbitMQConfig.RABBITMQ_DEMO_DIRECT_EXCHANGE, true, false);
    }

    @Bean
    public Binding bindDirect() {
        //链式写法,绑定交换机和队列,并设置匹配键
        return BindingBuilder
                //绑定队列
                .bind(rabbitmqDemoDirectQueue())
                //到交换机
                .to(rabbitmqDemoDirectExchange())
                //并设置匹配键
                .with(RabbitMQConfig.RABBITMQ_DEMO_DIRECT_ROUTING);
    }
}

然后再创建一个发送消息的Service类:

@Service
public class RabbitMQServiceImpl implements RabbitMQService {
    //日期格式化
    private static SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");

    @Resource
    private RabbitTemplate rabbitTemplate;

    @Override
    public String sendMsg(String msg) throws Exception {
        try {
            String msgId = UUID.randomUUID().toString().replace("-", "").substring(0, 32);
            String sendTime = sdf.format(new Date());
            Map map = new HashMap<>();
            map.put("msgId", msgId);
            map.put("sendTime", sendTime);
            map.put("msg", msg);
            rabbitTemplate.convertAndSend(RabbitMQConfig.RABBITMQ_DEMO_DIRECT_EXCHANGE, RabbitMQConfig.RABBITMQ_DEMO_DIRECT_ROUTING, map);
            return "ok";
        } catch (Exception e) {
            e.printStackTrace();
            return "error";
        }
    }
}

然后根据业务放在需要用的地方,比如定时任务,或者接口。我这里就简单一点使用Controller层进行发送:

@RestController
@RequestMapping("/mall/rabbitmq")
public class RabbitMQController {
    @Resource
    private RabbitMQService rabbitMQService;
    /**
     * 发送消息
     * 
     */
    @PostMapping("/sendMsg")
    public String sendMsg(@RequestParam(name = "msg") String msg) throws Exception {
        return rabbitMQService.sendMsg(msg);
    }
}

生产者写完之后,就写消费者端的代码,消费者很简单。maven依赖,yml文件配置和生产者一样。只需要创建一个类,@RabbitListener注解写上监听队列的名称,如图所示:

@Component
@RabbitListener(queues = {RabbitMQConfig.RABBITMQ_DEMO_TOPIC})  //队列名称,可以写多个
public class ConsumerRabbitMQ {

    @RabbitHandler
    public void process(Map map) {
        System.out.println("消费者ConsumerRabbitMQ从RabbitMQ服务消费:" + map.toString());
    }
}

然后启动两个项目,然后调用生产者中的接口

这时候就会持续监听队列的消息,只要生产者发送一条消息到MQ,消费者就消费一条。

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-GPmW5lGO-1671455235744)(assets/image-20220228205529-q1x19ec.png)]

此处注意:如果往消息队列中传输的是对象的话对象实体类必须序列化,要不然会报错

反向操作也是同理,继续定义一个队列名称,这里简写为key,类型为string

 @RabbitHandler
    public void process2(UserVo user) {


        System.out.println("消费者ConsumerRabbitMQ从RabbitMQ服务消费:" + user.toString());

     //        此处一系列的逻辑处理
     rabbitTemplate.convertAndSend("key","value");
     System.out.println("运行完毕!");

    }

然后在生产者模块中写一个监听方法

@RabbitListener(queuesToDeclare =@Queue("key"))  //队列名称,可以写多个
    public void jieShou(String value){

        System.out.println("接受到了对方的处理结果!" + value);
    }


此时的运行流程是:调用生产者接口>>向RabbitMQ发送数据>>生产者接口运行完毕>>消费者接收到数据运行监听方法>>

消费者向RabbitMQ发送数据>>消费者监听方法运行完毕>>生产者监听方法接收到数据并且运行>>运行完毕

你可能感兴趣的:(java-rabbitmq,rabbitmq,中间件)