郑昀 基于朱传志的设计文档 最后更新于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-
 
欢迎订阅我的微信订阅号『老兵笔记』,请扫描二维码关注:

#研发中间件介绍#异步消息可靠推送Notify的更多相关文章

  1. #研发中间件介绍#定时任务调度与管理JobCenter

    郑昀 最后更新于2014/11/11 关键词:定时任务.调度.监控报警.Job.crontab.Java 本文档适用人员:研发员工   没有JobCenter时我们要面对的:   电商业务链条很长,业 ...

  2. RabbitMQ 延迟队列,消息延迟推送

    目录 应用场景 消息延迟推送的实现 测试结果 应用场景 目前常见的应用软件都有消息的延迟推送的影子,应用也极为广泛,例如: 淘宝七天自动确认收货.在我们签收商品后,物流系统会在七天后延时发送一个消息给 ...

  3. NET SignaiR 实现消息的推送,并使用Push.js实现通知

    一.使用背景 1. SignalR是什么? ASP.NET SignalR 是为 ASP.NET 开发人员提供的一个库,可以简化开发人员将实时 Web 功能添加到应用程序的过程.实时 Web 功能是指 ...

  4. PHP版微信公共平台消息主动推送,突破订阅号一天只能发送一条信息限制

    2013年10月06日最新整理. PHP版微信公共平台消息主动推送,突破订阅号一天只能发送一条信息限制 微信公共平台消息主动推送接口一直是腾讯的私用接口,相信很多朋友都非常想要用到这个功能. 通过学习 ...

  5. Swift - 本地消息的推送通知(附样例)

    使用UILocalNotification可以很方便的实现消息的推送功能.我们可以设置这个消息的推送时间,推送内容等. 当推送时间一到,不管用户在桌面还是其他应用中,屏幕上方会都显示出推送消息. 1, ...

  6. .NET之微信消息模板推送

    最近在项目中使用到了微信消息模板推送的功能,也就是将对应的消息推送到对应的用户微信上去,前提是你必须要有一个微信公众号并且是付费了的才会有这个功能,还有就是要推送的用户必须是的关注了你的微信公众号的. ...

  7. dwr3实现消息精确推送详细步骤

    最近项目中需要用到推送消息,找了很久终于找到一篇不错的文章,方便以后查看就转载了,也分享给大家,希望能帮到有需要的人. 第一.在项目中引入dwr.jar,然后在web.xml中进行配置,配置如下: & ...

  8. 基于HTTP协议之WEB消息实时推送技术原理及实现

    很早就想写一些关于网页消息实时推送技术方面的文章,但是由于最近实在忙,没有时间去写文章.本文主要讲解基于 HTTP1.1 协议的 WEB 推送的技术原理及实现.本人曾经在工作的时候也有做过一些用到网页 ...

  9. spring boot 集成 websocket 实现消息主动推送

    spring boot 集成 websocket 实现消息主动 前言 http协议是无状态协议,每次请求都不知道前面发生了什么,而且只可以由浏览器端请求服务器端,而不能由服务器去主动通知浏览器端,是单 ...

随机推荐

  1. MongoDB的CRUD操作

    1. 前言 在上一篇文章中,我们介绍了MongoDB.现在,我们来看下如何在MongoDB中进行常规的CRUD操作.毕竟,作为一个存储系统,它的基本功能就是对数据进行增删改查操作. MongoDB中的 ...

  2. 编写.gitignore文件的几个小技巧

    记录几个编写.gitignore文件的小技巧,可能你早就知道了,但我是google了一番才找到写法. 忽略所有名称为bin的文件夹 bin/ 只忽略第一级目录中,名称为bin的文件夹 /bin/ 忽略 ...

  3. 用react的ReactCSSTransitionGroup插件实现简单的弹幕动画

    1,开始的思路 公司想做直播方面的项目,并想加入弹幕的功能,直播的页面已经作为一个组件放在了用react+redux写好的一个网站项目上.所以技术老大让我研究下如何用react实现弹幕的功能.下面我就 ...

  4. 【面试】输出"蛇形"矩阵

    一.题目描述 腾讯实习在线笔试的一道题目. 根据输入的数字(< 1000),输出这样的"蛇形"矩阵,如下.输入n,输出(n * n)阶矩阵,满足由外到内依次增大. 如: 输入 ...

  5. Azure Media Service (1) 使用OBS进行Azure Media Service直播

    <Windows Azure Platform 系列文章目录> 今天正好有客户问如何使用OBS进行Azure Media Service直播,我这里简单介绍一下. 先决条件: 1. OBS ...

  6. spring boot 调试 - 热部署

    maven gradle Maven: 命令行方式: mvn spring-boot:run -Drun.jvmArguments="-Xdebug -Xrunjdwp:transport= ...

  7. 关于C#的继承结论

    1.继承:继承是一个类可以使用另一个类的属性和方法(成员) 2.子类继承父类使用的符号为“  : ”(冒号) 3.子类可以继承父类私有成员,但不能访问(不能使用) 4.父类的构造,子类不能被继承 5. ...

  8. DevExpress ComboBoxEdit 添加值

    今天在使用ComboBoxEdit 这个控件的时候,不知道怎么添加值. 在官网上找到代码.在这里做个记录 ComboBoxEdit combo = new ComboBoxEdit(); ComboB ...

  9. android Can't bind to local 86XX for debugger

    For some reason eclipse DDMS always gives the error 'Can't bind to local 86XX for debugger' every ti ...

  10. 背水一战 Windows 10 (20) - 绑定: DataContextChanged, UpdateSourceTrigger, 对绑定的数据做自定义转换

    [源码下载] 背水一战 Windows 10 (20) - 绑定: DataContextChanged, UpdateSourceTrigger, 对绑定的数据做自定义转换 作者:webabcd 介 ...