消息队列MQ用来做什么的,市场上主流的四大MQ如何选择?RabbitMQ带你HelloWorld!

news2024/12/28 19:12:14

文章目录

  • MQ用来做什么的
  • MQ会有什么样的麻烦
  • MQ消息队列模式分类
  • MQ消息队列常用协议
  • 市场主流四大MQ
  • RabbitMQ项目开发
  • RabbitMQ中的组成部分

MQ用来做什么的

省流 :系统解耦、异步调用、流量削峰

系统解耦
首先举例下面这个场景,现有ABCDE五个系统,最初的时候BCD三个系统都要调用A系统的接口获取数据,一切都很正常,但是突然,D系统说:我不要了,你不用给我传数据了,A系统无奈,只能修改代码,将调用D系统的代码删除,这时候还没删除呢,E系统发送了请求,但是A系统这时候还没处理完D系统的请求,A系统卒!!!彻底崩溃。
在这里插入图片描述
上述场景中,BCDE都需要用到A系统提供的数据,A系统跟其他四个系统严重耦合,需要时时刻刻考虑其他四个系统要是挂了怎么办,需不需要重新发送数据给他们,这个时候的A系统内心是崩溃的。

但是如果使用了MQ之后 ,A系统的数据只需要放到MQ里面,其他的系统想请求获取数据只需要去MQ里面消费即可,如果突然不想请求了,就取消对MQ的消费就行了,A系统根本不需要考虑给谁去响应这个数据,也不需要去维护代码,也不用考虑其他系统是否调用成功,失败超时等情况。
在这里插入图片描述
异步调用
ABCD四个系统,A系统收到一个请求,需要在自己本地写库,还需要往BCD三个系统写库,A系统自己写本地库需要3ms,往其他系统写库相对较慢,B系统200ms ,C系统350ms,D系统400ms,这样算起来,整个功能从请求到响应的时间为3ms+200ms+350ms+400ms=953ms,接近一秒,对于用户来说,点个按钮要等这么长时间,基本是无法接受的,侧面也反映出这家研发人员技术不咋地。
在这里插入图片描述
一般的互联网企业,对于用户请求响应的时间要求在100ms-200ms之间,这样,用户的眼睛存在视觉暂停现象,用户响应时间在此范围内就可以了,所以上面的现象是不可取的。

如果用了MQ,用户发送请求到A系统耗时3ms,A系统发送三条消息到MQ,假如耗时5ms,用户从发送请求到相应3ms+5ms=8ms,仅用了8ms,用户的体验非常好。
在这里插入图片描述
流量削峰
JD系统每天0—19点,系统风平浪静,结果一到八点抢购的时候,每秒并发达到百万,假设JD数据库没秒能处理1.5w条并发请求(并非实际数据,主要为了举例),到八点抢购的时候,每秒并发百万,这直接导致系统异常,但是八点一过,可能也就几万用户在线操作,每秒的请求可能也就几百条,对整个系统毫无压力。

如果使用了MQ,每秒百万个请求写入MQ,因为JD系统每秒能处理1W+的请求,JD系统处理完然后再去MQ里面,再拉取1W+的请求处理,每次不要超过自己能处理的最大请求量就ok,这样下来,等到八点高峰期的时候,系统也不会挂掉,但是近一个小时内,系统处理请求的速度是肯定赶不上用户的并发请求的,所以都会积压在MQ中,甚至可能积压千万条,但是高峰期过后,每秒只会有一千多的并发请求进入MQ,但是JD系统还是会以每秒1W+的速度处理请求,所以高峰期一过,JD系统会很快消化掉积压在MQ的请求,在用户那边可能也就是等的时间长一点,但是绝对不会让系统挂掉。
在这里插入图片描述

MQ会有什么样的麻烦

系统可用性降低
系统引入的外部依赖越多,系统要面对的风险越高,拿场景一来说,本来ABCD四个系统配合的好好的,没啥问题,但是你偏要弄个MQ进来插一脚,虽然好处挺多,但是万一MQ挂掉了呢,那样你系统不也就挂掉了。

系统复杂程度提高
非要加个MQ进来,如何保证没有重复消费呢?如何处理消息丢失的情况?怎么保证消息传递的顺序?问题太多。

一致性的问题
A系统处理完再传递给MQ就直接返回成功了,用户以为你这个请求成功了,但是,如果在BCD的系统里,BC两个系统写库成功,D系统写库失败了怎么办,这样就导致数据不一致了。

所以。消息队列其实是一套非常复杂的架构,你在享受MQ带来的好处的同时,也要做各种技术方案把MQ带来的一系列的问题解决掉,等一切都做好之后,系统的复杂程度硬生生提高了一个等级。

MQ消息队列模式分类

1. 点对点PTP
消息生产者生产消息发送到queue中,然后消息消费者从queue中取出并且消费消息。 消息被消费以后,queue中不再存储,所以消息消费者不可能消费到已经被消费的消息。 Queue支持存在多个消费者,但是对一个消息而言,只会有一个消费者可以消费。
在这里插入图片描述
2. 发布/订阅
topic实现了发布和订阅,当你发布一个消息,所有订阅这个topic的服务都能得到这个消息,所以从1到N个订阅者都能得到一个消息的拷贝。
在这里插入图片描述

MQ消息队列常用协议

  • AMQP协议
  • MQTT协议
  • STOMP协议
  • XMPP协议

市场主流四大MQ

kafka、ActiveMQ、RabbitMQ、RocketMQ
在这里插入图片描述

RabbitMQ项目开发

超详细RabbitMQ入门,这一篇就够了

1. 安装erLang语言,配置环境变量
管网安装下载Winsdow10版本的erlang安装包
在这里插入图片描述
下载之后就得到这个东西
在这里插入图片描述
双击安装,一直点next就行了,安装完之后,配置环境变量
在这里插入图片描述
在这里插入图片描述
使用cmd命令,输入 erl -version 验证:
在这里插入图片描述
2. 安装RabbitMQ服务器
在RabbitMQ的gitHub项目中,下载windows版本的服务端安装包
在这里插入图片描述
下载之后得到这个东西
在这里插入图片描述
接着到双击安装,一直点下一步安装即可,安装完成后,找到安装目录
在这里插入图片描述
在此目录下打开cmd命令,进入到sbin目录下,输入rabbitmq-plugins enable rabbitmq_management命令安装管理页面的插件:
在这里插入图片描述
然后双击rabbitmq-server.bat启动脚本,然后打开服务管理可以看到RabbitMQ正在运行:
在这里插入图片描述
这时,打开浏览器输入http://localhost:15672,账号密码默认是:guest/guest
在这里插入图片描述
到这一步,安装就大功告成了!
在这里插入图片描述
3. 开始HelloWorld
导入依赖

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

在application.yml文件写入配置信息

spring:
    rabbitmq:
        host: 127.0.0.1
        port: 5672
        username: guest
        password: guest

配置公共项目common
在这里插入图片描述

public class RabbitConfig{
	// RabbitMQ的队列主题名称
	public static final String RABBITMQ_DEMO_TOPIC = "rabbitmqDemoTopic";
	
	//RabbitMQ的DIRECT交换机名称
	public static final String RABBITMQ_DEMO_DIRECT_EXCHANGE = "rabbitmqDemoDirectExchange";

	//RabbitMQ的DIRECT交换机和队列绑定的匹配键DirectRouting
	public static final String RABBITMQ_DEMO_DIRECT_ROUTING = "rabbitmqDemoDirectRouting";
}

RabbitMQ中间组件的配置

@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
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<String, Object> 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";
        }
    }
}
@RestController
@RequestMapping("/mall/rabbitmq")
public class RabbitMQController {
    @Resource
    private RabbitMQService rabbitMQService;
    /**
     * 发送消息
     * @author java技术爱好者
     */
    @PostMapping("/sendMsg")
    public String sendMsg(@RequestParam(name = "msg") String msg) throws Exception {
        return rabbitMQService.sendMsg(msg);
    }
}

消费者

@Component
@RabbitListener(queues = {RabbitMQConfig.RABBITMQ_DEMO_TOPIC})
public class RabbitDemoConsumer{
	@RabbitHandler
	public void process(Map map){
		System.out.println("消费者RabbitDemoConsumer从RabbitMQ服务端消费消息:"+map.toString());
	}
}

开始运行
运行前注意先运行服务器创建队列,再运行客户端获取消息。如果顺序相反,客户端先执行就会报错。

现在开始先启动生产者,发送一条消息,
在这里插入图片描述
在这里插入图片描述
最后再启动消费者,进行消费。
在这里插入图片描述
避免没有队列
由于队列不存在,启动消费者报错的这个问题。最好的方法是生产者和消费者都尝试创建队列,怎么写呢,有很多方式,我这里用一个相对简单一点的:

//实现BeanPostProcessor类,使用Bean的生命周期函数
@Component
public class DirectRabbitConfig implements BeanPostProcessor {
    //这是创建交换机和队列用的rabbitAdmin对象
    @Resource
    private RabbitAdmin rabbitAdmin;
    
    //初始化rabbitAdmin对象
    @Bean
    public RabbitAdmin rabbitAdmin(ConnectionFactory connectionFactory) {
        RabbitAdmin rabbitAdmin = new RabbitAdmin(connectionFactory);
        // 只有设置为 true,spring 才会加载 RabbitAdmin 这个类
        rabbitAdmin.setAutoStartup(true);
        return rabbitAdmin;
    }
    
    //实例化bean后,也就是Bean的后置处理器
    @Override
    public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
        //创建交换机
        rabbitAdmin.declareExchange(rabbitmqDemoDirectExchange());
        //创建队列
        rabbitAdmin.declareQueue(rabbitmqDemoDirectQueue());
        return null;
    }
}

这样启动生产者就会自动创建交换机和队列,不用等到发送消息才创建。

消费者需要加一点代码:

@Component
//使用queuesToDeclare属性,如果不存在则会创建队列
@RabbitListener(queuesToDeclare = @Queue(RabbitMQConfig.RABBITMQ_DEMO_TOPIC))
public class RabbitDemoConsumer {
    //...省略
}

这样,无论生产者还是消费者先启动都不会出现问题了~

RabbitMQ中的组成部分

Exchange:消息队列交换机。按一定的规则将消息路由转发到某个队列。
Queue:消息队列,存储消息的队列。
Producer:消息生产者。生产方客户端将消息同交换机路由发送到队列中。
Consumer:消息消费者。消费队列中存储的消息。
在这里插入图片描述

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/403734.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

小黑子—Java从入门到入土过程:第二章

Java零基础入门2.0Java系列第二章1. 注释和关键字2. 字面量3. 变量3.1 基本用法3.2 使用方式3.3 注意事项4. 变量练习5. 计算机中的数据存储5.1 计算机的存储规则5.2 进制5.3 进制间转换二进制转十八进制转十十六进制转十十进制转其他进制6. 数据类型7. 定义变量的练习8. 标识符…

MATLAB——将直接型转化为并联型和级联型

题目1(IIR)&#xff1a; 已知一个系统的传递函数为&#xff1a; H&#xff08;z&#xff09;8−4z−111z−2−2z−31−1.25z−10.75z−2−0.125z−3H&#xff08;z&#xff09;\frac{8-4z^{-1}11z^{-2}-2z^{-3}}{1-1.25z^{-1}0.75z^{-2}-0.125z^{-3}}H&#xff08;z&#xff09…

Leedcode 1137. 第 N 个泰波那契数

泰波那契序列 Tn 定义如下&#xff1a; T0 0, T1 1, T2 1, 且在 n > 0 的条件下 Tn3 Tn Tn1 Tn2 给你整数 n&#xff0c;请返回第 n 个泰波那契数 Tn 的值。 示例 1&#xff1a; 输入&#xff1a;n 4 输出&#xff1a;4 解释&#xff1a; T_3 0 1 1 2 T_4 1 …

2.线性表的顺序表示

数据结构很重要&#xff01; 数据结构很重要&#xff01;&#xff01;&#xff01; 数据结构很重要&#xff01;&#xff01;&#xff01;&#xff01; 思考 1.线性表的顺序表示内容有哪些&#xff1f;&#xff08;What&#xff09; 2.为什么要学线性表的顺序表示? ? (Why)…

POI 操作Excel的单元格样式超过64000的异常问题解决

文章目录POI 操作Excel的单元格样式超过64000的异常问题解决问题描述问题原因问题分析和解决简单的Excel文件生成Demo最终的解决方案POI 操作Excel的单元格样式超过64000的异常问题解决 问题描述 在用POI 生成Excel文件时&#xff0c;如果自定义的单元格的样式超过64000行&am…

SpringBoot+WebSocket实时监控异常

# 写在前面此异常非彼异常&#xff0c;标题所说的异常是业务上的异常。最近做了一个需求&#xff0c;消防的设备巡检&#xff0c;如果巡检发现异常&#xff0c;通过手机端提交&#xff0c;后台的实时监控页面实时获取到该设备的信息及位置&#xff0c;然后安排员工去处理。因为…

2.9.1 Packet Tracer - Basic Switch and End Device Configuration(作业)

Packet Tracer - 交换机和终端设备的基本 配置地址分配表目标使用命令行界面 (CLI)&#xff0c;在两台思科互联网络 操作系统 (IOS) 交换机上配置主机名和 IP 地址。使用思科 IOS 命令指定或限制对设备 配置的访问。使用 IOS 命令来保存当前的运行配置。配置两台主机设备的 IP …

JavaScript Boolean(布尔)对象

Boolean&#xff08;布尔&#xff09;对象用于将非布尔值转换为布尔值&#xff08;true 或者 false&#xff09;&#xff0c;是三种包装对象&#xff1a;Number、String和Boolean中最简单的一种&#xff0c;它没有大量的实例属性和方法。在线实例检查布尔值检查布尔对象是 true…

大数据-玩转数据-mysql规范

整体图谱 正文部分 一、数据库命令规范 所有数据库对象名称必须使用小写字母并用下划线分割 所有数据库对象名称禁止使用mysql保留关键字&#xff08;如果表名中包含关键字查询时&#xff0c;需要将其用单引号括起来&#xff09; 数据库对象的命名要能做到见名识意&#xff…

【算法时间复杂度】学习记录

最近开算法课&#xff0c;开几篇文章记录一下算法的学习过程。 关于算法的重要性 学习计算机当程序员的话&#xff0c;在编程过程中是绕不开算法这个大矿山的&#xff0c;需要我们慢慢挖掘宝藏。 算法&#xff08;Algorithm&#xff09;是指用来操作数据、解决程序问题的一组…

信创和去O大潮下,Oracle OCP(1z0-082 1z0-083)的含金量有多少?(文末附录像)

我自己就考了挺多Oracle的认证&#xff0c;下面是从Oracle的certview网站&#xff08;网址是certview加上点oracle点com&#xff09;上面查询到的姚远老师自己的Oracle证书。 目前国内强调自主可控&#xff0c;推信创和去Oracle数据库&#xff0c;很多想考Oracle OCP认证的学员…

Spring框架源码分析一

如何看源码&#xff08;方法论&#xff09;不要忽略源码中的注释使用翻译工具先梳理脉络&#xff0c;然后梳理细节即总分总&#xff0c;先总体过一遍&#xff0c;再看细节&#xff0c;再做一个总结大胆猜测&#xff08;8分靠猜&#xff09;&#xff0c;小心验证&#xff0c;再调…

ChatGPT真神奇,但是也真焦虑

ChatGPT火爆ChatGPT的火爆程度不用说也知道。就目前来说&#xff0c;已经开始冲击各行业了&#xff0c;比如客服、智能助手、语言学习、自然语言处理等等等。。ChatGPT冲击冲击最高的可能就是中间这个段位的了。高段位无法取代&#xff0c;但是低段位&#xff0c;通过使用ChatG…

Linux - 第7节 - 进程间通信

1.进程间通信介绍 进程间通信目的&#xff1a; 数据传输&#xff1a;一个进程需要将它的数据发送给另一个进程 。​​​​​​​ 资源共享&#xff1a;多个进程之间共享同样的资源。 ​​​​​​​ 通知事件&#xff1a;一个进程需要向另一个或一组进程发送消息&#xff0c;通…

数据结构-用栈实现队列

前言&#xff1a; 请你仅使用两个栈实现先入先出队列。队列应当支持一般队列支持的所有操作&#xff08;push、pop、peek、empty&#xff09;&#xff1a; 实现 MyQueue 类&#xff1a; void push(int x) 将元素 x 推到队列的末尾 int pop() 从队列的开头移除并返回元素 int…

双通道5.2GSPS(或单通道10.4GSPS)射频采样FMC+模块

概述 FMC140是一款具有缓冲模拟输入的低功耗、12位、双通道&#xff08;5.2GSPS/通道&#xff09;、单通道10.4GSPS、射频采样ADC模块&#xff0c;该板卡为FMC标准&#xff0c;符合VITA57.1规范&#xff0c;该模块可以作为一个理想的IO单元耦合至FPGA前端&#xff0c;8通道的JE…

socket编程-UDP各函数及其用法

socket编程-UDP socket主要类型 流套接字&#xff08;SOCK_STREAM&#xff09; 流套接字用于提供面向连接、可靠的数据传输服务。该服务将保证数据能够实现无差错、无重复送&#xff0c;并按顺序接收。流套接字之所以能够实现可靠的数据服务&#xff0c;原因在于其使用了传输…

电商API分享:获得淘宝商品评论、商品评论问答列表 分页显示 代码展示

item_review-获得淘宝商品评论item_question_answer-淘宝评论问答列表接口请求示例<?php// 请求示例 url 默认请求参数已经URL编码处理 // 本示例代码未加密secret参数明文传输&#xff0c;若要加密请参考&#xff1a;https://open.qq1635608656.cn/help/demo/sdk/demo-sig…

关于IDEA系列行号左侧空白处太宽的缩短

IntelliJ IDEA行号左侧空白处太宽&#xff0c;缩短的方法修改之前打开设置File–Settings–Editor–General–Gutter Icons&#xff08;可以在Settings左上角搜索框直接搜Gutter Icons&#xff09;将对号去掉就好了 &#xff0c;但是这些符号今后不会显示 &#xff0c;最重要的…

【数据结构】单链表中,如何实现 将链表中所有结点的链接方向“原地”逆转

一.实现一个单链表&#xff08;无头单向不循环&#xff09; 我们首先实现一个无头单向不循环单链表。 写出基本的增删查改功能&#xff0c;以及其它的一些功能&#xff08;可忽略&#xff09;。 #include<stdio.h> #include<assert.h> #include<stdlib.h>…