关于后端:什么是消息队列

3次阅读

共计 3911 个字符,预计需要花费 10 分钟才能阅读完成。

公司用到的很多技术,本人之前都没学过 (尬),于是只能缓缓补了。这次给大家写写我学习音讯队列的笔记,心愿对大家有帮忙。
一、什么是音讯队列?
音讯队列不晓得大家看到这个词的时候,会不会感觉它是一个比拟高端的技术,反正我是感觉它如同是挺牛逼的。

音讯队列,个别咱们会简称它为 MQ(Message Queue),嗯,就是很直白的简写。

咱们先不论音讯 (Message) 这个词,来看看队列(Queue)。这一看,队列大家应该都相熟吧。

队列是一种先进先出的数据结构。

在 Java 里边,曾经实现了不少的队列了:

那为什么还须要音讯队列 (MQ) 这种中间件呢???其实这个问题,跟之前我学 Redis 的时候很像。Redis 是一个以 key-value 模式存储的内存数据库,明明咱们能够应用相似 HashMap 这种实现类就能够达到相似的成果了,那还为什么要 Redis?《Redis 合集》

到这里,大家能够先猜猜为什么要用音讯队列 (MQ) 这种中间件,上面会持续补充。

音讯队列能够简略了解为:把要传输的数据放在队列中。

)
科普:

把数据放到音讯队列叫做生产者
从音讯队列里边取数据叫做消费者

二、为什么要用音讯队列?
为什么要用音讯队列,也就是在问:用了音讯队列有什么益处。咱们看看以下的场景
2.1 解耦
当初我有一个零碎 A,零碎 A 能够产生一个 userId

而后,当初有零碎 B 和零碎 C 都须要这个 userId 去做相干的操作

写成伪代码可能是这样的:
public class SystemA {

// 零碎 B 和零碎 C 的依赖
SystemB systemB = new SystemB();
SystemC systemC = new SystemC();

// 零碎 A 独有的数据 userId
private String userId = "Java3y";

public void doSomething() {

    // 零碎 B 和零碎 C 都须要拿着零碎 A 的 userId 去操作其余的事
    systemB.SystemBNeed2do(userId);
    systemC.SystemCNeed2do(userId);
    
}

}
复制代码结构图如下:

ok,所有安然无恙度过了几个天。
某一天,零碎 B 的负责人通知零碎 A 的负责人,当初零碎 B 的 SystemBNeed2do(String userId)这个接口不再应用了,让零碎 A 别去调它了。
于是,零碎 A 的负责人说 ” 好的,那我就不调用你了。”,于是就把调用零碎 B 接口的代码给删掉了:
public void doSomething() {

// 零碎 A 不再调用零碎 B 的接口了
//systemB.SystemBNeed2do(userId);
systemC.SystemCNeed2do(userId);

}
复制代码又过了几天,零碎 D 的负责人接了个需要,也须要用到零碎 A 的 userId,于是就跑去跟零碎 A 的负责人说:” 老哥,我要用到你的 userId,你调一下我的接口吧 ”
于是零碎 A 说:” 没问题的,这就搞 ”

而后,零碎 A 的代码如下:
public class SystemA {

// 曾经不再须要零碎 B 的依赖了
// SystemB systemB = new SystemB();

// 零碎 C 和零碎 D 的依赖
SystemC systemC = new SystemC();
SystemD systemD = new SystemD();

// 零碎 A 独有的数据
private String userId = "Java3y";

public void doSomething() {

   
    // 曾经不再须要零碎 B 的依赖了
    //systemB.SystemBNeed2do(userId);

    // 零碎 C 和零碎 D 都须要拿着零碎 A 的 userId 去操作其余的事
    systemC.SystemCNeed2do(userId);
    systemD.SystemDNeed2do(userId);

}

}
复制代码工夫飞逝:

又过了几天,零碎 E 的负责人过去了,通知零碎 A,须要 userId。
又过了几天,零碎 B 的负责人过去了,通知零碎 A,还是从新掉那个接口吧。
又过了几天,零碎 F 的负责人过去了,通知零碎 A,须要 userId。
……

于是零碎 A 的负责人,每天都被这给骚扰着,改来改去,改来改去 …….
还有另外一个问题,调用零碎 C 的时候,如果零碎 C 挂了,零碎 A 还得想方法解决。如果调用零碎 D 时,因为网络提早,申请超时了,那零碎 A 是反馈 fail 还是重试??
最初,零碎 A 的负责人,感觉隔一段时间就改来改去,没意思,于是就跑路了。
而后,公司招来一个大佬,大佬通过几天相熟,上来就说:将零碎 A 的 userId 写到音讯队列中,这样零碎 A 就不必常常改变了。为什么呢?上面咱们来一起看看:

零碎 A 将 userId 写到音讯队列中,零碎 C 和零碎 D 从音讯队列中拿数据。这样有什么益处?

零碎 A 只负责把数据写到队列中,谁想要或不想要这个数据(音讯),零碎 A 一点都不关怀。

即使当初零碎 D 不想要 userId 这个数据了,零碎 B 又忽然想要 userId 这个数据了,都跟零碎 A 无关,零碎 A 一点代码都不必改。

零碎 D 拿 userId 不再通过零碎 A,而是从音讯队列里边拿。零碎 D 即使挂了或者申请超时,都跟零碎 A 无关,只跟音讯队列无关。

这样一来,零碎 A 与零碎 B、C、D 都解耦了。
2.2 异步
咱们再来看看上面这种状况:零碎 A 还是间接调用零碎 B、C、D

代码如下:
public class SystemA {

SystemB systemB = new SystemB();
SystemC systemC = new SystemC();
SystemD systemD = new SystemD();

// 零碎 A 独有的数据
private String userId ;

public void doOrder() {
 
    // 下订单
      userId = this.order();
    // 如果下单胜利,则安顿其余零碎做一些事  
    systemB.SystemBNeed2do(userId);
    systemC.SystemCNeed2do(userId);
    systemD.SystemDNeed2do(userId);

}

}

复制代码假如零碎 A 运算出 userId 具体的值须要 50ms,调用零碎 B 的接口须要 300ms,调用零碎 C 的接口须要 300ms,调用零碎 D 的接口须要 300ms。那么这次申请就须要 50+300+300+300=950ms
并且咱们得悉,零碎 A 做的是次要的业务,而零碎 B、C、D 是非次要的业务。比方零碎 A 解决的是订单下单,而零碎 B 是订单下单胜利了,那发送一条短信通知具体的用户此订单已胜利,而零碎 C 和零碎 D 也是解决一些小事而已。
那么此时,为了进步用户体验和吞吐量,其实能够异步地调用零碎 B、C、D 的接口。所以,咱们能够弄成是这样的:

零碎 A 执行完了当前,将 userId 写到音讯队列中,而后就间接返回了(至于其余的操作,则异步解决)。

原本整个申请须要用 950ms(同步)
当初将调用其余零碎接口异步化,只须要 100ms(异步)

(例子可能举得不太好,但我感觉阐明到点子上就行了,见谅。)
2.3 削峰 / 限流
咱们再来一个场景,当初咱们每个月要搞一次大促,大促期间的并发可能会很高的,比方每秒 3000 个申请。假如咱们当初有两台机器解决申请,并且每台机器只能每次解决 1000 个申请。

那多进去的 1000 个申请,可能就把咱们整个零碎给搞崩了 … 所以,有一种方法,咱们能够写到音讯队列中:

零碎 B 和零碎 C 依据本人的可能解决的申请数去音讯队列中拿数据,这样即使有每秒有 8000 个申请,那只是把申请放在音讯队列中,去拿音讯队列的音讯由零碎本人去管制,这样就不会把整个零碎给搞崩。
三、应用音讯队列有什么问题?
通过咱们下面的场景,咱们曾经能够发现,音讯队列能做的事其实还是蛮多的。
说到这里,咱们先回到文章的结尾,” 明明 JDK 曾经有不少的队列实现了,咱们还须要音讯队列中间件呢?” 其实很简略,JDK 实现的队列品种尽管有很多种,然而都是简略的内存队列。为什么我说 JDK 是简略的内存队列呢?上面咱们来看看要实现音讯队列 (中间件) 可能要思考什么问题。
3.1 高可用
无论是咱们应用音讯队列来做解耦、异步还是削峰,音讯队列必定不能是单机的。试着想一下,如果是单机的音讯队列,万一这台机器挂了,那咱们整个零碎简直就是不可用了。

所以,当咱们我的项目中应用音讯队列,都是得集群 / 分布式的。要做集群 / 分布式就必然心愿该音讯队列可能提供现成的反对,而不是本人写代码手动去实现。
3.2 数据失落问题
咱们将数据写到音讯队列上,零碎 B 和 C 还没来得及取音讯队列的数据,就挂掉了。如果没有做任何的措施,咱们的数据就丢了。

学过 Redis 的都晓得,Redis 能够将数据长久化磁盘上,万一 Redis 挂了,还能从磁盘从将数据恢复过去。同样地,音讯队列中的数据也须要存在别的中央,这样才尽可能减少数据的失落。
那存在哪呢?

磁盘?
数据库?
Redis?
分布式文件系统?

同步存储还是异步存储?
3.3 消费者怎么失去音讯队列的数据?
消费者怎么从音讯队列里边失去数据?有两种方法:

生产者将数据放到音讯队列中,音讯队列有数据了,被动叫消费者去拿 (俗称 push)
消费者一直去轮训音讯队列,看看有没有新的数据,如果有就生产(俗称 pull)

3.4 其余
除了这些,咱们在应用的时候还得思考各种的问题:

音讯反复生产了怎么办啊?
我想保障音讯是相对有程序的怎么做?
……..

尽管音讯队列给咱们带来了那么多的益处,但同时咱们发现引入音讯队列也会进步零碎的复杂性。市面上当初曾经有不少音讯队列轮子了,每种音讯队列都有本人的特点,选取哪种 MQ 还得好好斟酌。
最初
本文次要解说了什么是音讯队列,音讯队列能够为咱们带来什么益处,以及一个音讯队列可能会波及到哪些问题。心愿给大家带来肯定的帮忙。

正文完
 0