公司用到的很多技术,本人之前都没学过(尬),于是只能缓缓补了。这次给大家写写我学习音讯队列的笔记,心愿对大家有帮忙。
一、什么是音讯队列?
音讯队列不晓得大家看到这个词的时候,会不会感觉它是一个比拟高端的技术,反正我是感觉它如同是挺牛逼的。
音讯队列,个别咱们会简称它为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独有的数据userIdprivate 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还得好好斟酌。
最初
本文次要解说了什么是音讯队列,音讯队列能够为咱们带来什么益处,以及一个音讯队列可能会波及到哪些问题。心愿给大家带来肯定的帮忙。