消息队列架构范式
消息队列是一种常见的分布式系统中用于解耦和异步通信的技术。消息队列的架构范式主要有以下几种:
- 点对点模式(Point-to-Point,简称 P2P):在点对点模式中,消息被发送到一个队列中,并且只有一个消费者可以从队列中接收和处理消息。当消费者处理完消息后,消息会被从队列中删除。这种模式适合于任务分发和负载均衡等场景。
- 发布/订阅模式(Publish/Subscribe,简称 Pub/Sub):在发布/订阅模式中,消息被发送到一个主题中,并且可以有多个消费者订阅这个主题并接收消息。这种模式适合于广播和通知等场景。
- 请求/响应模式(Request/Response,简称 Req/Res):在请求/响应模式中,客户端发送请求消息到队列中,服务端接收到请求消息后进行处理,并将响应消息发送回队列中,客户端再从队列中获取响应消息。这种模式适合于RPC调用等场景。
- 推拉模式(Push/Pull):在推拉模式中,消息生产者将消息推送到队列中,消息消费者从队列中拉取消息并进行处理。这种模式适合于异步处理和并发控制等场景。
以上是消息队列的几种架构范式,不同的场景可以选择不同的模式来实现异步通信和解耦
RabbitMQ支持多种消息队列模式,包括:
- 点对点模式(Point-to-Point,简称 P2P)。
- 发布/订阅模式(Publish/Subscribe,简称 Pub/Sub)。
- 工作队列模式(Work Queues,也称为任务队列模式)。
- 路由模式(Routing)。
- 主题模式(Topics)。
除此之外,RabbitMQ还支持一些高级特性,例如:
- 队列持久化(Queue Durability):确保即使在RabbitMQ宕机后,队列中的消息也不会丢失。
- 消息确认(Message Acknowledgments):确保消息被消费者成功处理后才将其从队列中删除。
- 消息优先级(Message Priorities):允许开发者指定消息的优先级,以确保高优先级消息能够更快地被处理。
- 消息过期(Message Expiration):允许开发者指定消息的过期时间,以确保过期的消息不会被消费者处理。
- 镜像队列(Mirrored Queues):允许在多个RabbitMQ节点之间复制队列,以提高系统的可靠性和容错性。
开发者可以根据实际需求选择合适的消息队列模式和特性,以构建可靠、高效的消息传递系统。
下面我会详细讲解 RabbitMQ 支持的各种消息队列模式。
- 点对点模式(Point-to-Point,简称 P2P)
点对点模式是最简单的消息队列模式之一。在这种模式下,一个生产者向一个队列发送消息,一个消费者从队列中获取消息并处理。每个消息只会被一个消费者处理。
举个例子,一个电商平台的订单系统可以使用点对点模式,当用户下单时,订单系统会将订单信息发送到一个队列中,然后配送系统会从队列中获取这些订单信息并处理。由于每个订单只会被一个配送员处理,因此可以避免多个配送员同时处理同一个订单的问题。
- 发布/订阅模式(Publish/Subscribe,简称 Pub/Sub)
发布/订阅模式是一种广播式的消息队列模式。在这种模式下,一个生产者向一个交换机(Exchange)发送消息,多个消费者通过订阅队列获取这些消息。每个消息会被交换机转发给所有与之绑定的队列。
举个例子,一个新闻网站的推送系统可以使用发布/订阅模式。当一篇新闻发布时,推送系统会将新闻信息发送到一个交换机中,所有订阅了该交换机的用户都可以接收到这篇新闻。
- 工作队列模式(Work Queues,也称为任务队列模式)
工作队列模式是一种竞争式的消息队列模式。在这种模式下,一个生产者向一个队列发送消息,多个消费者从队列中获取这些消息并处理。每个消息只会被一个消费者处理。
和点对点模式不同的是,工作队列模式允许多个消费者并发处理消息。当一个消息被发送到队列中时,它会被发送到一个空闲的消费者进行处理。如果所有消费者都在忙碌状态,那么该消息会被缓存到队列中,等待消费者空闲后再进行处理。
举个例子,一个邮件系统可以使用工作队列模式,当用户发送一封邮件时,邮件系统会将邮件信息发送到一个队列中,然后多个工作线程从队列中获取邮件信息并发送邮件。由于邮件发送可能需要一些时间,因此可以使用工作队列模式来实现异步发送邮件的功能。
- 路由模式(Routing)
路由模式是一种根据路由键(Routing Key)来进行消息路由的消息队列模式。在这种模式下,一个生产者向一个交换机发送消息,并指定一个路由键,交换机会将消息路由到与之绑定的队列中。
和发布/订阅模式路由模式和发布/订阅模式不同的是,路由模式可以根据消息的路由键将消息路由到特定的队列中,而不是广播给所有队列。
举个例子,一个物流系统可以使用路由模式,当用户下单时,订单系统会将订单信息发送到一个交换机中,并指定一个路由键,例如订单的目的地。然后物流系统会根据订单的目的地将订单信息路由到特定的队列中,例如北京配送队列、上海配送队列等等。这样可以提高配送效率,减少物流成本。
- 主题模式(Topic)
主题模式是一种根据主题(Topic)来进行消息路由的消息队列模式。在这种模式下,一个生产者向一个交换机发送消息,并指定一个主题,交换机会将消息路由到与之匹配的队列中。主题可以包含一个或多个单词,用点号(.)进行分隔。
举个例子,一个新闻订阅系统可以使用主题模式,当用户订阅某个主题时,例如“体育.足球”,订阅系统会将订阅信息发送到一个交换机中,并指定订阅主题为“体育.足球”。然后推送系统会将符合订阅主题的新闻信息路由到特定的队列中,例如“体育.足球新闻队列”,让订阅该主题的用户可以接收到相应的新闻推送。
以上就是 RabbitMQ 支持的各种消息队列模式。在实际应用中,可以根据具体的业务需求选择合适的模式,来实现消息的异步传递、解耦、削峰填谷等功能。
在主题模式中,消息的路由键和队列的绑定键都是主题(Topic),可以包含一个或多个单词,用点号(.)进行分隔。
主题模式中的路由键和绑定键都可以使用通配符进行匹配:
- *(星号):匹配一个单词
在绑定键中,可以使用 (星号)来匹配一个单词,例如绑定键为“.orange.*”,可以匹配的路由键为“quick.orange.rabbit”、“lazy.orange.elephant”等等,但不能匹配“quick.orange.male.rabbit”、“lazy.orange.male.elephant”等路由键。
- #(井号):匹配一个或多个单词
在绑定键中,可以使用 #(井号)来匹配一个或多个单词,例如绑定键为“lazy.#”,可以匹配的路由键为“lazy.brown.fox”、“lazy.orange.elephant”、“lazy”等等,但不能匹配“quick.orange.rabbit”等路由键。
在主题模式中,路由键的匹配规则如下:
- 路由键中的单词和绑定键中的单词相同,则匹配成功
- 绑定键中使用 *(星号)匹配一个单词,则匹配成功
- 绑定键中使用 #(井号)匹配一个或多个单词,则匹配成功
例如,一个绑定键为“.orange.”的队列,可以匹配路由键为“quick.orange.rabbit”、“lazy.orange.elephant”等等,但不能匹配“quick.brown.fox”、“lazy.orange.male.rabbit”等路由键。而一个绑定键为“lazy.#”的队列,可以匹配路由键为“lazy.brown.fox”、“lazy.orange.elephant”、“lazy”等等,但不能匹配“quick.brown.fox”、“lazy.orange.male.rabbit”等路由键。
总之,主题模式可以灵活地根据主题进行消息的路由,通过使用通配符来匹配不同的路由键和绑定键,可以实现更加精细的消息过滤和路由。
路由模式和主题模式都是 RabbitMQ 支持的消息队列模式,它们的区别主要在于消息的路由方式和匹配规则不同。
- 路由模式
路由模式中,生产者将消息发送到一个交换机中,交换机根据消息的路由键将消息路由到特定的队列中。路由键是一个简单的字符串,队列和交换机之间的绑定关系是固定的。
路由模式主要适用于消息需要被路由到特定队列的场景,例如物流系统中根据订单目的地将订单路由到特定的队列中。
- 主题模式
主题模式中,生产者将消息发送到一个交换机中,交换机根据消息的主题(Topic)将消息路由到特定的队列中。主题可以包含一个或多个单词,用点号(.)进行分隔。队列和交换机之间的绑定关系可以使用通配符进行匹配。
主题模式主要适用于消息需要根据主题进行过滤和路由的场景,例如新闻订阅系统中根据订阅主题将新闻路由到特定的队列中。
综上所述,路由模式和主题模式都可以根据不同的需求来选择使用。路由模式适合需要将消息路由到特定队列的场景,主题模式适合需要根据主题进行过滤和路由的场景。
在路由模式中,队列和交换机之间的绑定关系是固定的,生产者将消息发送到交换机中,交换机根据消息的路由键将消息路由到特定的队列中。在这个示例中,我们使用了 direct 类型的交换机,并使用 queueBind 方法将队列和交换机进行绑定。绑定关系是一对一的,即一个队列只能绑定到一个交换机中,而一个交换机也只能将消息路由到一个队列中。这与主题模式有所不同,主题模式中使用的是 topic 类型的交换机,并支持使用通配符进行匹配。
在 RabbitMQ 中,常见的交换机类型有以下四种:
- 直接交换机(Direct Exchange):根据消息的路由键(Routing Key)将消息路由到指定的队列中。消息传递时需要一个完全匹配的路由键,只有路由键与绑定时的路由键完全匹配时,消息才会被路由到对应的队列中。
- 主题交换机(Topic Exchange):与直接交换机类似,但是支持使用通配符进行匹配,可以匹配多个路由键。其中,通配符 * 可以代替一个单词,# 可以代替零个或多个单词。
- 头部交换机(Headers Exchange):根据消息的头部属性(Headers)来将消息路由到指定的队列中,与路由键和绑定键无关。可以将消息的头部属性与绑定时指定的一组键值对进行匹配,匹配成功时,消息会被路由到对应的队列中。
- 扇形交换机(Fanout Exchange):将消息路由到所有与之绑定的队列中。这种交换机通常用于广播消息,无需指定路由键,消息会被发送到所有与之绑定的队列中。、
虽然消息队列是一个非常有用的工具,但是它也有一些缺点:
- 复杂性:消息队列是一个复杂的系统,需要设计和管理。在设置和管理消息队列时,需要考虑一些问题,例如消息的持久化、失败处理和重试等。
- 异步:消息队列是一种异步通信机制,生产者和消费者之间的通信存在延迟,生产者无法立即得到响应,而消费者也无法立即得到消息。
- 可靠性:在消息队列中,消息可能会丢失或者重复传递。即使消息被成功传递到队列中,也不能保证它一定会被成功消费。为了保证可靠性,需要在消息队列的设计和实现中采取一些措施,例如消息的持久化和重试机制等。
- 一致性:在分布式系统中使用消息队列时,需要考虑一致性问题。例如,在使用消息队列进行分布式事务时,需要确保所有的操作都能够正确地执行,否则可能会导致数据不一致。
- 高可用性:在生产环境中,消息队列需要保证高可用性。如果消息队列系统发生故障,将会影响整个系统的稳定性和可用性。
总之,消息队列需要谨慎地使用,需要考虑它的优点和缺点,并根据具体的需求来选择合适的方案。
如果在异步发送消息给客户端说上传成功后,上传任务在后续发生了失败,我们需要采取一些措施来处理这种情况,以确保数据的完整性和可靠性。具体的处理方式可以根据实际情况来确定,以下是一些可能的解决方案:
- 回滚操作:如果上传任务失败,可以将之前的操作回滚,撤销上传的文件,并将客户端发送的上传成功消息撤回。
- 重试操作:如果上传任务失败,可以尝试重新上传,直到上传成功为止。在重试的过程中,可以限制重试次数和时间间隔,以防止一直重试导致资源浪费。
- 持久化数据:在上传前,将上传的数据持久化到本地存储或者数据库中,上传任务完成后再删除。如果上传任务失败,可以重新启动任务,重新上传之前的数据。
- 记录日志:记录上传任务的日志,包括上传的文件名、大小、上传时间等信息。如果上传任务失败,可以通过日志来查找错误原因,以便于后续的处理。
需要注意的是,异步操作和多线程操作都可能存在风险和难点,需要在实践中不断总结和优化。同时,我们需要尽可能地保证数据的完整性和可靠性,以确保系统的稳定性和可用性。
消息队列的幂等性是指在消息队列中,同一条消息被消费多次时,消费结果是相同的,不会产生重复或者错误的操作。
在实际应用中,由于网络波动、系统故障等原因,消息可能会被重复传递或者消费,如果没有处理好幂等性问题,就会导致重复操作或者错误结果。因此,保证消息队列的幂等性是非常重要的。
下面介绍一些常用的保证消息队列幂等性的方法:
- 去重:使用唯一标识来判断消息是否已经被处理过,如果已经处理过,则忽略该消息。可以使用数据库或者缓存来记录消息的状态,以便于快速判断消息是否已经被处理。
- 幂等操作:在处理消息时,采用幂等操作,即多次执行操作和单次执行操作的结果是一致的。例如,如果消息是增加一条记录,可以在数据库中使用唯一索引来避免重复插入数据。
- 消息确认机制:使用消息确认机制,确保消息被成功消费后才进行下一步操作。如果消息处理失败,则可以将消息重新放回队列,等待下一次消费。
- 事务机制:在处理消息时,使用事务机制,保证消息的处理是原子性的,要么全部成功,要么全部失败。如果消息处理失败,则可以回滚事务,重新处理消息。
需要注意的是,保证消息队列的幂等性需要根据具体的业务需求来设计和实现。不同的场景和业务可能需要不同的幂等性处理方式。因此,需要对业务进行仔细分析,确定合适的幂等性方案。
在数据库中,使用唯一索引是为了保证数据的唯一性。唯一索引是指该索引列的值必须唯一,不能重复。如果插入或更新记录时,违反了唯一索引的限制,数据库会抛出唯一索引冲突的异常,从而避免了数据的重复。
例如,在数据库表中,如果有一个唯一索引列是"email",那么每个email地址在该列中只能出现一次。如果有两个记录的email值相同,那么在插入第二个记录时,会因为唯一索引冲突而失败。
在保证消息队列的幂等性中,可以使用唯一索引来避免重复插入数据。例如,如果消息是增加一条记录,可以在数据库中使用唯一索引来限制消息的唯一性,从而避免重复插入数据。
需要注意的是,唯一索引会增加数据库的写入负载,因为数据库需要检查唯一索引列的值是否重复。因此,需要根据实际情况来决定是否使用唯一索引来保证数据的唯一性。
除了数据库的唯一索引,还有其他常用的幂等操作,包括:
- 自增长ID:在插入记录时,使用自增长ID来作为记录的主键,保证每个记录都有唯一的ID,避免重复插入。
- 去重集合:使用Redis等缓存系统的去重集合(set)来记录已经处理过的消息,每次处理消息时,先将消息的唯一标识加入去重集合,如果已经存在,则说明该消息已经被处理过,可以直接忽略。
- Token机制:在消息发送时,生成一个唯一的Token作为消息的标识,将Token存储在缓存或者数据库中,在消费消息时,检查Token是否已经存在,如果已经存在,则说明该消息已经被处理过,可以直接忽略。
需要注意的是,不同的业务场景和系统架构可能需要不同的幂等操作方式,需要根据具体情况来选择合适的幂等操作方法。同时,为了保证幂等操作的正确性,需要考虑到并发处理、分布式系统等因素,对系统进行仔细设计和测试。
去重表
这种方法适用于业务中有唯一标识的场景。假定以上场景,一个订单对应一个唯一的订单号,一个订单支付一次,这时候订单号可作为唯一标识,并且把订单号作为唯一索引(查出来只取符合条件的一条数据),将订单号写入去重表的操作,放入一个事务当中,如果订单重复创建,数据库会抛出唯一约束,事务进行回滚。这其实也用到的唯一ID,但与全局唯一ID相比,只是单单针对一个业务流程而已。
#你觉得今年春招回暖了吗#