欢迎光临
专注android技术,聚焦行业精粹,我们一直在努力

架构学习4——复杂度来源

1、前言

首先感谢运华大神的对“复杂度来源”的提炼和总结,这篇文章的题目在行业内也没有标准的答案,但却是在无数次的开发中总结和提炼出的经验,非常的宝贵。本篇文章先记录下运华大神总结的6个复杂度来源,结合自己的理解先记录下来,在以后的开发道路上不断对比自己的总结和大神的总结,会有更深的理解和更好的指导。

想要更多的了解运华大神的思想,还是建议去“极客时间”app购买课程或纸质版书籍。

2、提纲

一个系统的生命周期分为两个部分:“前期阶段”系统的功能,“发展阶段”系统的演进

前期阶段”产品提出一个系统的要求时,主要包含两个目标:基础功能可用性高,体验好。例如Wi-Fi万能钥匙这个app举例,首先它需要保证它的核心功能——连接所有公共Wi-Fi的成功率高,且连接Wi-Fi过程响应快。这个才能让Wi-Fi万能钥匙这款应用快速抢占市场,赢得用户的喜欢。

而对于“发展阶段”产品的方向取决于“前期阶段”的产品是否达到预期,例如DAU达到千万级别以上了(截止2016年6月,WiFi万能钥匙全球总用户量突破9亿、月活跃达5.2亿),那么此时的它需要扩展更多的业务(自有Wi-Fi,低价流量套餐等),同时也要考虑如何降低快速发展的业务带来的巨大成本压力;此时处于风口浪尖的它会接受到更大的安全考验,成为很多的竞品,黑客等的众矢之的;整个系统的代码规模也到了非常巨大的程度,可维护性也成为技术方面需要重点关注的问题。

以上这些论述为我们的系统架构复杂性梳理了一个这样的大纲:

2.1、“前期阶段”

前期阶段更多关注的是核心主打功能稳定且性能高,目标是打出品牌和用户口碑。而这些会换来品牌的提升,抢占了市场份额和获得期望的DAU。

2.2、“发展阶段”

此阶段更多关注的是业务扩展,核心竞争力(减小成本),保障业务安全(防止被攻击,保护用户隐私),以及应对庞大的系统规模。

以上这些点是运华大神的总结,我只是对这些点整理,并提出自己的理解。

3、详细分析

3.1、“前期阶段”

该阶段的产品处于试探阶段,产品希望自己的系统能快速面向用户,且对用户的影响是好的,能让尽量多的用户留在自己的app。所以此阶段对系统的高可用性和好的体验有更高的要求。

3.1.1、高可用

在中国app对“高可用”要求非常强烈,例如:著名的双11如果在0点出现系统崩溃,且10分钟内没有恢复,这对于天猫将会是多大的损失,而在国外“高可用”似乎没有那么强烈。

对于普通的app虽然对“高可用”没有像天猫双11那么强烈,但也非常重要,因为系统不可用到修复这段时间是钱的丢失,例如游戏10点上线,当天因为系统不可用导致1h后才恢复正常,这对于企业而言那也是很严重的,首先游戏应用市场半年前谈的广告为合作可能泡汤了,这可能损失一大笔钱。所以说“高可用”对于app而言非常重要。而对于“高可用”我们主要解决两个问题:

  • 功能不健壮,导致功能不可用
  • 崩溃导致不可用

而解决这两个问题,不单单是测试阶段可以解决的了,我们经常在做架构的时候听到这样的问题:

  • 我们怎么及时发现线上问题
  • 我们有什么办法可以尽快让用户收到最新的修复包
  • 对于特殊场景触发的问题,我们怎么在本地复现
  • 我们系统的兼容性和防呆是否考虑充分,是否覆盖所有用户场景

从中我们提炼出复杂性的来源:

  • 客户端发布出去后,如何及时动态更新
  • android系统碎片化严重,厂商定制度高导致很难兼容
  • 小白用户的错误操作难评估
  • 系统复杂,分支路径多,寻找问题复现路径难

为了尽可能解决这个“高可用”的复杂度的问题,我们需要在系统发布周期的各个阶段都采取针对性措施,这样才能应对任何过程中突如其来不可用问题,发布周期图如下:

我们常见的解决方案大致有以下几种:

  • 尽可能模拟线上环境:系统防呆处理,兼容性测试,monkey测试,单元测试
  • 及时发现线上问题:日报,核心路径监控
  • 快速定位:系统/用户行为日志收集;崩溃前页面栈和上下文logcat日志
  • 及时修复:动态修复,热补丁

3.1.2、高性能

上一节主要保证了功能的可用,但是功能体验不好,也会导致用户流失(12306除外,人家无可替代)。因为现在中国的应用市场不缺乏技术,任何应用都是可替代的,所以如果您的功能体验不好,人家随时可以换掉你。

产品大大也看到这一点,所以高性能也是产品对系统提出的基本要求,而高性能主要解决两个问题:

  • 核心功能用户体验差,导致用户流失
  • 效率低,处理时间长

用打包系统为例,A系统打包要1个小时,B系统打包3分钟,你作为用户你会选择哪一个呢?为了提高性能,我们经常会遇到下面两个复杂性问题:

  • 考虑多进程,多线程,进程间通信,多线程并发,在做架构设计的时候,需要话费很大的精力来结合业务进行分析、判断、选择、组合,这个过程很复杂
  • 系统设计权衡带来的复杂性(系统复杂性高,耦合严重,难以找到关键性能点;系统扩展性高,带来的路径长,导致性能降低)

第1点是我们经常用到的,再不引用新技术的前提下我们想要提升系统的性能,需要结合考虑多进程,多线程,进程通信,多线程并发的问题,这个过程也挺复杂的。

第2点我们也经常遇到,例如对一个已有系统去优化性能,如果它非常复杂且耦合严重,你怎么能轻易定位出到底是哪个最终依赖的逻辑性能低,甚至可能最终你发现所有逻辑性能都不低,但是复杂的相互依赖关系导致任务间相互等待才会导致性能低呢。这个是系统复杂、耦合性高带来的查找性能点的复杂度。

对于第2点,如果系统扩展性很高,例如很多模块都通过插件解耦了,但是插件之间的通信本身也是非常耗时的,插件越多带来的耗时越长,所以对于第2点架构师需要对系统设计权衡,而这个过程也会带来复杂度。

3.1.2.1、高性能例子之——Binder

关于Binder的基础知识,参考这篇文章《插件化基础之Binder和AIDL

我们知道Android系统是基于Linux的,Linux已经提供了管道、消息队列、共享内存和 Socket 等 IPC 机制,但没有一个是安全和高效兼备的进程间通信,所以Android为了设计出更安全,高效的进程通信,在权衡了这几个IPC的特点后,最终选择了基于速度快的内存拷贝机制,在它基础上实现了安全性高,速度快的Binder机制。

可以看出android的Binder机制的性能仅次于共享内存,但它的安全远远超过了共享内存。

我们在实际工作中为了提升系统性能,也会在进程,线程之间,以及它们彼此之间的通信方式反复斟酌,最终设计出一套适合自己系统的交互方式。

3.2、“发展阶段”

此时的产品已经有了固定的用户群体,此时产品有以下几点想法:

  • 吸引跟多的用户:例如定一个小目标希望用户量翻一倍
  • 降低成本:业务量的增大,会带来人力的成本、协作成本,版本迭代的成本

此时的技术也有想法了:

  • 代码量急剧增多,系统显得特别复杂、臃肿,变得难以维护
  • 此时系统初具影响力,开始受到来自高处的关注了,也被其它竞品盯上这块肥肉了。我们需要提升安全,守护自己的成果

3.2.1、产品的思路

3.2.1.1、如何吸引更多的用户?

业务需要提升转化,以UC浏览器为例,它已经把浏览器和搜索做到极致了,再往前走就会遇到瓶颈,毕竟浏览器的盘子就那么大,如果想要用户量再翻一倍,除非从其它的领域把用户转化过来。所以UC浏览器开始做新闻内容和视频等等。那么UC浏览器就需要在之前浏览器这一核心业务上扩展更多新的功能,所以到了这一步,对系统的可扩展性的需求就显得非常强烈了。

技术需要提升可“扩展性”来适应业务的发展,所以系统的可扩展性是为了解决变化带来的措手不及,改动大,风险高,工期长等问题。

它的复杂度体现在以下3点:

  • 不能每个设计点都考虑可扩展性
  • 不能完全不考虑可扩展性
  • 所有的预测都存在出错的可能性

可扩展性不是每个设计都要考虑,那样我们会无从下手,也难以落地。我们需要结合产品的方向去预测,既然是预测就不可能100%准确,也会存在出错的可能性,这也对可扩展性带来了复杂度。最直接的表现是在设计评审的时候,几个设计师为了一个设计点争得面红耳赤,不可开交,此时唯一能解决此局面的是一位丰富经验的架构师或是对未来方向有了明确思路的产品经理才能拍板。

所以我们在做可扩展性设计的时候,有以下两个最直接的方法

  • 剥离变化层和稳定层(不同的人对分层理解不一致)
  • 面向对象的方式提炼抽象层和实现层

我们需要了解我们的产品发展方向,识别出变化的和稳定的(不过不同的人的理解也会不同),这个是最直接最有效的方法,这个方法中我们需要设计变化层和稳定层之间的接口,这个非常重要。

此外另一个在Java中常常用到的面向对象的方式也是专门解决此问题出现的,回顾之前一篇关于架构的历史的文章,我们知道面向对象的思想就是为了解决庞大的业务而提出的。以下引用了百度百科对“面向对象”的解释:

一切事物皆对象,通过面向对象的方式,将现实世界的事物抽象成对象,现实世界中的关系抽象成类、继承,帮助人们实现对现实世界的抽象与数字建模。通过面向对象的方法,更利于用人理解的方式对复杂系统进行分析、设计与编程。同时,面向对象能有效提高编程的效率,通过封装技术,消息机制可以像搭积木的一样快速开发出一个全新的系统。

3.2.1.2、降低成本

系统模块越来越多,出问题的几率也越来越大,然而用户更新一个版本需要经过下载、安装两个步骤,频繁的更新对于用户而言是非常反感,所以很多用户一直使用旧版本,觉得也不错。产品为了让用户能尽快更新到新版本,会通过活动吸引等方式来提升更新率,然后需要很长一段时间才能把旧版本用户升上来,这个成本是很高的。我们知道插件化技术的出现帮助我们解决了这个问题,例如atlas框架可以让一个app成为“超级app”,简单理解为它包含了很多不同的小app,每个不同的小app可以独自发版本,互不影响;插件化也避免了用户安装的过程,动态加载进主app,用户毫无感知。

所以得出一个结论是:降低成本往往需要引入新技术或创造新技术

3.2.2、技术思路

3.2.2.1、规模

业务快速发展带来的一个明显的变化是代码量急剧增多,庞大的代码库会导致系统异常的复杂,此时做架构将会面临巨大的难度,因为“量变引起了质变”

它的复杂度体现在:

  • 功能越来越多,导致系统复杂度指数级上升
  • 数据越来越多,系统复杂度发生质变

3.2.2.2、安全

所谓的安全问题,是app为了保护自己有价值的数据,避免竞争对手的通过漏洞、代码不安全写法等空子窃取自己的信息;也需要防止竞争对手通过不正当途径攻击自己,让自己的系统不可用而导致业务损失。

它的复杂度体现在安全本身就是一个庞大而复杂的技术领域,而且对于任何一个系统,都不能保证它是没有漏洞的,这个持续的与黑客“攻”与“防”的较量永远也不会停止。很多大公司有自己专门的安全研究部门,就是为了保护自己公司的成果,

4、总结

前面文章的学习我们知道“架构设计目的是为了解决系统复杂度带来的问题”,而这篇文章我们看到系统在不同的阶段(这里分为了“前期阶段”和“发展阶段”2个阶段)面临不同的复杂度问题。通过这篇文章的学习,我们能清晰的知道在哪个阶段我们架构设计需要关注哪些复杂度问题,该忽略哪些过度的设计。文章中也有一些应对方法,指导我们如何去解决这些复杂度问题。

这篇文章对架构分析站的高度比较高,宏观的展现了一个系统的生命周期,和架构师在这生命周期中面临的问题,帮助新手了解架构的全貌,避免了初期对架构盲人摸象的困扰。

本篇文章也说明了架构设计不是一蹴而就,也不是生搬硬套别的架构能解决的。好的架构师能因地制宜,对不同的复杂度问题提供不同的解决办法,这样会更适合、更轻量、更敏捷。

架构更多的是一种思维,一种面对问题解决问题的思维方式,没有固定的招式,更多的是见招拆招。我们在实际工作中,建议多用架构的思维去思考和解决系统当前的问题,不要过激,也不要不想,这样对架构学习的帮助会比较大。

赞(1) 打赏
未经允许不得转载:花花鞋 » 架构学习4——复杂度来源
分享到: 更多 (0)

评论 抢沙发

  • 昵称 (必填)
  • 邮箱 (必填)
  • 网址

国内精品Android技术社区

联系我们

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

微信扫一扫打赏