在上周,我们讨论了使用消息队列的好处。然后我们回顾了消息队列产品的发展历史。如今,在项目中需要使用消息队列时,Apache Kafka似乎是首选产品。然而,考虑到特定需求时,它并不总是最佳选择。
基于数据库的队列
让我们再次使用星巴克的例子。最重要的两个需求是:
- 异步处理,使收银员可以在不等待的情况下接下一个订单。
- 持久性,以防出现问题时错过顾客的订单。
在这里,消息的顺序不太重要,因为咖啡师经常批量制作相同的饮料。可扩展性也不是很重要,因为队列受限于每个星巴克门店。
星巴克的队列可以在数据库表中实现。下面的图表显示了它的工作原理:
当收银员接受一个订单时,在数据库支持的队列中创建一个新订单。然后收银员可以继续接受另一个订单,而咖啡师则会批量获取新的订单。一旦订单完成,咖啡师会在数据库中标记为已完成。然后顾客可以在柜台上取走他们的咖啡。
每天结束时可以运行一个维护作业来删除已完成的订单(即“DONE”状态的订单)。
对于星巴克的用例,一个简单的数据库队列可以在不需要使用Kafka的情况下满足需求。具有CRUD(创建-读取-更新-删除)操作的订单表就可以胜任。
基于Redis的队列
基于数据库的消息队列仍然需要开发工作来创建队列表并从中读取/写入数据。对于预算有限且已经使用Redis进行缓存的小型创业公司,Redis也可以用作消息队列。
有三种使用Redis作为消息队列的方法:
- 发布/订阅(Pub/Sub)
- 列表(List)
- 流(Stream)
下面的图表显示了它们的工作原理。
发布/订阅是方便的,但有一些传递限制。消费者订阅一个键,当生产者向相同的键发布数据时,消费者会接收数据。限制在于数据最多只会传递一次。如果消费者关闭并且未接收到已发布的数据,则该数据会丢失。此外,数据不会持久保存在磁盘上。如果Redis宕机,所有发布/订阅数据都会丢失。发布/订阅适用于度量监视等情况,其中可以接受一些数据丢失。
Redis中的列表数据结构可以构建FIFO(先进先出)队列。消费者使用BLPOP以阻塞模式等待消息,因此应该应用超时。等待相同列表的消费者形成一个消费者组,每个消息只由一个消费者消费。作为Redis数据结构,列表可以持久保存在磁盘上。
流解决了上述两种方法的限制。消费者可以选择从何处读取消息 - 使用“$”表示新消息,“”表示特定消息ID,或使用“0-0”从开始读取消息。
总而言之,基于数据库和基于Redis的消息队列易于维护。如果它们无法满足我们的需求,专用的消息队列产品更好。接下来我们将比较两个流行的选项。
RabbitMQ vs. Kafka
对于需要可靠、可扩展和可维护的系统的大公司,在以下方面评估消息队列产品:
- 功能
- 性能
- 可扩展性
- 生态系统
下面的图表比较了两种典型的消息队列产品:RabbitMQ和Kafka。
工作原理
RabbitMQ的工作方式类似于消息中间件 - 它将消息推送给消费者,然后在确认后将其删除。这避免了消息积压,这是RabbitMQ认为有问题的。
Kafka最初是为大规模日志处理而构建的。它会保留消息直到过期,并允许消费者以自己的速度拉取消息。
语言和API
RabbitMQ是用Erlang编写的,这使得修改核心代码变得具有挑战性。然而,它提供了非常丰富的客户端API和库支持。
Kafka使用Scala和Java,但也有针对流行语言(如Python、Ruby 和Node.js)的客户端库和API。
性能和可扩展性
RabbitMQ每秒可以处理数万条消息。即使在更好的硬件上,吞吐量也不会大幅提高。
Kafka可以处理数百万条每秒的消息,并具有很高的可扩展性。
生态系统
许多现代大数据和流式应用程序默认集成了Kafka。这使得它非常适合这些用例。
消息队列用例
既然我们已经介绍了不同消息队列的特点,让我们看一些如何选择正确产品的示例。
日志处理与分析
对于具有购物车、订单和付款等服务的电子商务网站,我们需要分析日志以调查顾客订单。
下面的图表显示了使用“ELK”堆栈的典型架构:
- ElasticSearch - 为全文搜索索引日志
- LogStash - 日志收集代理
- Kibana - 用于搜索和可视化日志的用户界面
- Kafka - 分布式消息队列
图片