消息队列:秒杀时如何处理每秒上万次的下单请求?
用户查询的是少量的商品数据,属于查询的热点数据,你可以采用缓存策略将请求尽量挡在上层的缓存中,能被静态化的数据(比如商城里的图片和视频数据)尽量做到静态化,这样就可以命中 CDN 节点缓存减少 Web 服务器的查询量和带宽负担。Web 服务器比如 Nginx 可以直接访问分布式缓存节点,从而避免请求到达 Tomcat 等业务服务器。
真正下单的请求只有少数的能通过,但是会绕过缓存直接跟数据库交互。将秒杀请求暂存在消息队列中,然后业务服务器会响应用户“秒杀结果正在计算中”,释放了系统资源之后再处理其它用户的请求。
这就是消息队列在秒杀系统中最主要的作用:削峰填谷;另外在使用消息队列应对流量峰值时,需要对队列处理的时间、前端写入流量的大小、数据库处理能力做好评估,然后根据不同的量级来决定部署多少台队列处理程序。
异步处理、解耦合和削峰填谷是消息队列在秒杀系统设计中起到的主要作用,其中异步处理可以简化业务流程中的步骤,提升系统性能;削峰填谷可以削去到达秒杀系统的峰值流量,让业务逻辑的处理更加缓和;解耦合可以将秒杀系统和数据系统解耦开,这样两个系统的任何变更都不会影响到另一个系统。
消息投递:如何保证消息仅仅被消费一次?
消息为什么会丢失
消息从生产者写入到消息队列的过程;消息在消息队列中的存储场景;消息被消费者消费的过程。
1.业务服务器因网络抖动未发送成功到消息队列,采用消息重传解决,但可能导致消息重复发送,从而在消费分时候重复消费同样的消息。
2.消息队列本身丢失消息。拿 Kafka 举例,消息在 Kafka 中是存储在本地磁盘上的,而为了减少消息存储时对磁盘的随机 I/O,我们一般会将消息先写入到操作系统的 Page Cache 中,然后再找合适的时机刷新到磁盘上。因断电或者机器异常重启,Page Cache还没来得及刷盘就丢失了。解决方案只能采用更复杂的集群和"acks=all"
3.消费者接受消息的时候网络抖动导致消息并没有正确地接收到。一定要等到消息接收和处理完成后才能更新消费进度,但是这也会造成消息重复的问题,比方说某一条消息在处理之后消费者恰好宕机了,那么因为没有更新消费进度,所以当这个消费者重启之后还会重复地消费这条消息。
如何保证消息只被消费一次
为了避免消息丢失我们需要付出两方面的代价:一方面是性能的损耗,一方面可能造成消息重复消费。在生产、消费过程中增加消息幂等性的保证。
在消息生产过程中,在 Kafka0.11 版本和 Pulsar 中都支持“producer idempotency”的特性,翻译过来就是生产过程的幂等性,这种特性保证消息虽然可能在生产端产生重复,但是最终在消息队列存储时只会存储一份。
它的做法是给每一个生产者一个唯一的 ID,并且为生产的每一条消息赋予一个唯一 ID,消息队列的服务端会存储 < 生产者 ID,最后一条消息 ID> 的映射。当某一个生产者产生新的消息时,消息队列服务端会比对消息 ID 是否与存储的最后一条 ID 一致,如果一致就认为是重复的消息,服务端会自动丢弃。
而在消费端,幂等性的保证会稍微复杂一些,你可以从通用层和业务层两个层面来考虑。
在通用层面,你可以在消息被生产的时候使用发号器给它生成一个全局唯一的消息 ID,消息被处理之后把这个 ID 存储在数据库中,在处理下一条消息之前先从数据库里面查询这个全局 ID 是否被消费过,如果被消费过就放弃消费。你可以看到,无论是生产端的幂等性保证方式还是消费端通用的幂等性保证方式,它们的共同特点都是为每一个消息生成一个唯一的 ID,然后在使用这个消息的时候,先比对这个 ID 是否已经存在,如果存在则认为消息已经被使用过。所以这种方式是一种标准的实现幂等的方式。
在操作数据库的时候采用乐观锁和事务保证只消费一次数据。
消息队列:如何降低消息队列系统中消息的延迟?
监控
首先,Kafka 提供了工具叫做“kafka-consumer-groups.sh”(它在 Kafka 安装包的 bin 目录下)。可以查看Kafka当前队列消息的堆积数。
Kafka 通过 JMX 暴露了消息堆积的数据,可以写代码来获取,这样也可以方便地输出到监控系统中。
减少消息延迟的正确姿势
想要减少消息的处理延迟,我们需要在消费端和消息队列两个层面来完成。但Kafka一般不能增加消费者数量达到目的。因为在 Kafka 中,一个 Topic(话题)可以配置多个 Partition(分区),数据会被平均或者按照生产者指定的方式写入到多个分区中,那么在消费的时候,Kafka 约定一个分区只能被一个消费者消费,为什么要这么设计呢?在我看来,如果有多个 consumer(消费者)可以消费一个分区的数据,那么在操作这个消费进度的时候就需要加锁,可能会对性能有一定的影响。
所以说,话题的分区数量决定了消费的并行度,增加多余的消费者也是没有用处的,你可以通过增加分区来提高消费者的处理能力。
虽然不能增加 consumer,但你可以在一个 consumer 中提升处理消息的并行度,所以可以考虑使用多线程的方式来增加处理能力:你可以预先创建一个或者多个线程池,在接收到消息之后把消息丢到线程池中来异步地处理,这样,原本串行的消费消息的流程就变成了并行的消费,可以提高消息消费的吞吐量,在并行处理的前提下,我们就可以在一次和消息队列的交互中多拉取几条数据,然后分配给多个线程来处理。
选择高性能的数据存储方式配合零拷贝技术,可以提升消息的消费性能。
标签:消费,队列,Kafka,处理,消息,设计,ID From: https://www.cnblogs.com/twh233/p/17969455