消息队列MQ用来做什么的,市场上主流的四大MQ如何选择?RabbitMQ带你HelloWorld!
创始人
2024-06-01 14:43:16
0

文章目录

  • 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
导入依赖

org.springframework.bootspring-boot-starter-amqp

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

spring:rabbitmq:host: 127.0.0.1port: 5672username: guestpassword: 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交换机和队列绑定的匹配键DirectRoutingpublic static final String RABBITMQ_DEMO_DIRECT_ROUTING = "rabbitmqDemoDirectRouting";
}

RabbitMQ中间组件的配置

@Configuration
public class DirectRabbitConfig {@Beanpublic Queue rabbitmqDemoDirectQueue() {/*** 1、name:    队列名称* 2、durable: 是否持久化* 3、exclusive: 是否独享、排外的。如果设置为true,定义为排他队列。则只有创建者可以使用此队列。也就是private私有的。* 4、autoDelete: 是否自动删除。也就是临时队列。当最后一个消费者断开连接后,会自动删除。* */return new Queue(RabbitMQConfig.RABBITMQ_DEMO_TOPIC, true, false, false);}@Beanpublic DirectExchange rabbitmqDemoDirectExchange() {//Direct交换机return new DirectExchange(RabbitMQConfig.RABBITMQ_DEMO_DIRECT_EXCHANGE, true, false);}@Beanpublic 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");@Resourceprivate RabbitTemplate rabbitTemplate;@Overridepublic 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";}}
}
@RestController
@RequestMapping("/mall/rabbitmq")
public class RabbitMQController {@Resourceprivate 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{@RabbitHandlerpublic void process(Map map){System.out.println("消费者RabbitDemoConsumer从RabbitMQ服务端消费消息:"+map.toString());}
}

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

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

//实现BeanPostProcessor类,使用Bean的生命周期函数
@Component
public class DirectRabbitConfig implements BeanPostProcessor {//这是创建交换机和队列用的rabbitAdmin对象@Resourceprivate RabbitAdmin rabbitAdmin;//初始化rabbitAdmin对象@Beanpublic RabbitAdmin rabbitAdmin(ConnectionFactory connectionFactory) {RabbitAdmin rabbitAdmin = new RabbitAdmin(connectionFactory);// 只有设置为 true,spring 才会加载 RabbitAdmin 这个类rabbitAdmin.setAutoStartup(true);return rabbitAdmin;}//实例化bean后,也就是Bean的后置处理器@Overridepublic 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:消息消费者。消费队列中存储的消息。
在这里插入图片描述

相关内容

热门资讯

求宠文,男主对女主超宠,宠的没... 求宠文,男主对女主超宠,宠的没话说,宠的天理难容的那种,古代现代都可以,不求虐。我想还是4ᴨne.c...
帮我女儿起个名字,本人姓卫,妻... 帮我女儿起个名字,本人姓卫,妻姓惠,农历2007年5月初十19点40生的,在此谢谢了上面还有个姐姐:...
艾薇最终能跟拉美西斯二世一起吗... 艾薇最终能跟拉美西斯二世一起吗?当然了,他们约定:相见,亦不忘却往生结果要看作者的心情,作者不高兴就...
女主带空间穿到古代又穿回现代的... 女主带空间穿到古代又穿回现代的小说《九岁小妖后》是灵魂穿越,回了现代一次,结局又回古代了。我觉得挺好...
国足要是冲击世界杯成功 会造成... 国足要是冲击世界杯成功 会造成多大的轰动? (实话实说) 会带动多大的经济效益?估计有生之年难度较大...
零之轨迹中古战场的两个游客怎么... 零之轨迹中古战场的两个游客怎么救RPG不存在什么难度吧,路上遇一个,尽头杀完BOSS救一个
初恋的含义到底是什么呢? 初恋的含义到底是什么呢?是第一个女朋友?还是第一次爱的人??既然说是“初恋”就是初次恋爱。恋爱至少需...
你们喜欢棒棒堂还是飞轮海啊? 你们喜欢棒棒堂还是飞轮海啊?都喜欢!最喜欢棒棒堂里的王子和獒犬最喜欢飞轮海里的炎亚纶和吴尊都喜欢,更...
女生想起个笔名,要两个字,要冷... 女生想起个笔名,要两个字,要冷开头!不要那么俗的名字好不好呀~冷滢,冷清,冷冷,冷青,冷月……冷鸟,...
《魔兽世界》下个版本《熊猫人之... 《魔兽世界》下个版本《熊猫人之谜》,什么名字帅气?大家都起什么名字啊?可以考虑叫功夫熊猫里面那家伙的...
蛤蟆山怎么样 蛤蟆山怎么样-山-山在天桥附近一小山顶上,一块巨石如欲腾空飞跃的-,形象逼真,因此称之为-山。传说为...
一年级重阳节主题班会教案 一年级重阳节主题班会教案 2021年一年级重阳节主题班会教案(精选7篇)   作为一位杰出的教职工...
达芬奇画鸡蛋的故事告诉们什么道... 达芬奇画鸡蛋的故事告诉们什么道理1,孰能生巧。每个成功的背后都是要比别人花更多的努力的。2,一个物体...
为什么我点进直播间左上角却没有... 为什么我点进直播间左上角却没有邀请新用户红包?是不是新用户提交的信息有错误,这也是很有可能的,或者是...
不知道自己在写些什么的成语 不知道自己在写些什么的成语如果你想找意思是“不知道自己在写什么”的成语,可以用“不知所云”. 诸葛...
倾听自然秘语之为你“桃”醉—中... 倾听自然秘语之为你“桃”醉—中1班班本课程之十二春意浓,花开正盛,桃红梨白,各种盛放的鲜花争奇斗艳。...
道教成仙可以超出轮回吗 道教成仙可以超出轮回吗不能超越六道轮回,成仙属天道,虽寿命几万岁,但福报亨尽后,往往堕落恶道。
春眠不洗脚,处处蚊子咬,夜来巴... 春眠不洗脚,处处蚊子咬,夜来巴掌声,蚊子没有了。春眠不洗脚,处处蚊子咬,夜来巴掌声,蚊子没有了。春眠...
一个小女孩手拉一只小狗代表什么... 一个小女孩手拉一只小狗代表什么生肖小狗就是生肖狗;在十二生肖里的动物里,狗是家喻户晓的动物,狗的一生...
作文童话故事 作文童话故事暑假夏令营活动开始了,小鹿参加了一个爱心实践活动,活动回家后,它很认真地对妈妈说:“妈妈...