基于 Kafka 的消息推送系统设计

适用于消息推送系统的 Kafka 特性

  • 高可用性

    Kafka 能够保证不丢消息。

  • 高性能

    QPS 轻松上万。

  • 可扩展

    可搭建包含上千结点的集群。

不适用于消息推送系统的 Kafka 特性

  • 队列中的数据可能积压

    Kafka 主要用于离线数据处理,数据没有太强的时效要求。推送的消息往往有时效性,过期就失去意义了,消息队列堵塞后,系统需要将队列中的数据消费掉,才好继续提供服务。

  • 处理结果难以反馈给调用者

    如果一条消息通过消息队列传输到另一个模块进行处理,就很难将处理结果同步地传回调用方。使用消息队列就意味着为了知道处理结果,调用方要异步地接收结果通知,也就意味着要唯一地标识请求消息以便与通知进行配对。需要反馈的情形更适合使用 RPC 调用,可以考虑在 Kafka 之上封装 RPC 调用。

  • Kafka 主要用于内部系统间通信

    消费者和生产者一般要求是固定且7x24小时在线,通常为后台服务程序。而消息推送系统的客户端一般是在线不稳定,接入点不固定,需要支持离线消息。内部的稳定性与外部的不稳定性会带来冲突,这两者的交接处需要进行复杂的处理。

  • 消息的追踪与流控会成为问题

    消息可能滞留在某个 Kafka 、MySQL、内存中,因此会很难实现用户级、业务级、系统级的流控,业务服务器或客户端如果投递了过量的消息,会影响整个系统。

如何基于 Kafka 构建消息推送系统

  • 分而治之

    将海量的用户空间静态垂直划分到单个消息处理结点可掌控的粒度。

    比如将 uid % 100 分布到 100 个消息处理结点结点上,如果总共有 1000 万在线用户的话,每个消息处理结点只负责 10 万在线用户,完全可以将大部分状态放置在内存中,避免远程访问数据服务。

    消息处理结点间尽量不共享状态,各自存储属于自已的消息数据,通过往各自的 Kafka 投递消息进行交互。

  • 做为消息存储设施的一部分

    Kafka 是一种可靠的数据存储设施,可以认为它是只能进行顺序读取的存储设施。

    用户的上线、下线没有规律可言,也就是说有着天然的随机性。

    随机的读写还是通过 MySQL,可将 Kafka 用于顺序存储的场景,如:

 在线用户的消息发送与投递、离线消息或需要确认的消息的入库。

  • 谨慎地处理时序问题

    一个消息通过 MQ 发往 MySQL 进行存储,然后往用户发送该消息,收到用户发回的确认消息后,通过 MQ 往 MySQL 要求删除该消息,此时无法保证该消息已经写入 MySQL。

    这正是分布式系统所要面对的时序问题。

    对异步消息进行并行处理必然会带来无序,需要将同一用户的所有消息都调度到一个协程(或线程)进行顺序处理。

  • 做为系统级消息总线

    消息处理结点与其它系统模块(如:离线消息模块)交互也通过 Kafka 来进行。

  • 消息队列需要好的监控及运维

    队列堵塞会导致消息处理不及时,消息可能已经无效,需要进行处理。而使用消息队列意味着不允许失败,必须在承诺的期限内给出可预期的结果,也就是说无法为特定用户提供定制化的投递超时设置。


kafka