如何去设计一个消息队列

以下相关业务纯属假设,如有雷同,实属巧合,下面的设计案例仅仅描述一些关键的设计考虑点和决策点

1 需求

假想一个创业公司,叫胖批微博,胖批微博的业务发展很快,系统越来越多,系统之间的协调效率很低,如下:

  • 用户发了一条微博后,微博子系统需要通知审核子系统进行审核,然后通知统计子系统进行统计,再通知广告子系统进行广告预测,接着通知消息子系统进行消息推送、一条微博就会有十几个通知,现在都是通用接口直接调用的,每通知一个新系统,就要设计接口,进行测试,效率低,且定位问题麻烦
  • 用户等级达到VIP后,等级子系统要通知福利子系统进行奖品发放,通知客服子系统安排专属服务人员

新来的架构师在梳理这些问题时,结合自己的经验敏锐发现了问题在于架构上各业务子系统强耦合,正好消息队列又能完成子系统之间解耦,所以引入消息队列系统来解决这个问题很有必要

其他背景信息

  • 中间件团队规模不大,约6人
  • 中间件团队熟悉java语言,但有一个新同事C/C++很牛
  • 开发平台是linux,数据库是MySQL
  • 目前整个业务系统是单机房部署,没有双机房

2 设计流程

2.1 识别复杂度

  • 是否需要高性能:假设胖批微博的每天有1000万条微博
    • 那么微博子系统一天会产生1000万条数据,其他子系统总共会有1亿次的消息
    • 但是这是消息总量,高性能更多的是看秒级,如TPS,QPS。计算下来,一天内的平均每秒写入115,每秒读取消息是1150,再考虑请求数并不会是完全平均,所以设计目标是峰值计算,一般取平均3倍,TPS:345,QPS:3450
    • 这个量级不算很高,kafka都是万级,但是也会会增长,流量也会变得更多,为了考虑未来一两年,可以按照4倍峰值计算,最终的TPS:1380,QPS:13800,可以看到每秒1万多次的读取,其实也达到了高性能的要求的,但是使用kafka能应对
  • 是否需要高可用:可以假想如果消息丢失了怎么办
    • 首先当前流量很大,每天1000万条微博,所以如果对于审核子系统,没有审核到某个微博,可能导致触犯法律,或者社会性危害
    • 如果用户达到VIP后,却发现别人有的奖品,自己没有,可能会不满意,且导致用户流失,但是相对来说没有审核那么严重
    • 所以综合来看,消息队列需要高可用,包括数据写入、存储、消息读取都要高可用
  • 是否需要高扩展性:消息队列使用的场景和明确,就是给各个子系统解耦,所以没有扩展需求,因此并不是复杂度关键

综合下来,消息队列的复杂性主要体现在:高性能消息读取高可用消息写入高可用消息存储高可用消息读取

2.2 设计备选方案

  • 备选方案1:采用开源Kafka
    • 是一个成熟的开源消息队列方案、功能强大、性能高、且成熟
  • 备选方案2:集群+MySQL存储
    • 设计要求虽然要求高性能,C++等语言的性能优势是比java大的,但是C++更适合开发高性能中间件,但是为了语言的性能优势而去让团队切换语言是没必要的
    • 所以使用java开发消息队列,例如采用Netty这种java中的高性能网络库来开发消息队列系统,但是QPS达到了13800,单个Netty来支撑还是可能存在问题,所以我们决定采用Netty加上集群的方式来分散QPS的压力,负载均衡就采用简单的轮询即可
    • 这样高性能读取、高可用写入等都能满足,但是高可用存储和高可用读取确实比较复杂
      • 高可用存储要保证:写入的消息在单台服务器宕机时不丢失
      • 高可用读取要保证:写入的消息在单台服务器宕机后能继续读取
    • 可以使用MySQL的主备方案,主备之间数据的复制能保证读取,主备结构可以保证存储
  • 备选方案3:集群+自研存储方案
    • 在2的基础上,将MySQL存储换为了自研的存储方案,因为MySQL的关系型数据特点本身就不是很契合消息队列的数据特点
    • Kafka就是自己实现一套文件存储和复制方案

可以看出高性能消息读取单机系统设计并没有多少方案可选,很大原因是团队的背景约束,java为熟悉语言,适用于高性能中间件的C/C++的人员又少,虽然上面给出了3个方案,比较少,但是现实情况会更复杂,比如开源的MQ还可以选择ActiveMQ,RocketMQ,RabbitMQ,存储方案可以选HBase,Redis+Mysql等

2.3 评估和选择备选方案

参加备选方案的人组织了架构师、研发、测试、运维、几个核心业务主管,人之常情——踢皮球

  • 备选方案1:采用开源的Kafka
    • 支持
      • Kafka比较成熟,且业务团队已经或多或少了解过Kafka,能节省大量的开发投入
      • Kafka比较成熟,无须太多的测试投入
    • 反对
      • 但Kafka设计目的是支撑大容量的日志消息传输,但是当前要解决的问题是业务数据的可靠传输,并且能够保证高性能
      • Kafka由Scala语言编写,运维团队没有维护Scala的经验,很难快速应对问题,且运维有一套运维体系,Kafka很难融入进来
  • 备选方案2:集群+MySQL存储
    • 支持
      • 能有效融入当前运维体系,且MySQL存储数据,可靠性有保障
    • 反对
      • MySQL存储消息数据性能肯定不如文件系统,且会影响团队声誉,毕竟用MySQL做消息队列看起来另类且土
      • 运维又认为成本过高,一个数据分组就需要4台机器(2台Server+2台DB)
      • 测试人力投入大,各方面都需要进行测试
  • 备选方案3:集群+自研存储系统
    • 支持
      • 部分研发人员任务很好,能够展现中间件团队的技术实力,性能相比MySQL要高
    • 反对
      • 一部分研发认为方案复杂度较高,想要达到稳定可靠需要长时间的迭代
      • 运维认为存储系统在没成熟之前,存在各种问题,丢数据等,并且不认为研发能够一上来就解决这种问题
      • 测试和运维一样的意见,测试不一定能够测出全部问题
        环评表如下
质量属性引入KafkaMySQL存储自研存储
性能
复杂度低,开箱即用中,MySQL存储和复制已有,只需要开发服务器集群即可高,自研存储系统复杂度更高
硬件成本高,一个分区就需要2S+2DB低,和Kafka一样
可运维性低,无法融入现有的运维体系高,MySQL运维很成熟,能融入现有体系高,只需维护服务器即可,能融入现有体系
可靠性高,成熟的开源方案高,MySQL存储很成熟低,最初时自研的存储系统的可靠性很难保证
人力投入低,开箱即用中,只需要开发服务器集群高,需要开发服务器集群和存储系统

架构师最终选择了备选方案2

原因

  • 排除备选1:运维性低,会导致新项目上线后,无法快速解决问题,也就意味着整个系统出现停滞的时间会很久,以及出了问题之后的数据丢失问题,且需求是一个可靠传输的MQ
  • 排除备选3:复杂度过高,人又少,可能导致开发周期过长,且开发过程中有其他的开发任务以及中间件维护工作
  • 备选2复杂度不高,可靠性有保障
    • 缺点是性能,但是当前的性能要求不高,即使后续业务量提升了,备选2能支持平行扩展支撑来提高性能下限
    • 成本过高也是一个缺点,备机本身就是为了应对突发故障和备份,可以和其他业务系统部署在同一个机器上
    • 虽然技术上看起来不优越,但是设计的目的首先就是要满足业务需求,并不是证明自己(合适原则)

2.4 细化方案

细化设计1 数据库表如何设计
  • 数据库设计两类表,一类是日志表,用于消息写入时快速存储到M小ySQL中;另一类是消息表,每个消息队列一张表。
  • 业务系统发布消息时,首先写入日志表,日志表写入成功就代表消息写入成功;后台线程再从日志表中读取消息写入记录,将消息内容写入消息表中。
  • 业务系统读取消息时,从消息表中读取。
  • 日志表表名为MQ_LOG,包含的字段:日志IID、发布者信息、发布时间、队列名称、消息内容。
  • 消息表表名就是队列名称,包含的字段:消息ID(递增生成人消息内容、消息发布时间、消息发布者。
  • 日志表需要及时清除已经写入消息表的日志数据,消息表最多保存30天的消息数据。
细化设计2 数据如何复制
  • 直接采用MySQL的主从复制,只复制消息存储表
细化设计3 主备如何倒换
  • 使用ZK做主备决策:主备都在ZK建立节点,主服务器路径规则(/MQ/server/分区编号/master),备机(/MQ/server/分区编号/slave),备机去挂在监听主机节点,主机断开连接后,备机修改自己的状态,对外提供服务
细化设计4 业务服务器如何写入消息
  • 消息队列系统设计两个角色:生产者和消费者,且都有唯一名称
  • 消息队列系统通过SDK提供给其他业务系统调用
    • SDK从配置中读取消息队列系统的服务器信息,使用轮询算法发起消息写入请求,如果发送后无响应(ack)或返回错误,则SDK再次轮询发送
细化设计5 业务服务器如何读取消息
  • 消息队列系统通过SDK提供给其他业务系统调用
    • SDK从配置中读取消息队列系统的服务器信息,轮流向所有服务发起读取请求
  • 消息队列服务器,记录每个消费者状态,即读到哪条消息——index,当接收到读取请求时,就可以根据index返回下一条消息
细化设计6 业务系统和消息队列之间的通信协议
  • 考虑消息队列系统后续可能会对接多种编程语言编写的系统,提高兼容性,就选择了TCP,数据格式为ProtocolBuffer

3 总结

  • 识别复杂度对架构师来说是一项挑战,因为原始的需求中并没有哪个地方会明确地说复杂度在哪里,需要架构师在理解需求的基础上进行分析。
  • 有经验的架构师可能一看需求就知道复杂度大概在哪里,如果经验不足,则只能采取“排查法”,从不同的角度逐一进行分析。
  • 架构师关注的不是一天的数据,而是1秒的数据,即TPS和QPS。
  • 备选方案的选择和很多因素相关,并不单单考虑性能高低、技术是否优越这些纯技术因素,业务的需求特点、运维团队的经验、已有的技术体系、团队人员的技术水平都会影响备选方案的选择。
  • 架构设计目的不是证明自己(参考架构设计原则1:合适原则),而是更快更好地满足业务需求。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.rhkb.cn/news/492900.html

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

【Axure高保真原型】伸缩表单

今天和大家分享伸缩表单的原型模板,效果包括在需要填写内容较多时,可以对填写内容进行分类,然后通过点击上下箭头,收起或展开对应的信息。这个模版里面包含了输入框、下拉列表、选择器、上次图片共多种种常用的元件,后…

InternVL简读

InternVL: Scaling up Vision Foundation Models and Aligning for Generic Visual-Linguistic Tasks 1. Introduction 需要解决的问题: existing VLLMs [5, 81, 131, 177, 187] commonly employ lightweight “glue” layers, such as QFormer [81] or linear pr…

从源码分析swift GCD_DispatchGroup

前言: 最近在写需求的时候用到了DispatchGroup,一直没有深入去学习,既然遇到了那么就总结下吧。。。。 基本介绍: 任务组(DispatchGroup) DispatchGroup 可以将多个任务组合在一起并且监听它们的完成状态。…

AFL-Fuzz 的使用

AFL-Fuzz 的使用 一、工具二、有源码测试三、无源码测试 一、工具 建议安装LLVM并使用afl-clang-fast或afl-clang-lto进行编译,这些工具提供了更现代和高效的插桩技术。您可以按照以下步骤安装LLVM和afl-clang-fast: sudo apt update sudo apt install…

ONES 功能上新|ONES Copilot、ONES Wiki 新功能一览

ONES Copilot 可基于工作项的标题、描述、属性信息,对工作项产生的动态和评论生成总结。 针对不同类型的工作项,总结输出的内容有对应的侧重点。 应用场景: 在一些流程步骤复杂、上下游参与成员角色丰富的场景中,工作项动态往往会…

EasyGBS国标GB28181平台P2P远程访问故障排查指南:客户端角度的排查思路

在现代视频监控系统中,P2P(点对点)技术因其便捷性和高效性而被广泛应用。然而,当用户在使用P2P远程访问时遇到设备不在线或无法访问的问题时,有效的排查方法显得尤为重要。本文将从客户端的角度出发,详细探…

Soul Android端稳定性背后的那些事

前言:移动应用的稳定性对于用户体验和产品商业价值都有着至关重要的作用。应用崩溃会导致关键业务中断、用户留存率下降、品牌口碑变差、生命周期价值下降等影响,甚至会导致用户流失。因此,稳定性是APP质量构建体系中最基本和最关键的一环。当…

mfc140u.dll是什么文件?如何解决mfc140u.dll丢失的相关问题

遇到“mfc140u.dll文件丢失”的错误通常影响应用程序的运行,这个问题主要出现在使用Microsoft Visual C环境开发的软件中。mfc140u.dll是一个重要的系统文件,如果它丢失或损坏,会导致相关程序无法启动。本文将简要介绍几种快速有效的方法来恢…

mybatis分页插件的使用

1. 引入依赖包 <dependency><groupId>com.github.pagehelper</groupId><artifactId>pagehelper</artifactId><version>6.1.0</version> </dependency> 2 添加分页插件配置 2.1 使用配置类的方式&#xff08;推荐&#xff09…

手机便签哪个好用?手机桌面便签app下载推荐

在快节奏的现代生活中&#xff0c;我们常常需要记录一些重要的信息和灵感&#xff0c;以便于日后查阅和回顾。手机便签软件因其便携性和易用性&#xff0c;成为了我们日常生活中不可或缺的工具。无论是购物清单、待办事项、灵感记录还是重要笔记&#xff0c;手机便签都能帮助我…

Zabbix6.0升级为6.4

为了体验一些新的功能&#xff0c;比如 Webhook 和问题抑制等&#xff0c;升级个小版本。 一、环境信息 1. 版本要求 一定要事先查看官方文档&#xff0c;确认组件要求的版本&#xff0c;否则版本过高或者过低都会出现问题。 2. 升级前后信息 环境升级前升级后操作系统CentOS…

怎么将pdf中的某一个提取出来?介绍几种提取PDF中页面的方法

怎么将pdf中的某一个提取出来&#xff1f;传统上&#xff0c;我们可能通过手动截取屏幕或使用PDF阅读器的复制功能来提取信息&#xff0c;但这种方法往往不够精确&#xff0c;且无法保留原文档的排版和格式。此外&#xff0c;很多时候我们需要提取的内容可能涉及多个页面、多个…

LeetCode:101. 对称二叉树

跟着carl学算法&#xff0c;本系列博客仅做个人记录&#xff0c;建议大家都去看carl本人的博客&#xff0c;写的真的很好的&#xff01; 代码随想录 LeetCode&#xff1a;101. 对称二叉树 给你一个二叉树的根节点 root &#xff0c; 检查它是否轴对称。 示例 1&#xff1a; 输…

力扣-图论-18【算法学习day.68】

前言 ###我做这类文章一个重要的目的还是给正在学习的大家提供方向和记录学习过程&#xff08;例如想要掌握基础用法&#xff0c;该刷哪些题&#xff1f;&#xff09;我的解析也不会做的非常详细&#xff0c;只会提供思路和一些关键点&#xff0c;力扣上的大佬们的题解质量是非…

深度学习之目标检测——RCNN

Selective Search 背景:事先不知道需要检测哪个类别,且候选目标存在层级关系与尺度关系 常规解决方法&#xff1a;穷举法&#xff0c;在原始图片上进行不同尺度不同大小的滑窗&#xff0c;获取每个可能的位置 弊端&#xff1a;计算量大&#xff0c;且尺度不能兼顾 Selective …

【计算机视觉基础CV】03-深度学习图像分类实战:鲜花数据集加载与预处理详解

本文将深入介绍鲜花分类数据集的加载与处理方式&#xff0c;同时详细解释代码的每一步骤并给出更丰富的实践建议和拓展思路。以实用为导向&#xff0c;为读者提供从数据组织、预处理、加载到可视化展示的完整过程&#xff0c;并为后续模型训练打下基础。 前言 在计算机视觉的深…

Linux 中的 mkdir 命令:深入解析

在 Linux 系统中&#xff0c;mkdir 命令用于创建目录。它是文件系统管理中最基础的命令之一&#xff0c;广泛应用于日常操作和系统管理中。本文将深入探讨 mkdir 命令的功能、使用场景、高级技巧&#xff0c;并结合 GNU Coreutils 的源码进行详细分析。 1. mkdir 命令的基本用法…

电商数据采集电商,行业数据分析,平台数据获取|稳定的API接口数据

电商数据采集可以通过多种方式完成&#xff0c;其中包括人工采集、使用电商平台提供的API接口、以及利用爬虫技术等自动化工具。以下是一些常用的电商数据采集方法&#xff1a; 人工采集&#xff1a;人工采集主要是通过基本的“复制粘贴”的方式在电商平台上进行数据的收集&am…

排序算法(3)——归并排序、计数排序

目录 1. 归并排序 1.1 递归实现 1.2 非递归实现 1.3 归并排序特性总结 2. 计数排序 代码实现 3. 总结 1. 归并排序 基本思想&#xff1a; 归并排序&#xff08;merge sort&#xff09;是建立在归并操作上的一种有效的排序算法&#xff0c;该算法是采用分治法&#xff0…

android RadioButton + ViewPager+fragment

RadioGroup viewpage fragment 组合显示导航栏 1、首先主界面的布局控件就是RadioGroup viewpage <?xml version"1.0" encoding"utf-8"?> <LinearLayout xmlns:android"http://schemas.android.com/apk/res/android"xmlns:tools…