架构设计第 1 步:识别复杂度
在设计软件架构的过程中,识别并理解系统的复杂性是至关重要的一步。这是因为,只有当我们准确地分析出系统面临的主要复杂性时,才能确保架构设计方案的正确性。如果分析失误,无论设计方案多么高级,都可能偏离解决实际问题的正确路径,导致效果不佳。
考虑一个例子:假设一个系统主要的复杂性来源于其业务逻辑的复杂和功能之间的紧密耦合。如果在这种情况下,架构师设计了一个以高吞吐量(TPS为50000/秒)为目标的架构,那么无论这个架构的性能表现有多优秀,它都未能解决系统实际的复杂性问题。
通常,架构的复杂性源于需求对高性能、高可用性、可扩展性等方面的要求。但在分析系统复杂性时,架构师不应该机械地认为所有系统都必须同时满足这些要求。实际上,大多数情况下,复杂性问题主要集中在上述几个方面的某一个或某两个方面。真正同时面临三个或以上复杂性问题的情况非常罕见,这种情况通常意味着之前的系统设计存在问题,或者是架构师的分析判断有误。即便确实面对多重复杂性要求,也应进行优先级排序,逐一解决。
以一个亿级用户平台为例,该平台最初的设计目标是模仿腾讯QQ的用户规模和功能复杂度,导致其设计了超过40个子系统。这种过度设计不仅导致系统过于复杂、运维困难、开发效率低下,而且由于业务并未达到预期的规模,造成了大量资源的浪费。最终,团队不得不花费额外的两年时间进行重构,将子系统数量减半,系统才逐渐趋于稳定。
面对一个同时存在多个复杂度问题的系统,最合理的做法是分步骤解决。首先,明确系统面临的主要复杂度问题,并根据业务需求、技术现状和团队能力进行优先级排序。例如,在上述亿级用户平台的案例中,团队首先将精力集中在减少子系统数量上,这一改进不仅提高了开发效率,还减少了系统的故障率。在此基础上,团队又成功实施了异地多活方案,进一步提升了系统的稳定性。
担心按优先级解决问题可能导致后续需推倒重来的方案是有理论基础的,但在实践中几乎不会发生。软件系统的可塑性和灵活性意味着对于同一问题,通常存在多种解决方案。即使真的需要重构,新方案也应该能够同时解决之前已解决的问题,而这通常依赖于新技术的引入。比如,Hadoop就是一个能够同时处理大数据的高性能、高可用性和大容量问题的技术解决方案。
对架构师而言,识别系统复杂性是一项挑战,需要在深入理解需求的基础上进行全面分析。有经验的架构师可能能迅速洞察复杂性所在,而对于经验较少的架构师,则可能需要通过排查法,从不同角度逐一分析,才能准确把握系统的复杂性。
图片
识别复杂度实战
在创业公司“前浪微博(虚拟)”的快速发展过程中,其系统架构开始显现出效率低下的问题,尤其是在多个业务子系统间的协作上。以发布微博为例,从审核到统计、广告预测再到消息推送,微博子系统需要与十几个其他系统进行接口调用,每增加一个通知,就意味着额外的接口设计和测试工作,这大大降低了开发效率,同时使问题定位变得异常复杂。同样的情况也出现在用户升级至VIP等级时,等级子系统需要通知诸如福利、客服、商品子系统等多个系统进行相应处理。
针对这些挑战,一位新加入的架构师提出引入消息队列系统来解耦各个子系统的直接依赖。经过一系列的分析、讨论和审批后,该提案得到了批准。
在对消息队列系统的需求进行深入分析时,中间件团队采用了“排查法”来确定其面临的主要复杂性。首先关注的是性能问题。假设前浪微博每天产生1000万条微博消息,每条微博消息需被十个子系统处理,这意味着日总消息处理量约为1亿次。将这个数字分解到每秒,意味着平均每秒需处理115条写入消息和1150条读取消息。但为了应对峰值,设计目标通常设定为平均值的三倍,即TPS(每秒事务处理量)为345,QPS(每秒查询处理量)为3450。即使如此,考虑到业务增长,性能设计目标被进一步设定为峰值的四倍,即TPS为1380,QPS为13800,以保证系统对未来业务增长的充分准备。
接下来是高可用性的需求分析。考虑到消息丢失可能带来的严重后果,如审核系统的消息丢失可能导致违反法规,VIP等级奖励的不发放可能导致用户不满,消息队列在写入、存储和读取各环节都需保证高度的可靠性。
至于可扩展性,鉴于消息队列的功能较为固定,当前看来并不是主要的复杂度所在。
综合上述分析,消息队列系统面临的主要复杂性在于高性能的消息读取和全流程的高可用性保障。这次详细的分析和讨论,不仅适用于“前浪微博”面临的挑战,也为其他企业提供了一种系统性问题解决的框架。需要注意的是,这里的性能目标设定为峰值的四倍主要是基于对业务增长速度的预估,并不是一个固定的倍数,不同业务场景下的预估倍数可能会有所不同