消息队列为何如此重要?

大家好,我是【架构摆渡人】,一只十年的程序猿。这是消息队列的第一篇文章,这个系列会给大家分享很多在实际工作中有用的经验,如果有收获,还请分享给更多的朋友。

不知道大家平时是否有使用过Queue相关的类,比如ArrayBlockingQueue,DelayQueue等队列。如果你说你平时写业务代码都没用过这些,其实也很正常,但是你其实间接都使用过。

比如线程池,这个大家肯定都用过,那么你想象下,如果你一直往线程池里面丢任务,当任务丢不进去之后会触发拒绝策略。但是前期的这个任务都是在排队等待执行,那这些任务暂存在哪里呢?这个暂存的容器就是Queue。

我们通过ThreadPoolExecutor的构造函数就可以看出是否使用了Queue,代码如下:

 public ThreadPoolExecutor(int corePoolSize,
                              int maximumPoolSize,
                              long keepAliveTime,
                              TimeUnit unit,
                              BlockingQueue<Runnable> workQueue,
                              RejectedExecutionHandler handler) {
        this(corePoolSize, maximumPoolSize, keepAliveTime, unit, workQueue,
             Executors.defaultThreadFactory(), handler);
}
复制代码

可以看出,Queue的作用就是存储。但是为什么大家在业务开发中很少直接使用Java中的Queue,那是因为业务代码都涉及到数据的存储,而Queue这些的数据都是在内存中,没有持久化,所以能用的场景比较有限。

虽然没有用到这些Queue,但是我们却经常用到一些开源的消息中间件,因为这些中间件都支持了持久化,并且功能更强大,但其实本质上他们都是消息队列,都是数据暂存的容器。

那么消息队列为何如此重要呢?我觉得主要就是可以将系统进行解耦同时能够异步处理,提升吞吐量。

系统间的解耦

假设你做的电商的业务,下单后需要给用户发短信,需要移除购物车内的数据等等操作。按正常逻辑那就是在下单逻辑中调用购物车和短信服务的接口,那么此时就相当于直接依赖了短信和购物车。

假设购物车服务挂掉了,那么下单时调用移除接口必然报错。报错要处理还是不处理呢?从业务上来说这个报错肯定不能影响下单对吧,但是这样就会导致下单后,购物车的数据还存在。那么必然要对异常的请求进行处理,比如记个日志,然后人工或者程序进行后置的操作。这样就麻烦了呀,而且将整个逻辑的复杂度提高了。

如果我们引入了消息队列,那这件事情就简单多了。直接将要处理的动作,通过消息的形式告诉消息队列,只要告诉就完了,然后就可以告诉用户下单成功了。短信服务和购物车服务会去消费消息队列的消息,然后去执行对应的业务逻辑。我在下图中对于投递消息用的是虚线,目的是告诉大家,这块就解耦了。

假设购物车服务有几分钟挂了,那么等它重启正常后就会继续消费消息,然后把对应的数据移除,这里是最终一致性。

异步处理,提升吞吐量

这里就不贴图了,大家一看上面那张图就知道了。当下单完成后,消息投递之后,就立马返回了。而投递消息会非常快,因为不涉及到业务逻辑的处理。所以我们通过消息的形式,将一些不需要立马获得返回值的业务场景,进行了异步处理。同时下单的响应时间变短了,吞吐量自然就上来了。

总结

消息队列之所以在面试过程中必问,也是因为它是我们工作中不可缺少的一款中间件。正因为它的重要性,我们才更要去学习,要了解它的功能特点和使用场景,这样才能在工作中去解决具体的问题。

原创:架构摆渡人(公众号ID:jiagoubaiduren),欢迎分享,转载请保留出处。

本文已收录至学习网站 cxytiandi.com/ ,里面有Spring Boot, Spring Cloud,分库分表,微服务,面试等相关内容。

猜你喜欢

转载自juejin.im/post/7087399340913721352