郑昀 基于朱传志的设计文档 最后更新于2014/11/11
关键词:异步消息、订阅者集群、可伸缩、Push模式、Pull模式
本文档适用人员:研发
电商系统为什么需要 NotifyServer?
如子柳所说,电商系统『需要两种中间件系统,一种是实时调用的中间件(淘宝的HSF,高性能服务框架)、一种是异步消息通知的中间件(淘宝的Notify)』。那么用传统的 ActiveMQ/RabbitMQ 来实现 异步消息发布和订阅 不行吗?
2013年之前我们确实用的是 ActiveMQ,当然主要是订阅者 Pull 模式,选 MySQL 做消息持久化存储,SA 还为此反复测试了各种高可用方案,如下图所示,ActiveMQ 5.8,mq主从+mysql互为主从+MMM。
图1 mq 高可用
它有三个问题。
第一,它对上游发布者要求可能不是那么高,但要求下游实现消息订阅时要健壮,比如订阅者把消息读走了后它挂了也得不丢弃消息继续处理,比如非常重要的消息不能只有一个单点订阅者,必须有订阅者集群,但又不能重复处理消息。我在《#研发中间件介绍#JobCenter》中说过,对每一位开发者维护者提出高要求,这不是我们的解题思路。我在《职场培训第五期:职场的真相》中给出了解题思路:『要摒弃单纯依靠员工之间互相提醒、依靠个人认真细致来规避相同错误的固有思路,铁打营盘流水兵,靠人终归是靠不住的,最好靠遵循规则的机器』。是的,异步消息的可靠推送(Push),应该是消息中间件的职责。
第二,ActiveMQ 的高可用方案在可伸缩上不那么灵活, 不适合电商业务。譬如说,我一开始用一组 [(mq1+mysql1(master角色)),(mq2+mysql2)] 来支撑所有业务的异步消息,但突然七夕节一个销售验证高峰即将到来,需要尽量平滑地把某些消息队列转移出去,用另一组支撑;或者我看某个消息队列的消息量 比较大,想追加一个 mysql 节点单独存储它的消息。总之就是线上尽量平滑地扩容 mq server和 database,这事儿还得咱们自己从头搞才顺手。
最后一个问题是所有开源系统的典型问题,伴随着开源系统以及各种 Driver 的版本升级,我们会一路踏入它埋下的每一个大大小小的坑。当然,不是说我们自己写的中间件就没有 Bug,但 ActiveMQ 确实让人摊手,如下面的 RCA 案例所示。
- RCA:ActiveMQ 的生产者流量控制导致订单中心大量线程挂起;
- RCA:PHP连MQ超时导致主库连接被打满,引发众多应用数据不一致——原因在于 PHP::Stomp 包的默认重试次数和默认超时时间;
- RCA:调小 ActiveMQ之持久化 MySQL 的 wait_timeout 导致发送 MQ 消息频频失败。
最终我们还是选择自己来面对如下场景,采用 Push 模式(NotifyServer 主动向下游 Push 消息):
图2 一个异步消息需要很多订阅者集群分头处理
淘宝是怎么考虑这些问题的?
- 可靠性:
- 消息的投递分为两个阶段
- 发布者向Broker发送消息
- Broker向订阅者投递消息
- 因此,消息有可能在三个地方丢失
- 发布者到Broker之间
- Broker本身
- 从Broker到订阅者
- 稳定性
- 监视
- Broker内存使用
- 消息收发功能
- 消息堆积情况
- 存储的插入速度
- 各个任务队列长度
- 其他各项即时统计数据等
- 控制
- 自动移除失效存储节点
- 优雅降级的控制
- 添加新存储节点
- 添加新Broker
- 限制
- 有可能产生重复消息
- 对订阅者的要求
- 幂等性 f(f(x)) = f(x)
- 重复调用多次产生的业务结果与调用一次产生的业务结果相同
它内部两个消息中间件产品的区别为:
图3 消息中间件对比
以上资料出自于《消息中间件-Notify的概念和原理.pdf》。
窝窝如何实现 NotifyServer 的?
2013年2月,经过几轮的讨论,技术选型初步确定,研发2部传志开始构建 NotifyServer。
他设计了如下概念:
图4 notifyserver 的几个角色概念
技术模型可以描述为:
- 模块关系
- 各个模块(队列、生产者、交换中心、DB、消息体缓存、队列缓存、日志缓存、分配中心、消费者)存在一定的对应关系,通过这些对应关系能够更好路由和分流消息,动态扩展系统,改善系统瓶颈。
- 这些对应关系都存储在控制中心关系数据库中,通过控制台界面来进行配置,各模块在启动和定时到控制中心来更新这些关系,用于消息的分配。
- 这些关系都遵守一定规则,添加更改不会影响系统的稳定性,如:一个队列必须对应两个以上的交换中心来处理消息,如果DB中还有消息没有消费完毕不允许直接删除,等等。
- 模块监控
- 控制台定期测试各个模块的健康状况。
- 各个模块会定期向控制中心发送一些监控数据,报告自己的运行状态。
- 控制台收集监控数据,以图表、拓扑图等形式向管理人员展示或报警。
- 消息跟踪
- 每一条消息在进入系统后都会被分配一个唯一标识。
- 各模块在处理消息时都会产生特定的日志信息,日志信息实时的传送到日志系统。
- 唯一标识+日志+各模块信息和关系可以容易的跟踪每一条消息的执行情况。
那么最简单的消息消费泳道图如下所示:
图5 消息消费
分配中心主导的慢速/重试Push,它会尽量从缓存(Redis)中拿消息体,尽量减少对 DB 的访问,尤其是消息体特别大的时候,效果会比较明显,如下图所示。
图6 重试的泳道图
对于可伸缩、高可用,他是这么考虑的:
- 吞吐量:
- 交换中心、分配中心都采用平行结构。
- 队列使用持久化方式缓存,使用缓存减少对DB的操作。
- 交换中心与分配中心分离,性能互不影响。
- 动态改变网络拓扑结构,分流系统瓶颈。
- 动态控制吞吐量参数,调整系统性能。
- 扩展性
- 交换中心、分配中心、DB、缓存可以动态添加或删除。
- 队列路由路径可以动态改变。
- 可用性
- 交换中心、分配中心采用平行方式提高可用性。
- DB采用 master-master 方式提高可用性。
- 缓存采用多点读写方式提过可用性。
- 一致性
- 消息状态持久化在DB,未分配或消费失败的会再次被提取。
- 分配中心采用快慢两种方式接收消息处理消息。
- 等幂性
- 缓存保存正在处理的消息,防止重复分配。
与 JobCenter 一样,NotifyServer 也纳入在我们的 idcenter 体系下,这样可以共用一套帐号体系(LDAP),共用一个统一的权限分配:
图7 notifyserver 的入口
图8 notifyserver 的主界面
图9 notifyserver 系统队列(主要是配置信息)界面
图10 notifyserver 监控队列(主要是运行时状况)界面
2013年中旬,经过积分业务的试用后,传志的 NotifyServer 开始在内部推广,各种异步消息发布和订阅一点一点地搬进来,ActiveMQ 方案下线。
-over-
相关推荐
2. **异步流程**:同时,系统将需要异步执行的任务(如发送新手红包等)推送到消息队列,由Notify负责将这些任务分发给相应的后端服务。 #### 设计理念与原理 Notify在设计上与传统消息中间件有所不同,其核心设计...
Notify_1.4_truncate.docx Notify_1.7_client_design.doc Notify_1.7_client_develop_guide.docx ... ... Notify1.7_server_design.docx Notify1.7UserGuide.pptx Notify1.8_SEDA.docx Notify2010plan.pptx Notify...
消息中间件的核心特征包括松散耦合、异步处理、可靠性和消息发送及业务操作的一致性。 1. 松散耦合:消息中间件允许发送者和接收者之间不直接依赖。发送者仅需要知道消息的格式和目的地,而不需要了解接收者的实现...
在Android开发中,消息推送和通知栏管理是关键部分,它们允许应用在后台与用户进行交互,即使应用没有运行在前台。本资源包含五个不同示例的源代码,旨在帮助开发者理解和实现Android应用中的消息推送通知功能。下面...
本文将详细介绍如何在Eclipse中实现本地消息推送通知栏功能。 首先,理解“本地消息推送”:与远程服务器推送(如Google Firebase Cloud Messaging)不同,本地消息推送是在设备本地触发的,不依赖网络服务,常用于...
### Android中利用App实现消息推送机制...总之,通过合理配置`AndroidManifest.xml`中的`service`标签和编写相应的服务逻辑,开发者可以在Android应用中实现稳定可靠的消息推送功能,从而提升用户体验和应用的活跃度。
通过JavaPush,开发者可以创建高效、可靠且自定义的消息推送系统,以满足各种业务需求。 首先,理解JavaPush的基础概念是非常重要的。推送服务通常由服务器端(后端)和客户端(前端)两部分组成。服务器端负责生成...
此资源"安卓消息推送通知栏相关-Android--CardView新闻展示推送效果.rar"主要关注如何利用CardView组件来构建美观且实用的新闻展示推送效果。CardView是Android支持库中的一个视图容器,它为内容提供了一个带有阴影...
在JavaScript中,这个脚本可能包含了处理推送逻辑、调用微信推送接口以及格式化和验证消息内容的代码。当青龙面板有新的事件发生时,如任务完成、错误出现或者系统维护等,sendNotify.js就会被触发,将相关信息推送...
在MFC(Microsoft Foundation Classes)框架中,`WM_NOTIFY`消息扮演着极其关键的角色,特别是在处理控件间通信时。本文将深入解析`WM_NOTIFY`消息的机制、应用场景以及其在MFC中的实现细节。 #### WM_NOTIFY消息...
这个"安卓消息推送通知栏相关-消息推送完美demo.rar"压缩包包含了一些关于如何实现在安卓应用中实现消息推送并显示在通知栏的示例代码和资源。 首先,我们要了解安卓系统中的`Notification`类,它是构建通知的核心...
在安卓平台上,消息推送服务是应用开发者不可或缺的功能之一,它能实现在后台向用户发送实时信息,如系统更新、新消息提醒或者应用活动通知。这个压缩包“安卓消息推送通知栏相关-消息推送最新demo服务器.rar”包含...
百度推送(Baidu Push)是百度提供的一项云消息推送服务,旨在帮助开发者高效、稳定地向移动设备发送消息,提高用户活跃度和留存率。其主要功能包括单播、广播、标签推送、定时推送等,适用于各种业务场景,如新闻...
在Android开发中,消息推送是应用与用户保持互动的关键手段之一。`NotificationTest`是一个用于演示如何在Android系统中实现消息推送并展示在通知栏的简单示例项目。这个项目包含了一个基本的Java APK源码,可能需要...
在Android系统中,消息推送是一种重要的功能,它允许应用程序在后台向用户显示通知,即使应用没有在前台运行。本文将详细探讨如何利用`NotificationManager`实现Android的消息推送。 首先,我们要理解`...
【Android高级应用源码-消息推送最新demo +服务器】 在Android开发中,消息推送是一项重要的功能,它使得应用能够在后台与用户保持互动,提供实时的通知和更新。本资源包含了一个高级的消息推送Demo,以及配套的...
本文将深入探讨如何在Spring Boot项目中实现WebSocket的消息推送,包括群发和指定到个人或多人。 首先,我们需要在Spring Boot项目中引入WebSocket的相关依赖。通常,我们会添加`spring-websocket`和`spring-...
本地推送是一种在应用程序不处于前台运行状态时,仍然可以向用户发送消息或提醒的技术。它在移动应用开发中尤其常见,比如iOS的UNUserNotificationCenter和Android的NotificationManager。本实例将探讨如何在本地...
在Android系统中,本地推送(Local Push)是一种应用程序在不依赖远程服务器的情况下,实现消息推送的技术。它允许应用在特定时间或满足特定条件时向用户显示通知,无需持续连接到互联网,因此对于节省数据流量和...