1. 异步
1.1 同步异步的问题(串行)
串行方式:将订单信息写入数据库成功后,发送注册邮件,再发送注册短信。以上三个任务全部完成后,返回给客户端
java
public void makeOrder(){
// 1 :保存订单
orderService.saveOrder();
// 2: 发送短信服务
messageService.sendSMS("order");//1-2 s
// 3: 发送email服务
emailService.sendEmail("order");//1-2 s
// 4: 发送APP服务
appService.sendApp("order");
}
1.2 并行方式 异步线程池
并行方式:将订单信息写入数据库成功后,发送注册邮件的同时,发送注册短信。以上三个任务完成后,返回给客户端。与串行的差别是,并行的方式可以提高处理的时间
java
public void makeOrder(){
// 1 :保存订单
orderService.saveOrder();
// 相关发送
relationMessage();
}
public void relationMessage(){
// 异步
theadpool.submit(new Callable<Object>{
public Object call(){
// 2: 发送短信服务
messageService.sendSMS("order");
}
})
// 异步
theadpool.submit(new Callable<Object>{
public Object call(){
// 3: 发送email服务
emailService.sendEmail("order");
}
})
// 异步
theadpool.submit(new Callable<Object>{
public Object call(){
// 4: 发送短信服务
appService.sendApp("order");
}
})
// 异步
theadpool.submit(new Callable<Object>{
public Object call(){
// 4: 发送短信服务
appService.sendApp("order");
}
})
}
存在问题:
1:耦合度高
2:需要自己写线程池自己维护成本太高
3:出现了消息可能会丢失,需要你自己做消息补偿
4:如何保证消息的可靠性你自己写
5:如果服务器承载不了,你需要自己去写高可用
1.3 异步消息队列的方式
java
public void makeOrder(){
// 1 :保存订单
orderService.saveOrder();
rabbitTemplate.convertSend("ex","2","消息内容");
}
好处
1:完全解耦,用MQ建立桥接
2:有独立的线程池和运行模型
3:出现了消息可能会丢失,MQ有持久化功能
4:如何保证消息的可靠性,死信队列和消息转移的等
5:如果服务器承载不了,你需要自己去写高可用,HA镜像模型高可用。
按照以上约定,用户的响应时间相当于是订单信息写入数据库的时间,也就是50毫秒。注册邮件,发送短信写入消息队列后,直接返回,因此写入消息队列的速度很快,基本可以忽略,因此用户的响应时间可能是50毫秒。因此架构改变后,系统的吞吐量提高到每秒20 QPS。比串行提高了3倍,比并行提高了两倍
2. 高内聚,低耦合
3. 削峰
3. 分布式事务的可靠消费和可靠生产
3.1 可靠生产
- 发布确认 (Publisher Confirms):
- RabbitMQ 支持发布确认机制,允许生产者知道消息是否已经被代理 服务器接收到。
- 当生产者发送一条消息时,它可以设置一个回调函数,当消息被成功接收后,这个回调函数会被触发。
- 如果消息未能成功发送,生产者可以采取重试策略或其他补救措施。
事务 (Transactions):
- 生产者可以将消息发送包裹在一个事务中。在事务提交之前,所有操作都不会生效。
- 这种方法虽然能够确保消息要么全部发送成功,要么全部失败,但会显著降低性能,因为事务处理需要更多的网络往返和锁定资源。
- 在大多数情况下,推荐使用发布确认而不是事务。
- 持久化消息 (Persistent Messages):
- 生产者可以在发送消息时将其标记为持久化的,这样即使 RabbitMQ 重启,这些消息也会被保存下来。
- 持久化消息通常与队列的持久化设置一起使用,以确保消息不会丢失。
3.2 可靠消费
- 手动确认 (Manual Acknowledgments):
- 消费者可以通过设置自动确认为 false 来控制何时确认消息。
- 只有当消费者完成处理并且明确调用确认方法时,RabbitMQ 才会从队列中移除该消息。
- 如果消费者处理消息过程中发生错误,没有发送确认,那么 RabbitMQ 会将消息重新放入队列中,等待其他消费者处理。
- 死信交换机 (Dead Letter Exchanges, DLX):
- 当消息达到一定的条件(如尝试次数超过限制)时,消息可以被路由到一个特殊的交换机,称为死信交换机。
- 死信交换机可以用来存储或进一步处理这些无法正常处理的消息。
- 消息TTL (Time To Live):
- 设置消息的有效期,过期的消息将会被丢弃或者被转发到死信交换机。
- 这对于清理那些长时间未被处理的消息很有用。
- 镜像队列 (Mirrored Queues):
- 镜像队列是 RabbitMQ 中的一种高可用性解决方案,其中队列的内容会在集群中的多个节点上复制。
- 即使某个节点崩溃,消息也不会丢失,因为它们仍然存在于其他节点上。
4. 索引、缓存、静态化处理的数据同步
4.1 索引更新
- 异步处理:当数据库中的数据发生变化时,应用程序可以发送一条消息到 RabbitMQ,而不是直接更新索引。一个专门的消费者(可能是另一个服务或进程)监听这个消息,并负责更新索引。这样可以将索引更新操作从主业务流程中分离出来,提高系统的响应速度。
- 解耦:通过使用消息队列,可以将索引更新逻辑与业务逻辑解耦。即使索引服务暂时不可用,也不会影响主要业务流程,因为消息会被存储在队列中,直到被消费。
4.2 缓存更新
- 事件驱动的缓存刷新:当数据库中的数据发生变更后,可以通过消息通知相关的缓存服务,使其能够及时地更新或清除过时的缓存条目。
- 一致性保证:虽然缓存通常不要求强一致性,但使用消息队列可以帮助实现最终一致性。当数据发生变化时,相关服务可以通过订阅消息来更新它们的缓存。
4.3 静态化处理
- 后台任务触发:对于需要生成或更新的静态内容,如HTML页面、图片缩略图等,可以在数据变更时发送一条消息。静态内容生成器可以是单独的服务,它监听消息并执行生成或更新操作。
- 流量削峰:如果静态内容的生成是一个资源密集型的操作,那么使用消息队列可以帮助平滑请求峰值,避免因突然大量请求导致系统过载。
5. 流量监控
RabbitMQ 本身并不是一个流量监控工具,但它可以与监控系统结合使用来实现对消息队列的流量监控。通过监控 RabbitMQ 的各项指标,你可以了解系统的健康状况、性能瓶颈以及可能存在的问题。
5.1使用 RabbitMQ 内置的管理插件
RabbitMQ 提供了一个管理界面(Management UI),它可以通过 HTTP API 访问,并且包含了一些基本的监控功能。这些功能包括:
- 队列状态:查看队列中的消息数量、消费者数量等。
- 连接和通道:监控当前活动的连接数和通道数。
- 交换机状态:检查交换机的状态和绑定情况。
- 节点信息:获取集群中各个节点的信息,包括内存使用情况、磁盘空间等。
5.2 使用外部监控工具
为了更全面地监控 RabbitMQ 并与其他系统集成,通常会使用专门的监控工具或平台,例如:
- Prometheus + Grafana:Prometheus 是一个开源的监控系统和时间序列数据库。你可以配置 Prometheus 来抓取 RabbitMQ 暴露的指标,并使用 Grafana 创建可视化仪表板。
- Datadog, New Relic, Zabbix 等第三方监控服务也提供了对 RabbitMQ 的支持,它们可以帮助你设置警报规则,进行历史数据分析等。
6. 日志监控(ELK)
RabbitMQ 本身不是一个日志管理或分析工具,但它可以作为传输层来帮助构建日志收集和处理的管道。ELK(Elasticsearch, Logstash, Kibana)堆栈是一种流行的日志管理和可视化解决方案,其中:
- Elasticsearch 是一个分布式的搜索和分析引擎。
- Logstash 是一个服务器端的数据处理管道,可以从多个来源采集数据,转换并发送到指定的目的地。
- Kibana 提供了可视化界面,用于展示 Elasticsearch 中的数据。
可以将 RabbitMQ 与 ELK 堆栈结合起来使用,尤其是当你需要一种可靠的、可扩展的方式来传输日志信息时。
7. 下单、订单分发、抢票
同上面的异步、削峰、解耦。