网易音乐消息队列实践调研
业务背景
网易云音乐从13年4月上线以来,业务和用户突飞猛进。后台技术也从传统的 Tomcat 集群到分布式微服务快速演进和迭代,在业务的不断催生下,诞生了云音乐的 RPC,API 网关和链路跟踪等多种服务,消息队列也从 RabbitMQ 集群迁移到 Kafka集群。对于消息队列,更多处于使用阶段,也在使用中出现很多问题。因此我们期望提供一种完全可控,出现问题我们自己能排查,能跟踪,可以根据业务需求做定制改造的消息队列。
生产环境
截止到2019年Q1,基于 RocketMQ 改造实现的消息队列在网易云音乐生产环境已经有6个集群。涉及顺序消息,普通消息,多种高可用高可靠要求。
接入应用 数量 200+,每条业务线核心业务都有涉及。峰值 QPS 已达 30w+,topic 800+。在测试环境单个集群,由于环境很多,Topic 数量也疯长,单个达到 4000+,未来随着 kafka 迁移的更多,Topic 数量还会不断上涨。
从 2018 年 11 月开始,禁止新业务接入kafka,新接入全部使用 RocketMQ,自2019 Q1推动Kafka业务迁移到RocketMQ,目前已经迁移70%+,业务整体稳定性得到极大提升。
随着业务接入 RocketMQ 的增多,也不断催生下游大数据生态的接入和使用。云音乐大数据主要使用 flink,目前 flink 在运行 job 60+,其中 RocketMQ 消息量 每天达 8 亿 +,这一块未来还有不少增长空间。
技术选型
RocketMQ vs RabbitMQ vs Kafka性能对比
由于RabbitMQ持久化性能只有2.6万/秒,不能满足业务对高性能、高吞吐的要求。所以2017年从RabbitMQ迁移到Kafka
功能特性完备性对比
RocketMQ系统架构
开源RocketMQ主要问题如下:
- Broker仅提供了Master到Slave的复制功能,没有Failover切换的能力
- 事物消息不开源(我们开始研发时不开源)
- 消息发送消费无追踪(我们开始研发时不开源)
- 告警与监控体系没有
- 开源控制台不完善
业务要求
- 具备自主failover切换能力
- 监控dashboard大盘
- 根据不同业务场景提供灵活适配的QOS能力。如商城消息不能丢失,交易事物消息支持,消息发送消费追踪,失败排查等能力
- 提供发送耗时,消费耗时,消费延迟,消费失败异常等提供监控和告警能力
- 功能强大和较完善的管理控制台
- 对Nodejs和Python支持,另外要提供http接入能力
优化架构设计
以开源RocketMQ为内核,完全继承开源 RocketMQ 具备的特性,然后以此为基础进行扩展
改进措施:
- 通过新增对broker健康检查,提供主从broker自主failover能力
- 发送耗时,消费耗时,消费失败异常采集上报,写入ES,根据阀值配置监控项
- 有broker宕机,提供服务降级组件
- 系统异常时,客户端会将消息发送本地或者发送到容灾集群,降低系统宕机时对业务的影响。
- 提供系统巡检能力,关键关键状态异常快速发送报警
各组件交互流程如下:
- NameServer提供topic路由信息发现,配置中心能力。
- Broker 提供 topic 消息存储,索引,查询。同时 Broker 自身提供 HA 需要的复制,角色修改,探活,状态获取等 API。同时 Broker 定时向所有 Nameserver 注册。
- Nginx 提供发现 NameServer 能力,由运维将 nameserver 列表填写到 hotdoc 中。避免 NameServer 变更业务重新配置上线。
- 降级组件提供消息发送失败的处理,在消息发送失败的情况下 client 会将消息发送到容灾集群,由降级组件统一处理保证发送方业务的稳定性。
- Failover 组件检查 Broker 状态,Broker 宕机根据 QOS 要求切主。
- Console 提供资源管控,消息查询,轨迹跟踪,监控报表,监控告警配置,死信重投等能力。
- 巡检组件巡检消息队列自身集群所有组件健康与服务状态,有异常提前通知运维和消息队列相关人员。
- 监控组件提供监控报表数据采集处理,消息队列大盘数据采集处理。
- 告警组件主要采集告警信息,根据控制台配置的告警阀值和人员信息通知相应业务方。
- 消息队列大盘提供消息队列集群自身的监控状态,主备复制状态,QPS 等集群大盘报表展示。
新增高级特性
RocketMQ为成为Apache顶级项目前,github开源版本是没有消息轨迹跟踪和事务特性的。网易云音乐对代码进行了修改和扩展,用于支持一下一些特性:
- 消息轨迹跟踪
- 事务消息
- 多环境隔离
- 消费线程池自定义
- 消费限流与降级
消息轨迹
以上实现和开源RocketMQ-v4.4中提供的消息轨迹实现机制一样。和开源不同的是,云音乐消息队列提供发送消费、事物消息回查轨迹,同时消费失败时,也在轨迹中提供失败异常信息,这样就能够追踪失败原因。
事务消息
云音乐通过修改存储引擎实现自己的事物消息。提供事务消息回查按时间收敛能力,回查不到状态超过次数进入死信,同时提供死信告警,事务消息回查死信处理能力。
多环境隔离
云音乐有很多条业务线,每条业务线都有很多个需求在做,同时各个业务线之间的访问都是通过服务化的方式进行。为提升开发和测试效率,通过对 RPC 流量打标,的方式将流量隔离到相应环境,并一路透传下去。消息也一样,同一个需求发送的消息需要相应的环境开发,测试,和其他互不影响。因此云音乐设计实现了消息队列的隔离,加快业务开发测试,预发快速验证能力。
消费线程池自定义支持
开源 RocketMQ 消费端仅有一个消费线程池,多个topic的消费会互相影响。另外同一个消费端仅有一个 listener,如果是多个 topic,需要上层业务分发。云音乐同一个应用都会有多个topic消费,有的多达 30+ 个。因此优先级高的 topic 需要自定义自己的消费线程池,优先级低的使用公共的。另外 每个topic也要有自己的listener,这样就不用上层分发。基于上述要求,我们增加订阅可以同时指定listener和consumeThreadExecutor 的方式。
消费限流与降级
开源RocketMQ并没有提供消费限流能力,基于Sentinel的是pull模式,而不是 push 模式,不能很好满足云音乐的业务需求。云音乐的消息消费不少都要写数据库或者访问第三方,这些消费和在线业务都是同一个应用,消息队列自身虽然具备削峰填谷的能力,但是消费端会最大化使用消费线程池,这会对在线业务也产生影响。为保证在线业务优先,需要限制消费的速度,减少瞬时高峰消息消费对在线业务的影响。这部分可以通过调整消费线程池和消费qps来调整。我们选择了调整消费 qps 消费限流的方式,这样能和监控数据对起来并提供控制台动态调整能力,消费线程池调整虽然我们也提供动态调整线程池能力但是并不是线性的,调整起来没有参考,比较困难。消费限流做在了底层而不是让应用层自己做,和上层的区别时,上层限流了会触发消息消费一次并且失败,底层不会失败也不算消费一次,因为还没投递上层业务。
多机房网络bug修复
云音乐有部分业务部署在建德,需要消费杭州的数据。这部分消费的机器总是隔三差五报 timeout。经过排查,发现 client 新建的连接总是在关闭创建循环,非常不稳定,这部分排查 remoting 层的代码发现 client 建立连接存在并发问题,会将建立好的链接关闭。定位待开源 client 的 remoting bug 后,我们进行了修复。另外后来几天,曲库的业务同学也报发送3s超时,经过仔细排查,发现异常日志和连接建立日志和网络建立并发问题的日志一致,协同业务升级到最新修复的客户端解决。业务升级上线后如上所示,发送非常平稳,不再超时。
回馈开源社区
作为开源的受益者,部分改动已经提交到Apache RocketMQ官方且被采纳,如消费限流,消费线程池,网络 bug 修复等。
未来规划与展望
- 从核心歌单和曲库业务都有QPS高吞吐的push业务在运行,后续推广到日志领域。
- 涉及到日志传输开源 RocketMQ 有部分性能问题,需要做优化,目前我们已经完成这部分优化,由于优先级的关系,还没推广到日志传输相关应用。我们期望云音乐的消息队列能够拓展到日志方面,将消息队列具备的完善监控告警等能力赋能到大数据,NDC 订阅(数据库更新订阅服务)。同时增加路由能力减少多机房间跨机房访问。
- RocketMQ只有网易音乐使用,未来联合杭研,将网易音乐RocketMQ贡献的扩展版推广到其他大产品线。
参考资料
网易云音乐的消息队列改造之路:https://www.infoq.cn/article/2_D683SzYss0PjMdKXg2