first commit

This commit is contained in:
张乾
2024-10-16 06:37:41 +08:00
parent 633f45ea20
commit 206fad82a2
3590 changed files with 680090 additions and 0 deletions

View File

@@ -0,0 +1,59 @@
因收到Google相关通知网站将会择期关闭。相关通知内容
00 开篇词 照着做,你也能成为架构师!
每个程序员心中都有一个成为架构师的梦想,梦想是美好的,但道路是曲折的。
我大概在2006年开始参与架构设计原本以为学习架构设计就像学习一门编程语言一样先学习一下基本的语法再研究一下细节和原理然后实践一下就能够快速掌握。但真正实践后才发现架构设计的难度和复杂度要高很多。从最早开始接触架构设计到自我感觉初步完整掌握架构设计至少花费了6年时间。等到自我感觉彻底掌握架构设计的精髓至少花费了8年的时间当然这个过程中我不是一直在做架构设计
我曾经以为是自己天资愚笨才会这样,后来我带了团队,看到几乎每个程序员在尝试架构设计的时候,都面临着我曾经遇到过的各种困惑和瓶颈。特别是我作为职业等级晋升评委的时候,发现很多同学技术能力很强,业务也很不错,但却卡在了架构设计这部分。我意识到这应该不是个人天资的问题,而是架构设计本身的一些特性导致的。
我总结几个架构设计相关的特性:
1.架构设计的思维和程序设计的思维差异很大。
架构设计的关键思维是判断和取舍,程序设计的关键思维是逻辑和实现。很多程序员在转换为架构师后,很难一开始就意识到这个差异,还是按照写代码的方式去思考架构,会导致很多困惑。
2.架构设计没有体系化的培训和训练机制。
大学的课程几乎没有架构设计相关的课程,架构设计的书籍更多的也只是关注某个架构设计点,没有体系化的架构设计书籍,导致程序员在学习上没有明确指导,只能自己慢慢摸索,效率低,容易踩坑。
3.程序员对架构设计的理解存在很多误区。
例如:要成为架构师必须要有很强的技术天分;架构师必须有很强的创造力;架构设计必须要高大上才能体现架构师能力;架构一定要具备高可用、高性能……这些似是而非的误区让很多技术人员望而生畏,还没尝试就已经放弃了。
得益于移动互联网技术的快速发展我在加入UC后有很多的机会直接参与架构设计这些架构背后的业务形形色色包括社交、电商、游戏、中间件、内部运营系统用到的技术栈差异也比较大包括PHPJava、C++等。虽然每次架构设计对我来说都是一个新的挑战,但正好也提供了非常好的机会,让我亲身体验不同的架构设计。在这个过程中,我不断学习、思考、实践、总结、改进、交流,逐步形成了自己的一套架构设计方法论。
有了这套方法论后,首先,我自己在做架构设计的时候游刃有余,不管什么样的业务,不管什么样的技术,按照这套方法论都能够设计出优秀的架构。在职业等级面评的时候,就算我之前从来没有接触过对方的业务,也能快速理解对方描述的架构和发现其中做得好或者做得不好的地方;其次,在指导其他同事的时候效果明显。原来对架构设计比较迷茫的同学,通过几次结合案例进行的方法论培训,都能够很快地掌握这套方法论并在实践中应用。甚至有很多其他业务线的同学,遇到架构设计的困惑,也来找我交流和指导。
我是一个很喜欢分享的人经常在InfoQ写文章、在知乎写回答当看到别人在经过我的指导后恍然大悟甚至醍醐灌顶的那种神态或者发自内心由衷感谢的时候我自己也会很有成就感。我在极客时间的专栏《从0开始学架构》将与你分享我的架构设计方法论希望能够帮助更多怀揣架构师梦想的同学早日实现自己的梦想。
这个专栏涵盖了我的整套架构设计方法论和架构实践,主要包括以下内容。
架构基础:我会先介绍架构设计的本质、历史背景和目的,然后从复杂度来源以及架构设计的原则和流程来详细介绍架构基础。
高性能架构模式:我会从存储高性能、计算高性能方面,介绍几种设计方案的典型特征和应用场景。
高可用架构模式我会介绍CAP原理、FMEA分析方法分析常见的高可用存储架构和高可用计算架构并给出一些设计方法和技巧。
可扩展架构模式:我会介绍可扩展模式及其基本思想,分析一些常见架构模式。
架构实战:我会将理论和案例结合,帮助你落地前面提到的架构原则、架构流程和架构模式。
通过本专栏的学习,你会收获:
清楚地理解架构设计相关的概念、本质、目的,避免架构师在实践过程中把握不住重点、分不清主次,眉毛胡子一把抓,导致架构设计变形或者“四不像” 。
掌握通用的架构设计原则,无论是何种业务或技术,架构师在判断和选择的时候有一套方法论可以参考,避免架构设计举棋不定,或者拍脑袋式设计。
掌握标准的架构设计流程,即使是刚开始做架构设计的新手,也能够按照步骤一步一步设计出合适的架构,避免某些步骤缺失导致错误的架构设计。
深入理解已有的架构模式,做到能够根据架构特点快速挑选合适的模式完成架构设计,或者在已有的模式上进行创新,或者将已有的模式组合出新的架构。
掌握架构演进和开源系统使用的一些技巧。
好的开始是成功的一半,希望专栏的内容能够有效地帮助你更快地掌握架构设计的技巧,更好地设计出优秀的架构,实现自己心中的技术梦想!
毕竟,只要你努力,技术的梦想一定会实现!

View File

@@ -0,0 +1,197 @@
因收到Google相关通知网站将会择期关闭。相关通知内容
01 架构到底是指什么?
你好,我是华仔。
2018年我发布了《从0开始学架构》这门课程分享了我之前在电信业务和移动互联网业务方面的经验和感悟。
后来我转岗去了蚂蚁国际从事更加复杂的支付业务。为什么说支付业务更加复杂因为它涉及的关联方多、业务流程长、业务模型复杂对安全、高可用、高性能等都有更高的要求。我有幸参与了一个海外钱包从0到1的建设过程积累了不少实战经验于是对复杂业务的架构设计有了新的理解。
2020年因为身体等原因我离开了蚂蚁国际之后就一边休养身体一边系统地总结梳理过去的经验先后创作了《大厂晋升指南》和《架构实战营》。在打磨课程和与各位同学交流的过程中我对于架构学习的难点和应用的痛点又有了新的心得体会。
因此,我决定更新这门课程的部分内容,把这些新的收获也全部分享出来,希望能帮助你与时俱进地提升架构水平。
架构到底是指什么
对于技术人员来说“架构”是一个再常见不过的词了。我们会对新员工培训整个系统的架构参加架构设计评审学习业界开源系统例如MySQL和Hadoop的架构研究大公司的架构实现例如微信架构和淘宝架构……
虽然“架构”这个词很常见,但如果深究一下,“架构”到底是指什么,大部分人就搞不清楚了。例如以下这些问题,你能够准确地回答吗?
微信有架构,微信的登录系统也有架构,微信的支付系统也有架构,当我们谈微信架构时,到底是在谈什么架构?
Linux有架构MySQL有架构JVM也有架构使用Java开发、MySQL存储、跑在Linux上的业务系统也有架构应该关注哪个架构呢
架构和框架是什么关系?有什么区别?
身为架构师,如果你连架构的定义都搞不清楚,那么无论是自己设计架构、给别人讲解架构,还是学习别人的架构,都会暴露问题,要么无从下手,要么张冠李戴。这无疑会成为你面试、晋升和带领团队工作时的绊脚石。
比如有些同学明明在系统架构上做了不少有价值的工作,但是在给晋升面试的评委讲解的时候,只会说“我们是微服务架构”,然后就不知道讲什么了。结果得到的评价大打折扣,晋升失败,非常可惜。
要想准确地理解架构的定义,关键就在于把三组容易混淆的概念梳理清楚:
系统与子系统
模块与组件
框架与架构
系统与子系统
我们先来看维基百科定义的“系统”:
系统泛指由一群有关联的个体组成,根据某种规则运作,能完成个别元件不能单独完成的工作的群体。它的意思是“总体”“整体”或“联盟”。
我来提炼一下里面的关键内容。
关联系统是由一群有关联的个体组成的没有关联的个体堆在一起不能成为一个系统。例如把一个发动机和一台PC放在一起不能称之为一个系统把发动机、底盘、轮胎、车架组合起来才能成为一台汽车。
规则:系统内的个体需要按照指定的规则运作,而不是单个个体各自为政。规则规定了系统内个体分工和协作的方式。例如,汽车发动机负责产生动力,然后通过变速器和传动轴,将动力输出到车轮上,从而驱动汽车前进。
能力:系统能力与个体能力有本质的差别,系统能力不是个体能力之和,而是产生了新的能力。例如,汽车能够载重前进,而发动机、变速器、传动轴、车轮本身都不具备这样的能力。
我们再来看子系统的定义:
子系统也是由一群有关联的个体所组成的系统,多半会是更大系统中的一部分。
其实,子系统的定义和系统定义是一样的,只是观察的角度有差异,一个系统可能是另外一个更大系统的子系统。
按照这个定义,系统和子系统比较容易理解,我们以微信为例来做一个分析:
微信本身是一个系统,包含聊天、登录、支付、朋友圈等子系统。
朋友圈这个系统又包括动态、评论、点赞等子系统。
评论这个系统可能又包括防刷子系统、审核子系统、发布子系统、存储子系统。
评论审核子系统不再包含业务意义上的子系统而是包括各个模块或者组件这些模块或者组件本身也是另外一个维度上的系统。例如MySQL、Redis等是存储系统但不是业务子系统。
现在,我们可以回答第一个问题了。一个系统的架构,只包括顶层这一个层级的架构,而不包括下属子系统层级的架构。所以微信架构,就是指微信系统这个层级的架构。当然,微信的子系统,比如支付系统,也有它自己的架构,同样只包括顶层。
模块与组件
模块和组件两个概念在实际工作中很容易混淆,我们经常能够听到类似这样的说法:
MySQL模块主要负责存储数据而Elasticsearch模块主要负责数据搜索。
我们有安全加密组件、有审核组件。
App的下载模块使用了第三方的组件。
造成这种现象的主要原因是,模块与组件的定义并不好理解,也不能很好地进行区分。我们来看看这两者在维基百科上的定义:
软件模块Module是一套一致而互相有紧密关连的软件组织。它分别包含了程序和数据结构两部分。现代软件开发往往利用模块作为合成的单位。模块的接口表达了由该模块提供的功能和调用它时所需的元素。模块是可能分开被编写的单位。这使它们可再用和允许人员同时协作、编写及研究不同的模块。
软件组件定义为自包含的、可编程的、可重用的、与语言无关的软件单元,软件组件可以很容易被用于组装应用程序中。
可能你看完这两个定义后一头雾水,还是不知道这两者有什么区别。造成这种现象的根本原因是,模块和组件都是系统的组成部分,只是从不同的角度拆分系统而已。
从业务逻辑的角度来拆分系统后,得到的单元就是“模块”;从物理部署的角度来拆分系统后,得到的单元就是“组件”。划分模块的主要目的是职责分离;划分组件的主要目的是单元复用。
其实“组件”的英文Component也可翻译成中文的“零件”一词。“零件”更容易理解一些它是一个物理的概念并且具备“独立且可替换”的特点。
我以一个最简单的网站系统来为例。假设我们要做一个学生信息管理系统这个系统从逻辑的角度来拆分可以分为“登录注册模块”“个人信息模块”和“个人成绩模块”从物理的角度来拆分可以拆分为Nginx、Web服务器和MySQL。
现在我们可以回答第二个问题了。如果你是业务系统的架构师首先需要思考怎么从业务逻辑的角度把系统拆分成一个个模块角色其次需要思考怎么从物理部署的角度把系统拆分成组件角色例如选择MySQL作为存储系统。但是对于MySQL内部的体系架构Parser、Optimizer、Caches&Buffers和Storage Engines等你其实是可以不用关注的也不需要在你的业务系统架构中展现这些内容。
框架与架构
框架是和架构比较相似的概念,且两者有较强的关联关系,所以在实际工作中,这两个概念有时我们容易分不清楚。参考维基百科上框架与架构的定义,我来解释两者的区别。
软件框架Software framework通常指的是为了实现某个业界标准或完成特定基本任务的软件组件规范也指为了实现某个软件组件规范时提供规范所要求之基础功能的软件产品。
我来提炼一下其中关键部分:
框架是组件规范例如MVC就是一种最常见的开发规范类似的还有MVP、MVVM、J2EE等框架。
框架提供基础功能的产品例如Spring MVC是MVC的开发框架除了满足MVC的规范Spring提供了很多基础功能来帮助我们实现功能包括注解@Controller等、Spring Security、Spring JPA等很多基础功能。
软件架构指软件系统的“基础结构”,创造这些基础结构的准则,以及对这些结构的描述。
单纯从定义的角度来看,框架和架构的区别还是比较明显的:框架关注的是“规范”,架构关注的是“结构”。
框架的英文是Framework架构的英文是ArchitectureSpring MVC的英文文档标题就是“Web MVC framework”。
虽然如此,在实际工作中我们却经常碰到一些似是而非的说法,例如:
我们的系统是MVC架构。
我们需要将Android App重构为MVP架构。
我们的系统基于SSH框架开发。
我们是SSH的架构。
XX系统是基于Spring MVC框架开发标准的MVC架构。
……
究竟什么说法是对的,什么说法是错的呢?
其实这些说法都是对的。造成这种现象的根本原因隐藏于架构的定义中,关键就是“基础结构”这个概念,并没有明确说是从什么角度来分解的。采用不同的角度或者维度,可以将系统划分为不同的结构,其实我在“模块与组件”中的“学生管理系统”示例已经包含了这点。
从业务逻辑的角度分解,“学生管理系统”的架构是:
从物理部署的角度分解,“学生管理系统”的架构是:
从开发规范的角度分解“学生管理系统”可以采用标准的MVC框架来开发因此架构又变成了MVC架构
这些“架构”都是“学生管理系统”正确的架构只是从不同的角度来分解而已这也是IBM的RUP将软件架构视图分为著名的“4+1视图”的原因。
现在,我们可以回答第三个问题了。框架是一整套开发规范,架构是某一套开发规范下的具体落地方案,包括各个模块之间的组合关系以及它们协同起来完成功能的运作规则。
重新定义架构4R架构
参考维基百科的定义再结合我自己的一些理解和思考我将软件架构重新定义为软件架构指软件系统的顶层Rank结构它定义了系统由哪些角色Role组成角色之间的关系Relation和运作规则Rule
因为这个定义中的4个关键词都可以用R开头的英文单词来表示分别是Rank、Role、Relation和Rule所以我把定义简称为“4R架构定义”每个R的详细解释如下。
第一个RRank。它是指软件架构是分层的对应“系统”和“子系统”的分层关系。通常情况下我们只需要关注某一层的架构最多展示相邻两层的架构而不需要把每一层的架构全部糅杂在一起。无论是架构设计还是画架构图都应该采取“自顶向下逐步细化”的方式。以微信为例Rank的含义如下所示
L0\L1\L2指层级一个L0往下可以分解多个L1一个L1可以往下分解多个L2以此类推一般建议不超过5层L0~L4
第二个RRole。它是指软件系统包含哪些角色每个角色都会负责系统的一部分功能。架构设计最重要的工作之一就是将系统拆分为多个角色。最常见的微服务拆分其实就是将整体复杂的业务系统按照业务领域的方式拆分为多个微服务每个微服务就是系统的一个角色。
第三个RRelation。它是指软件系统的角色之间的关系对应到架构图中其实就是连接线角色之间的关系不能乱连任何关系最后都需要代码来实现包括连接方式HTTP、TCP、UDP和串口等、数据协议JSON、XML和二进制等以及具体的接口等。
第四个RRule。它是指软件系统角色之间如何协作来完成系统功能。我们在前面解读什么是“系统”的时候提到过系统能力不是个体能力之和而是产生了新的能力。那么这个新能力具体如何完成的呢具体哪些角色参与了这个新能力呢这就是Rule所要表达的内容。在架构设计的时候核心的业务场景都需要设计Rule。
在实际工作中为了方便理解Rank、Role和Relation是通过系统架构图来展示的而Rule是通过系统序列图System Sequence Diagram来展示的。
我们以一个简化的支付系统为例,支付系统架构图如下所示:
“扫码支付”这个核心场景的系统序列图如下所示:
小结
今天我为你梳理了与架构有关的几个容易混淆的概念包括系统与子系统、模块与组件、框架与架构并且提炼出了4R架构定义希望对你有所帮助。
这就是今天的全部内容,留一道思考题给你吧。你原来理解的架构是如何定义的?对比我今天讲的架构定义,你觉得差异在哪里?
欢迎你把答案写到留言区,和我一起讨论。相信经过深度思考的回答,也会让你对知识的理解更加深刻。(编辑乱入:精彩的留言有机会获得丰厚福利哦!)

View File

@@ -0,0 +1,142 @@
因收到Google相关通知网站将会择期关闭。相关通知内容
02 架构设计的历史背景
理解了架构的有关概念和定义之后,今天,我会给你讲讲架构设计的历史背景。我认为,如果想要深入理解一个事物的本质,最好的方式就是去追寻这个事物出现的历史背景和推动因素。我们先来简单梳理一下软件开发进化的历史,探索一下软件架构出现的历史背景。
机器语言1940年之前
最早的软件开发使用的是“机器语言”直接使用二进制码0和1来表示机器可以识别的指令和数据。例如在8086机器上完成“s=768+12288-1280”的数学运算机器码如下
101100000000000000000011
000001010000000000110000
001011010000000000000101
不用多说不管是当时的程序员还是现在的程序员第一眼看到这样一串东西时肯定是一头雾水因为这实在是太难看懂了这还只是一行运算如果要输出一个“hello world”面对几十上百行这样的0/1串眼睛都要花了
看都没法看更何况去写这样的程序如果不小心哪个地方敲错了将1敲成了0例如
101100000000000000000011
000001010000000000110000
001011000000000000000101
如果要找出这个程序中的错误,程序员的心里阴影面积有多大?
归纳一下,机器语言的主要问题是三难:太难写、太难读、太难改!
汇编语言20世纪40年代
为了解决机器语言编写、阅读、修改复杂的问题汇编语言应运而生。汇编语言又叫“符号语言”用助记符代替机器指令的操作码用地址符号Symbol或标号Label代替指令或操作数的地址。
例如为了完成“将寄存器BX的内容送到AX中”的简单操作汇编语言和机器语言分别如下。
机器语言1000100111011000
汇编语言mov ax,bx
相比机器语言来说汇编语言就清晰得多了。mov是操作ax和bx是寄存器代号mov ax,bx语句基本上就是“将寄存器BX的内容送到AX”的简化版的翻译即使不懂汇编单纯看到这样一串语言至少也能明白大概意思。
汇编语言虽然解决了机器语言读写复杂的问题但本质上还是面向机器的因为写汇编语言需要我们精确了解计算机底层的知识。例如CPU指令、寄存器、段地址等底层的细节。这对于程序员来说同样很复杂因为程序员需要将现实世界中的问题和需求按照机器的逻辑进行翻译。例如对于程序员来说在现实世界中面对的问题是4 + 6 = ?。而要用汇编语言实现一个简单的加法运算,代码如下:
.section .data
a: .int 10
b: .int 20
format: .asciz "%d\n"
.section .text
.global _start
_start:
movl a, %edx  
addl b, %edx  
pushl %edx
pushl $format
call printf
movl $0, (%esp)
call exit
这还只是实现一个简单的加法运算所需要的汇编程序,可以想象一下,实现一个四则运算的程序会更加复杂,更不用说用汇编写一个操作系统了!
除了编写本身复杂还有另外一个复杂的地方在于不同CPU的汇编指令和结构是不同的。例如Intel的CPU和Motorola的CPU指令不同同样一个程序为Intel的CPU写一次还要为Motorola的CPU再写一次而且指令完全不同。
高级语言20世纪50年代
为了解决汇编语言的问题计算机前辈们从20世纪50年代开始又设计了多个高级语言最初的高级语言有下面几个并且这些语言至今还在特定的领域继续使用。
Fortran1955年名称取自”FORmula TRANslator”即公式翻译器由约翰·巴科斯John Backus等人发明。
LISP1958年名称取自”LISt Processor”即枚举处理器由约翰·麦卡锡John McCarthy等人发明。
Cobol1959年名称取自”Common Business Oriented Language”即通用商业导向语言由葛丽丝·霍普Grace Hopper发明。
为什么称这些语言为“高级语言”呢?原因在于这些语言让程序员不需要关注机器底层的低级结构和逻辑,而只要关注具体的问题和业务即可。
还是以4 + 6=这个加法为例如果用LISP语言实现只需要简单一行代码即可
(+ 4 6)
除此以外通过编译程序的处理高级语言可以被编译为适合不同CPU指令的机器语言。程序员只要写一次程序就可以在多个不同的机器上编译运行无须根据不同的机器指令重写整个程序。
第一次软件危机与结构化程序设计20世纪60年代~20世纪70年代
高级语言的出现解放了程序员但好景不长随着软件的规模和复杂度的大大增加20世纪60年代中期开始爆发了第一次软件危机典型表现有软件质量低下、项目无法如期完成、项目严重超支等因为软件而导致的重大事故时有发生。例如1963年美国http://en.wikipedia.org/wiki/Mariner_1的水手一号火箭发射失败事故就是因为一行FORTRAN代码错误导致的。
软件危机最典型的例子莫过于IBM的System/360的操作系统开发。佛瑞德·布鲁克斯Frederick P. Brooks, Jr.作为项目主管率领2000多个程序员夜以继日地工作共计花费了5000人一年的工作量写出将近100万行的源码总共投入5亿美元是美国的“曼哈顿”原子弹计划投入的1/4。尽管投入如此巨大但项目进度却一再延迟软件质量也得不到保障。布鲁克斯后来基于这个项目经验而总结的《人月神话》一书成了畅销的软件工程书籍。
为了解决问题在1968、1969年连续召开两次著名的NATO会议会议正式创造了“软件危机”一词并提出了针对性的解决方法“软件工程”。虽然“软件工程”提出之后也曾被视为软件领域的银弹但后来事实证明软件工程同样无法根除软件危机只能在一定程度上缓解软件危机。
差不多同一时间“结构化程序设计”作为另外一种解决软件危机的方案被提了出来。艾兹赫尔·戴克斯特拉Edsger Dijkstra于1968年发表了著名的《GOTO有害论》论文引起了长达数年的论战并由此产生了结构化程序设计方法。同时第一个结构化的程序语言Pascal也在此时诞生并迅速流行起来。
结构化程序设计的主要特点是抛弃goto语句采取“自顶向下、逐步细化、模块化”的指导思想。结构化程序设计本质上还是一种面向过程的设计思想但通过“自顶向下、逐步细化、模块化”的方法将软件的复杂度控制在一定范围内从而从整体上降低了软件开发的复杂度。结构化程序方法成为了20世纪70年代软件开发的潮流。
第二次软件危机与面向对象20世纪80年代
结构化编程的风靡在一定程度上缓解了软件危机,然而随着硬件的快速发展,业务需求越来越复杂,以及编程应用领域越来越广泛,第二次软件危机很快就到来了。
第二次软件危机的根本原因还是在于软件生产力远远跟不上硬件和业务的发展。第一次软件危机的根源在于软件的“逻辑”变得非常复杂,而第二次软件危机主要体现在软件的“扩展”变得非常复杂。结构化程序设计虽然能够解决(也许用“缓解”更合适)软件逻辑的复杂性,但是对于业务变化带来的软件扩展却无能为力,软件领域迫切希望找到新的银弹来解决软件危机,在这种背景下,面向对象的思想开始流行起来。
面向对象的思想并不是在第二次软件危机后才出现的早在1967年的Simula语言中就开始提出来了但第二次软件危机促进了面向对象的发展。面向对象真正开始流行是在20世纪80年代主要得益于C++的功劳后来的Java、C#把面向对象推向了新的高峰。到现在为止,面向对象已经成为了主流的开发思想。
虽然面向对象开始也被当作解决软件危机的银弹,但事实证明,和软件工程一样,面向对象也不是银弹,而只是一种新的软件方法而已。
软件架构的历史背景
虽然早在20世纪60年代戴克斯特拉这位上古大神就已经涉及软件架构这个概念了但软件架构真正流行却是从20世纪90年代开始的由于在Rational和Microsoft内部的相关活动软件架构的概念开始越来越流行了。
与之前的各种新方法或者新理念不同的是,“软件架构”出现的背景并不是整个行业都面临类似相同的问题,“软件架构”也不是为了解决新的软件危机而产生的,这是怎么回事呢?
卡内基·梅隆大学的玛丽·肖Mary Shaw和戴维·加兰David Garlan对软件架构做了很多研究他们在1994年的一篇文章《软件架构介绍》An Introduction to Software Architecture中写到
“When systems are constructed from many components, the organization of the overall system-the software architecture-presents a new set of design problems.”
简单翻译一下:随着软件系统规模的增加,计算相关的算法和数据结构不再构成主要的设计问题;当系统由许多部分组成时,整个系统的组织,也就是所说的“软件架构”,导致了一系列新的设计问题。
这段话很好地解释了“软件架构”为何先在Rational或者Microsoft这样的大公司开始逐步流行起来。因为只有大公司开发的软件系统才具备较大规模而只有规模较大的软件系统才会面临软件架构相关的问题例如
系统规模庞大,内部耦合严重,开发效率低;
系统耦合严重,牵一发动全身,后续修改和扩展困难;
系统逻辑复杂,容易出问题,出问题后很难排查和修复。
软件架构的出现有其历史必然性。20世纪60年代第一次软件危机引出了“结构化编程”创造了“模块”概念20世纪80年代第二次软件危机引出了“面向对象编程”创造了“对象”概念到了20世纪90年代“软件架构”开始流行创造了“组件”概念。我们可以看到“模块”“对象”“组件”本质上都是对达到一定规模的软件进行拆分差别只是在于随着软件的复杂度不断增加拆分的粒度越来越粗拆分的层次越来越高。
《人月神话》中提到的IBM 360大型系统开发时间是1964年那个时候结构化编程都还没有提出来更不用说软件架构了。如果IBM 360系统放在20世纪90年代开发不管是质量还是效率、成本都会比1964年开始做要好得多当然这样的话我们可能就看不到《人月神话》了。
小结
今天我为你回顾了软件开发进化的历史,以及软件架构出现的历史背景,从历史发展的角度,希望对你深入了解架构设计的本质有所帮助。
这就是今天的全部内容,留一道思考题给你吧。为何结构化编程、面向对象编程、软件工程、架构设计最后都没有成为软件领域的银弹?
欢迎你把答案写到留言区,和我一起讨论。相信经过深度思考的回答,也会让你对知识的理解更加深刻。(编辑乱入:精彩的留言有机会获得丰厚福利哦!)

View File

@@ -0,0 +1,153 @@
因收到Google相关通知网站将会择期关闭。相关通知内容
03 架构设计的目的
周二,我们聊了架构出现的历史背景和推动因素。以史为鉴,对我们了解架构设计的目的很有帮助。谈到架构设计,相信每个技术人员都是耳熟能详,但如果深入探讨一下,“为何要做架构设计?”或者“架构设计目的是什么?”类似的问题,大部分人可能从来没有思考过,或者即使有思考,也没有太明确可信的答案。
架构设计的误区
关于架构设计的目的,常见的误区有:
因为架构很重要,所以要做架构设计
这是一句正确的废话,架构是很重要,但架构为何重要呢?
例如:不做架构设计系统就跑不起来么?
其实不然,很多朋友尤其是经历了创业公司的朋友可能会发现,公司的初始产品可能没有架构设计,大伙撸起袖子简单讨论一下就开始编码了,根本没有正规的架构设计过程,而且也许产品开发速度还更快,上线后运行也还不错。
例如:做了架构设计就能提升开发效率么?
也不尽然,实际上有时候最简单的设计开发效率反而是最高的,架构设计毕竟需要投入时间和人力,这部分投入如果用来尽早编码,项目也许会更快。
例如:设计良好的架构能促进业务发展么?
好像有一定的道理例如设计高性能的架构能够让用户体验更好但反过来想我们照抄微信的架构业务就能达到微信的量级么肯定不可能不要说达到微信的量级达到微信的1/10做梦都要笑醒了。
不是每个系统都要做架构设计吗
这其实是知其然不知其所以然,系统确实要做架构设计,但还是不知道为何要做架构设计,反正大家都要做架构设计,所以做架构设计肯定没错。
这样的架构师或者设计师很容易走入生搬硬套业界其他公司已有架构的歧路,美其名曰“参考”“微改进”。一旦强行引入其他公司架构后,很可能会发现架构水土不服,或者运行起来很别扭等各种情况,最后往往不得不削足适履,或者不断重构,甚至无奈推倒重来。
公司流程要求系统开发过程中必须有架构设计
与此答案类似还有因为“架构师总要做点事情”,所以要做架构设计,其实都是舍本逐末。因为流程有规定,所以要做架构设计;因为架构师要做事,所以要做架构设计,这都是很表面地看问题,并没有真正理解为何要做架构设计,而且很多需求并不一定要进行架构设计。如果认为架构师一定要找点事做,流程一定要进行架构设计,就会出现事实上不需要架构设计但形式上却继续去做架构设计,不但浪费时间和人力,还会拖慢整体的开发进度。
为了高性能、高可用、可扩展,所以要做架构设计
能够给出这个答案说明已经有了一定的架构经历或者基础毕竟确实很多架构设计都是冲着高性能、高可用……等“高XX”的目标去的。
但往往持有这类观点的架构师和设计师会给项目带来巨大的灾难这绝不是危言耸听而是很多实际发生的事情为什么会这样呢因为这类架构师或者设计师不管三七二十一不管什么系统也不管什么业务上来就要求“高性能、高可用、高扩展”结果就会出现架构设计复杂无比项目落地遥遥无期团队天天吵翻天……等各种让人抓狂的现象费尽九牛二虎之力将系统整上线却发现运行不够稳定经常出问题出了问题很难解决加个功能要改1个月……等各种继续让人抓狂的事件。
架构设计的真正目的
那架构设计的真正目的究竟是什么?
从周二与你分享的架构设计的历史背景,可以看到,整个软件技术发展的历史,其实就是一部与“复杂度”斗争的历史,架构的出现也不例外。简而言之,架构也是为了应对软件系统复杂度而提出的一个解决方案,通过回顾架构产生的历史背景和原因,我们可以基本推导出答案:架构设计的主要目的是为了解决软件系统复杂度带来的问题。
这个结论虽然很简洁,但却是架构设计过程中需要时刻铭记在心的一条准则,为什么这样说呢?
首先,遵循这条准则能够让“新手”架构师心中有数,而不是一头雾水。
新手架构师开始做架构设计的时候心情都很激动希望大显身手甚至恨不得一出手就设计出世界上最牛的XX架构从此走上人生巅峰但真的面对具体的需求时往往都会陷入一头雾水的状态
“这么多需求,从哪里开始下手进行架构设计呢?”。
“架构设计要考虑高性能、高可用、高扩展……这么多高XX全部设计完成估计要1个月但老大只给了1周时间”。
“业界A公司的架构是XB公司的方案是Y两个差别比较大该参考哪一个呢”。
以上类似问题,如果明确了“架构设计是为了解决软件复杂度”原则后,就很好回答。
“这么多需求,从哪里开始下手进行架构设计呢?”
——通过熟悉和理解需求,识别系统复杂性所在的地方,然后针对这些复杂点进行架构设计。
“架构设计要考虑高性能、高可用、高扩展……这么多高XX全部设计完成估计要1个月但老大只给了1周时间”
——架构设计并不是要面面俱到,不需要每个架构都具备高性能、高可用、高扩展等特点,而是要识别出复杂点然后有针对性地解决问题。
“业界A公司的架构是XB公司的方案是Y两个差别比较大该参考哪一个呢
——理解每个架构方案背后所需要解决的复杂点,然后才能对比自己的业务复杂点,参考复杂点相似的方案。
其次,遵循这条准则能够让“老鸟”架构师有的放矢,而不是贪大求全。
技术人员往往都希望自己能够做出最牛的东西,架构师也不例外,尤其是一些“老鸟”架构师,为了证明自己的技术牛,可能会陷入贪大求全的焦油坑而无法自拔。例如:
“我们的系统一定要做到每秒TPS 10万”。
“淘宝的架构是这么做的,我们也要这么做”。
“Docker现在很流行我们的架构应该将Docker应用进来”。
以上这些想法,如果拿“架构设计是为了解决软件复杂度”这个原则来衡量,就很容易判断。
“我们的系统一定要做到每秒TPS 10万”
——如果系统的复杂度不是在性能这部分TPS做到10万并没有什么用。
“淘宝的架构是这么做的,我们也要这么做”
——淘宝的架构是为了解决淘宝业务的复杂度而设计的,淘宝的业务复杂度并不就是我们的业务复杂度,绝大多数业务的用户量都不可能有淘宝那么大。
“Docker现在很流行我们的架构应该将Docker应用进来”
——Docker不是万能的只是为了解决资源重用和动态分配而设计的如果我们的系统复杂度根本不是在这方面引入Docker没有什么意义。
简单的复杂度分析案例
我来分析一个简单的案例,一起来看看如何将“架构设计的真正目的是为了解决软件系统复杂度带来的问题”这个指导思想应用到实践中。
假设我们需要设计一个大学的学生管理系统,其基本功能包括登录、注册、成绩管理、课程管理等。当我们对这样一个系统进行架构设计的时候,首先应识别其复杂度到底体现在哪里。
性能一个学校的学生大约1 ~ 2万人学生管理系统的访问频率并不高平均每天单个学生的访问次数平均不到1次因此性能这部分并不复杂存储用MySQL完全能够胜任缓存都可以不用Web服务器用Nginx绰绰有余。
可扩展性:学生管理系统的功能比较稳定,可扩展的空间并不大,因此可扩展性也不复杂。
高可用学生管理系统即使宕机2小时对学生管理工作影响并不大因此可以不做负载均衡更不用考虑异地多活这类复杂的方案了。但是如果学生的数据全部丢失修复是非常麻烦的只能靠人工逐条修复这个很难接受因此需要考虑存储高可靠这里就有点复杂了。我们需要考虑多种异常情况机器故障、机房故障针对机器故障我们需要设计MySQL同机房主备方案针对机房故障我们需要设计MySQL跨机房同步方案。
安全性学生管理系统存储的信息有一定的隐私性例如学生的家庭情况但并不是和金融相关的也不包含强隐私例如玉照、情感的信息因此安全性方面只要做3个事情就基本满足要求了Nginx提供ACL控制、用户账号密码管理、数据库访问权限控制。
成本:由于系统很简单,基本上几台服务器就能够搞定,对于一所大学来说完全不是问题,可以无需太多关注。
还有其他方面,如果有兴趣,你可以自行尝试去分析。通过我上面的分析,可以看到这个方案的主要复杂性体现在存储可靠性上,需要保证异常的时候,不要丢失所有数据即可(丢失几个或者几十个学生的信息问题不大),对应的架构如下:
学生管理系统虽然简单,但麻雀虽小五脏俱全,基本上能涵盖软件系统复杂度分析的各个方面,而且绝大部分技术人员都曾经自己设计或者接触过类似的系统,如果将这个案例和自己的经验对比,相信会有更多的收获。
小结
今天我为你分析了架构设计的误区,结合周二讲的架构设计的历史背景,给出架构设计的主要目的是为了解决软件系统复杂度带来的问题,并分析了一个简单复杂度的案例,希望对你有所帮助。
这就是今天的全部内容,留一道思考题给你吧。请按照“架构设计的主要目的是为了解决软件复杂度带来的问题”这个指导思想来分析一下你目前的业务系统架构,看看是否和你当时分析的结果一样?
欢迎你把答案写到留言区,和我一起讨论。相信经过深度思考的回答,也会让你对知识的理解更加深刻。(编辑乱入:精彩的留言有机会获得丰厚福利哦!)

View File

@@ -0,0 +1,132 @@
因收到Google相关通知网站将会择期关闭。相关通知内容
04 复杂度来源:高性能
周四我为你讲了架构设计的主要目的是为了解决软件系统复杂度带来的问题。那么从今天开始我将为你深入分析复杂度的6个来源先来聊聊复杂度的来源之一高性能。
对性能孜孜不倦的追求是整个人类技术不断发展的根本驱动力。例如计算机从电子管计算机到晶体管计算机再到集成电路计算机运算性能从每秒几次提升到每秒几亿次。但伴随性能越来越高相应的方法和系统复杂度也是越来越高。现代的计算机CPU集成了几亿颗晶体管逻辑复杂度和制造复杂度相比最初的晶体管计算机根本不可同日而语。
软件系统也存在同样的现象。最近几十年软件系统性能飞速发展从最初的计算机只能进行简单的科学计算到现在Google能够支撑每秒几万次的搜索。与此同时软件系统规模也从单台计算机扩展到上万台计算机从最初的单用户单工的字符界面Dos操作系统到现在的多用户多工的Windows 10图形操作系统。
当然技术发展带来了性能上的提升不一定带来复杂度的提升。例如硬件存储从纸带→磁带→磁盘→SSD并没有显著带来系统复杂度的增加。因为新技术会逐步淘汰旧技术这种情况下我们直接用新技术即可不用担心系统复杂度会随之提升。只有那些并不是用来取代旧技术而是开辟了一个全新领域的技术才会给软件系统带来复杂度因为软件系统在设计的时候就需要在这些技术之间进行判断选择或者组合。就像汽车的发明无法取代火车飞机的出现也并不能完全取代火车所以我们在出行的时候需要考虑选择汽车、火车还是飞机这个选择的过程就比较复杂了要考虑价格、时间、速度、舒适度等各种因素。
软件系统中高性能带来的复杂度主要体现在两方面,一方面是单台计算机内部为了高性能带来的复杂度;另一方面是多台计算机集群为了高性能带来的复杂度。
单机复杂度
计算机内部复杂度最关键的地方就是操作系统。计算机性能的发展本质上是由硬件发展驱动的尤其是CPU的性能发展。著名的“摩尔定律”表明了CPU的处理能力每隔18个月就翻一番而将硬件性能充分发挥出来的关键就是操作系统所以操作系统本身其实也是跟随硬件的发展而发展的操作系统是软件系统的运行环境操作系统的复杂度直接决定了软件系统的复杂度。
操作系统和性能最相关的就是进程和线程。最早的计算机其实是没有操作系统的,只有输入、计算和输出功能,用户输入一个指令,计算机完成操作,大部分时候计算机都在等待用户输入指令,这样的处理性能很显然是很低效的,因为人的输入速度是远远比不上计算机的运算速度的。
为了解决手工操作带来的低效,批处理操作系统应运而生。批处理简单来说就是先把要执行的指令预先写下来(写到纸带、磁带、磁盘等),形成一个指令清单,这个指令清单就是我们常说的“任务”,然后将任务交给计算机去执行,批处理操作系统负责读取“任务”中的指令清单并进行处理,计算机执行的过程中无须等待人工手工操作,这样性能就有了很大的提升。
批处理程序大大提升了处理性能但有一个很明显的缺点计算机一次只能执行一个任务如果某个任务需要从I/O设备例如磁带读取大量的数据在I/O操作的过程中CPU其实是空闲的而这个空闲时间本来是可以进行其他计算的。
为了进一步提升性能人们发明了“进程”用进程来对应一个任务每个任务都有自己独立的内存空间进程间互不相关由操作系统来进行调度。此时的CPU还没有多核和多线程的概念为了达到多进程并行运行的目的采取了分时的方式即把CPU的时间分成很多片段每个片段只能执行某个进程中的指令。虽然从操作系统和CPU的角度来说还是串行处理的但是由于CPU的处理速度很快从用户的角度来看感觉是多进程在并行处理。
多进程虽然要求每个任务都有独立的内存空间进程间互不相关但从用户的角度来看两个任务之间能够在运行过程中就进行通信会让任务设计变得更加灵活高效。否则如果两个任务运行过程中不能通信只能是A任务将结果写到存储B任务再从存储读取进行处理不仅效率低而且任务设计更加复杂。为了解决这个问题进程间通信的各种方式被设计出来了包括管道、消息队列、信号量、共享存储等。
多进程让多任务能够并行处理任务,但本身还有缺点,单个进程内部只能串行处理,而实际上很多进程内部的子任务并不要求是严格按照时间顺序来执行的,也需要并行处理。例如,一个餐馆管理进程,排位、点菜、买单、服务员调度等子任务必须能够并行处理,否则就会出现某个客人买单时间比较长(比如说信用卡刷不出来),其他客人都不能点菜的情况。为了解决这个问题,人们又发明了线程,线程是进程内部的子任务,但这些子任务都共享同一份进程数据。为了保证数据的正确性,又发明了互斥锁机制。有了多线程后,操作系统调度的最小单位就变成了线程,而进程变成了操作系统分配资源的最小单位。
多进程多线程虽然让多任务并行处理的性能大大提升但本质上还是分时系统并不能做到时间上真正的并行。解决这个问题的方式显而易见就是让多个CPU能够同时执行计算任务从而实现真正意义上的多任务并行。目前这样的解决方案有3种SMPSymmetric Multi-Processor对称多处理器结构、NUMANon-Uniform Memory Access非一致存储访问结构、MPPMassive Parallel Processing海量并行处理结构。其中SMP是我们最常见的目前流行的多核处理器就是SMP方案。
操作系统发展到现在如果我们要完成一个高性能的软件系统需要考虑如多进程、多线程、进程间通信、多线程并发等技术点而且这些技术并不是最新的就是最好的也不是非此即彼的选择。在做架构设计的时候需要花费很大的精力来结合业务进行分析、判断、选择、组合这个过程同样很复杂。举一个最简单的例子Nginx可以用多进程也可以用多线程JBoss采用的是多线程Redis采用的是单进程Memcache采用的是多线程这些系统都实现了高性能但内部实现差异却很大。
集群的复杂度
虽然计算机硬件的性能快速发展,但和业务的发展速度相比,还是小巫见大巫了,尤其是进入互联网时代后,业务的发展速度远远超过了硬件的发展速度。例如:
2016年“双11”支付宝每秒峰值达12万笔支付。
2017年春节微信红包收发红包每秒达到76万个。
要支持支付和红包这种复杂的业务,单机的性能无论如何是无法支撑的,必须采用机器集群的方式来达到高性能。例如,支付宝和微信这种规模的业务系统,后台系统的机器数量都是万台级别的。
通过大量机器来提升性能,并不仅仅是增加机器这么简单,让多台机器配合起来达到高性能的目的,是一个复杂的任务,我针对常见的几种方式简单分析一下。
1.任务分配
任务分配的意思是指每台机器都可以处理完整的业务任务,不同的任务分配到不同的机器上执行。
我从最简单的一台服务器变两台服务器开始,来讲任务分配带来的复杂性,整体架构示意图如下。
从图中可以看到1台服务器演变为2台服务器后架构上明显要复杂多了主要体现在
需要增加一个任务分配器这个分配器可能是硬件网络设备例如F5、交换机等可能是软件网络设备例如LVS也可能是负载均衡软件例如Nginx、HAProxy还可能是自己开发的系统。选择合适的任务分配器也是一件复杂的事情需要综合考虑性能、成本、可维护性、可用性等各方面的因素。
任务分配器和真正的业务服务器之间有连接和交互(即图中任务分配器到业务服务器的连接线),需要选择合适的连接方式,并且对连接进行管理。例如,连接建立、连接检测、连接中断后如何处理等。
任务分配器需要增加分配算法。例如,是采用轮询算法,还是按权重分配,又或者按照负载进行分配。如果按照服务器的负载进行分配,则业务服务器还要能够上报自己的状态给任务分配器。
这一大段描述,即使你可能还看不懂,但也应该感受到其中的复杂度了,更何况还要真正去实践和实现。
上面这个架构只是最简单地增加1台业务机器我们假设单台业务服务器每秒能够处理5000次业务请求那么这个架构理论上能够支撑10000次请求实际上的性能一般按照8折计算大约是8000次左右。
如果我们的性能要求继续提高假设要求每秒提升到10万次上面这个架构会出现什么问题呢是不是将业务服务器增加到25台就可以了呢显然不是因为随着性能的增加任务分配器本身又会成为性能瓶颈当业务请求达到每秒10万次的时候单台任务分配器也不够用了任务分配器本身也需要扩展为多台机器这时的架构又会演变成这个样子。
这个架构比2台业务服务器的架构要复杂主要体现在
任务分配器从1台变成了多台对应图中的任务分配器1到任务分配器M这个变化带来的复杂度就是需要将不同的用户分配到不同的任务分配器上即图中的虚线“用户分配”部分常见的方法包括DNS轮询、智能DNS、CDNContent Delivery Network内容分发网络、GSLB设备Global Server Load Balance全局负载均衡等。
任务分配器和业务服务器的连接从简单的“1对多”1台任务分配器连接多台业务服务器变成了“多对多”多台任务分配器连接多台业务服务器的网状结构。
机器数量从3台扩展到30台一般任务分配器数量比业务服务器要少这里我们假设业务服务器为25台任务分配器为5台状态管理、故障处理复杂度也大大增加。
上面这两个例子都是以业务处理为例实际上“任务”涵盖的范围很广可以指完整的业务处理也可以单指某个具体的任务。例如“存储”“运算”“缓存”等都可以作为一项任务因此存储系统、运算系统、缓存系统都可以按照任务分配的方式来搭建架构。此外“任务分配器”也并不一定只能是物理上存在的机器或者一个独立运行的程序也可以是嵌入在其他程序中的算法例如Memcache的集群架构。
2.任务分解
通过任务分配的方式我们能够突破单台机器处理性能的瓶颈通过增加更多的机器来满足业务的性能需求但如果业务本身也越来越复杂单纯只通过任务分配的方式来扩展性能收益会越来越低。例如业务简单的时候1台机器扩展到10台机器性能能够提升8倍需要扣除机器群带来的部分性能损耗因此无法达到理论上的10倍那么高但如果业务越来越复杂1台机器扩展到10台性能可能只能提升5倍。造成这种现象的主要原因是业务越来越复杂单台机器处理的性能会越来越低。为了能够继续提升性能我们需要采取第二种方式任务分解。
继续以上面“任务分配”中的架构为例,“业务服务器”如果越来越复杂,我们可以将其拆分为更多的组成部分,我以微信的后台架构为例。
通过上面的架构示意图可以看出微信后台架构从逻辑上将各个子业务进行了拆分包括接入、注册登录、消息、LBS、摇一摇、漂流瓶、其他业务聊天、视频、朋友圈等
通过这种任务分解的方式,能够把原来大一统但复杂的业务系统,拆分成小而简单但需要多个系统配合的业务系统。从业务的角度来看,任务分解既不会减少功能,也不会减少代码量(事实上代码量可能还会增加,因为从代码内部调用改为通过服务器之间的接口调用),那为何通过任务分解就能够提升性能呢?
主要有几方面的因素:
简单的系统更加容易做到高性能
系统的功能越简单影响性能的点就越少就更加容易进行有针对性的优化。而系统很复杂的情况下首先是比较难以找到关键性能点因为需要考虑和验证的点太多其次是即使花费很大力气找到了修改起来也不容易因为可能将A关键性能点提升了但却无意中将B点的性能降低了整个系统的性能不但没有提升还有可能会下降。
可以针对单个任务进行扩展
当各个逻辑任务分解到独立的子系统后整个系统的性能瓶颈更加容易发现而且发现后只需要针对有瓶颈的子系统进行性能优化或者提升不需要改动整个系统风险会小很多。以微信的后台架构为例如果用户数增长太快注册登录子系统性能出现瓶颈的时候只需要优化登录注册子系统的性能可以是代码优化也可以简单粗暴地加机器消息逻辑、LBS逻辑等其他子系统完全不需要改动。
既然将一个大一统的系统分解为多个子系统能够提升性能那是不是划分得越细越好呢例如上面的微信后台目前是7个逻辑子系统如果我们把这7个逻辑子系统再细分划分为100个逻辑子系统性能是不是会更高呢
其实不然,这样做性能不仅不会提升,反而还会下降,最主要的原因是如果系统拆分得太细,为了完成某个业务,系统间的调用次数会呈指数级别上升,而系统间的调用通道目前都是通过网络传输的方式,性能远比系统内的函数调用要低得多。我以一个简单的图示来说明。
从图中可以看到当系统拆分2个子系统的时候用户访问需要1次系统间的请求和1次响应当系统拆分为4个子系统的时候系统间的请求次数从1次增长到3次假如继续拆分下去为100个子系统为了完成某次用户访问系统间的请求次数变成了99次。
为了描述简单我抽象出来一个最简单的模型假设这些系统采用IP网络连接理想情况下一次请求和响应在网络上耗费为1ms业务处理本身耗时为50ms。我们也假设系统拆分对单个业务请求性能没有影响那么系统拆分为2个子系统的时候处理一次用户访问耗时为51ms而系统拆分为100个子系统的时候处理一次用户访问耗时竟然达到了149ms。
虽然系统拆分可能在某种程度上能提升业务处理性能但提升性能也是有限的不可能系统不拆分的时候业务处理耗时为50ms系统拆分后业务处理耗时只要1ms因为最终决定业务处理性能的还是业务逻辑本身业务逻辑本身没有发生大的变化下理论上的性能是有一个上限的系统拆分能够让性能逼近这个极限但无法突破这个极限。因此任务分解带来的性能收益是有一个度的并不是任务分解越细越好而对于架构设计来说如何把握这个粒度就非常关键了。
小结
今天我给你讲了软件系统中高性能带来的复杂度主要体现的两方面,一是单台计算机内部为了高性能带来的复杂度;二是多台计算机集群为了高性能带来的复杂度,希望对你有所帮助。
这就是今天的全部内容,留一道思考题给你吧。你所在的业务体系中,高性能的系统采用的是哪种方式?目前是否有改进和提升的空间?

View File

@@ -0,0 +1,132 @@
因收到Google相关通知网站将会择期关闭。相关通知内容
05 复杂度来源:高可用
今天,我们聊聊复杂度的第二个来源高可用。
参考维基百科,先来看看高可用的定义。
系统无中断地执行其功能的能力,代表系统的可用性程度,是进行系统设计时的准则之一。
这个定义的关键在于“无中断”但恰好难点也在“无中断”上面因为无论是单个硬件还是单个软件都不可能做到无中断硬件会出故障软件会有bug硬件会逐渐老化软件会越来越复杂和庞大……
除了硬件和软件本质上无法做到“无中断”,外部环境导致的不可用更加不可避免、不受控制。例如,断电、水灾、地震,这些事故或者灾难也会导致系统不可用,而且影响程度更加严重,更加难以预测和规避。
所以,系统的高可用方案五花八门,但万变不离其宗,本质上都是通过“冗余”来实现高可用。通俗点来讲,就是一台机器不够就两台,两台不够就四台;一个机房可能断电,那就部署两个机房;一条通道可能故障,那就用两条,两条不够那就用三条(移动、电信、联通一起上)。高可用的“冗余”解决方案,单纯从形式上来看,和之前讲的高性能是一样的,都是通过增加更多机器来达到目的,但其实本质上是有根本区别的:高性能增加机器目的在于“扩展”处理性能;高可用增加机器目的在于“冗余”处理单元。
通过冗余增强了可用性,但同时也带来了复杂性,我会根据不同的应用场景逐一分析。
计算高可用
这里的“计算”指的是业务的逻辑处理。计算有一个特点就是无论在哪台机器上进行计算,同样的算法和输入数据,产出的结果都是一样的,所以将计算从一台机器迁移到另外一台机器,对业务并没有什么影响。既然如此,计算高可用的复杂度体现在哪里呢?我以最简单的单机变双机为例进行分析。先来看一个单机变双机的简单架构示意图。
你可能会发现,这个双机的架构图和上期“高性能”讲到的双机架构图是一样的,因此复杂度也是类似的,具体表现为:
需要增加一个任务分配器,选择合适的任务分配器也是一件复杂的事情,需要综合考虑性能、成本、可维护性、可用性等各方面因素。
任务分配器和真正的业务服务器之间有连接和交互,需要选择合适的连接方式,并且对连接进行管理。例如,连接建立、连接检测、连接中断后如何处理等。
任务分配器需要增加分配算法。例如,常见的双机算法有主备、主主,主备方案又可以细分为冷备、温备、热备。
上面这个示意图只是简单的双机架构,我们再看一个复杂一点的高可用集群架构。
这个高可用集群相比双机来说分配算法更加复杂可以是1主3备、2主2备、3主1备、4主0备具体应该采用哪种方式需要结合实际业务需求来分析和判断并不存在某种算法就一定优于另外的算法。例如ZooKeeper采用的就是1主多备而Memcached采用的就是全主0备。
存储高可用
对于需要存储数据的系统来说整个系统的高可用设计关键点和难点就在于“存储高可用”。存储与计算相比有一个本质上的区别将数据从一台机器搬到到另一台机器需要经过线路进行传输。线路传输的速度是毫秒级别同一机房内部能够做到几毫秒分布在不同地方的机房传输耗时需要几十甚至上百毫秒。例如从广州机房到北京机房稳定情况下ping延时大约是50ms不稳定情况下可能达到1s甚至更多。
虽然毫秒对于人来说几乎没有什么感觉,但是对于高可用系统来说,就是本质上的不同,这意味着整个系统在某个时间点上,数据肯定是不一致的。按照“数据+ 逻辑= 业务”这个公式来套的话数据不一致即使逻辑一致最后的业务表现就不一样了。以最经典的银行储蓄业务为例假设用户的数据存在北京机房用户存入了1万块钱然后他查询的时候被路由到了上海机房北京机房的数据没有同步到上海机房用户会发现他的余额并没有增加1万块。想象一下此时用户肯定会背后一凉马上会怀疑自己的钱被盗了然后赶紧打客服电话投诉甚至打110报警即使最后发现只是因为传输延迟导致的问题站在用户的角度来说这个过程的体验肯定很不好。
除了物理上的传输速度限制传输线路本身也存在可用性问题传输线路可能中断、可能拥塞、可能异常错包、丢包并且传输线路的故障时间一般都特别长短的十几分钟长的几个小时都是可能的。例如2015年支付宝因为光缆被挖断业务影响超过4个小时2016年中美海底光缆中断3小时等。在传输线路中断的情况下就意味着存储无法进行同步在这段时间内整个系统的数据是不一致的。
综合分析,无论是正常情况下的传输延迟,还是异常情况下的传输中断,都会导致系统的数据在某个时间点或者时间段是不一致的,而数据的不一致又会导致业务问题;但如果完全不做冗余,系统的整体高可用又无法保证,所以存储高可用的难点不在于如何备份数据,而在于如何减少或者规避数据不一致对业务造成的影响。
分布式领域里面有一个著名的CAP定理从理论上论证了存储高可用的复杂度。也就是说存储高可用不可能同时满足“一致性、可用性、分区容错性”最多满足其中两个这就要求我们在做架构设计时结合业务进行取舍。
高可用状态决策
无论是计算高可用还是存储高可用,其基础都是“状态决策”,即系统需要能够判断当前的状态是正常还是异常,如果出现了异常就要采取行动来保证高可用。如果状态决策本身都是有错误或者有偏差的,那么后续的任何行动和处理无论多么完美也都没有意义和价值。但在具体实践的过程中,恰好存在一个本质的矛盾:通过冗余来实现的高可用系统,状态决策本质上就不可能做到完全正确。下面我基于几种常见的决策方式进行详细分析。
1.独裁式
独裁式决策指的是存在一个独立的决策主体,我们姑且称它为“决策者”,负责收集信息然后进行决策;所有冗余的个体,我们姑且称它为“上报者”,都将状态信息发送给决策者。
独裁式的决策方式不会出现决策混乱的问题,因为只有一个决策者,但问题也正是在于只有一个决策者。当决策者本身故障时,整个系统就无法实现准确的状态决策。如果决策者本身又做一套状态决策,那就陷入一个递归的死循环了。
2.协商式
协商式决策指的是两个独立的个体通过交流信息,然后根据规则进行决策,最常用的协商式决策就是主备决策。
这个架构的基本协商规则可以设计成:
2台服务器启动时都是备机。
2台服务器建立连接。
2台服务器交换状态信息。
某1台服务器做出决策成为主机另一台服务器继续保持备机身份。
协商式决策的架构不复杂,规则也不复杂,其难点在于,如果两者的信息交换出现问题(比如主备连接中断),此时状态决策应该怎么做。
如果备机在连接中断的情况下认为主机故障那么备机需要升级为主机但实际上此时主机并没有故障那么系统就出现了两个主机这与设计初衷1主1备是不符合的。
如果备机在连接中断的情况下不认为主机故障则此时如果主机真的发生故障那么系统就没有主机了这同样与设计初衷1主1备是不符合的。
如果为了规避连接中断对状态决策带来的影响,可以增加更多的连接。例如,双连接、三连接。这样虽然能够降低连接中断对状态带来的影响(注意:只能降低,不能彻底解决),但同时又引入了这几条连接之间信息取舍的问题,即如果不同连接传递的信息不同,应该以哪个连接为准?实际上这也是一个无解的答案,无论以哪个连接为准,在特定场景下都可能存在问题。
综合分析,协商式状态决策在某些场景总是存在一些问题的。
3.民主式
民主式决策指的是多个独立的个体通过投票的方式来进行状态决策。例如ZooKeeper集群在选举leader时就是采用这种方式。
民主式决策和协商式决策比较类似其基础都是独立的个体之间交换信息每个个体做出自己的决策然后按照“多数取胜”的规则来确定最终的状态。不同点在于民主式决策比协商式决策要复杂得多ZooKeeper的选举算法ZAB绝大部分人都看得云里雾里更不用说用代码来实现这套算法了。
除了算法复杂民主式决策还有一个固有的缺陷脑裂。这个词来源于医学指人体左右大脑半球的连接被切断后左右脑因为无法交换信息导致各自做出决策然后身体受到两个大脑分别控制会做出各种奇怪的动作。例如当一个脑裂患者更衣时他有时会一只手将裤子拉起另一只手却将裤子往下脱。脑裂的根本原因是原来统一的集群因为连接中断造成了两个独立分隔的子集群每个子集群单独进行选举于是选出了2个主机相当于人体有两个大脑了。
从图中可以看到正常状态的时候节点5作为主节点其他节点作为备节点当连接发生故障时节点1、节点2、节点3形成了一个子集群节点4、节点5形成了另外一个子集群这两个子集群的连接已经中断无法进行信息交换。按照民主决策的规则和算法两个子集群分别选出了节点2和节点5作为主节点此时整个系统就出现了两个主节点。这个状态违背了系统设计的初衷两个主节点会各自做出自己的决策整个系统的状态就混乱了。
为了解决脑裂问题民主式决策的系统一般都采用“投票节点数必须超过系统总节点数一半”规则来处理。如图中那种情况节点4和节点5形成的子集群总节点数只有2个没有达到总节点数5个的一半因此这个子集群不会进行选举。这种方式虽然解决了脑裂问题但同时降低了系统整体的可用性即如果系统不是因为脑裂问题导致投票节点数过少而真的是因为节点故障例如节点1、节点2、节点3真的发生了故障此时系统也不会选出主节点整个系统就相当于宕机了尽管此时还有节点4和节点5是正常的。
综合分析,无论采取什么样的方案,状态决策都不可能做到任何场景下都没有问题,但完全不做高可用方案又会产生更大的问题,如何选取适合系统的高可用方案,也是一个复杂的分析、判断和选择的过程。
小结
今天我给你讲了复杂度来源之一的高可用,分析了计算高可用和存储高可用两个场景,给出了几种高可用状态决策方式,希望对你有所帮助。
这就是今天的全部内容,留一道思考题给你吧。高性能和高可用是很多系统的核心复杂度,你认为哪个会更复杂一些?理由是什么?

View File

@@ -0,0 +1,148 @@
因收到Google相关通知网站将会择期关闭。相关通知内容
06 复杂度来源:可扩展性
你好,我是华仔。复杂度来源前面已经讲了高性能和高可用,今天我们来聊聊可扩展性。
可扩展性是指,系统为了应对将来需求变化而提供的一种扩展能力,当有新的需求出现时,系统不需要或者仅需要少量修改就可以支持,无须整个系统重构或者重建。
由于软件系统固有的多变性,新的需求总会不断提出来,因此可扩展性显得尤其重要。在软件开发领域,面向对象思想的提出,就是为了解决可扩展性带来的问题;后来的设计模式,更是将可扩展性做到了极致。得益于设计模式的巨大影响力,几乎所有的技术人员对于可扩展性都特别重视。
设计具备良好可扩展性的系统,有两个基本条件:
正确预测变化
完美应对变化
但要达成这两个条件,本身也是一件复杂的事情,我来具体分析一下。
预测变化
软件系统与硬件或者建筑相比,有一个很大的差异:软件系统在发布后,还可以不断地修改和演进。
这就意味着不断有新的需求需要实现。
如果新需求能够少改代码甚至不改代码就可以实现,那当然是皆大欢喜的,否则来一个需求就要求系统大改一次,成本会非常高,程序员心里也不爽(改来改去),产品经理也不爽(做得那么慢),老板也不爽(那么多人就只能干这么点事)。
因此作为架构师,我们总是试图去预测所有的变化,然后设计完美的方案来应对。当下一次需求真正来临时,架构师可以自豪地说:“这个我当时已经预测到了,架构已经完美地支持,只需要一两天工作量就可以了!”
然而理想是美好的,现实却是复杂的。有一句谚语:“唯一不变的是变化。”如果按照这个标准去衡量,架构师每个设计方案都要考虑可扩展性,例如:
架构师准备设计一个简单的后台管理系统当架构师考虑用MySQL存储数据时是否要考虑后续需要用Oracle来存储
当架构师设计用HTTP做接口协议时是否要考虑要不要支持ProtocolBuffer
甚至更离谱一点架构师是否要考虑VR技术对架构的影响从而提前做好可扩展性
如果每个点都考虑可扩展性,架构师会不堪重负,架构设计也会异常庞大且最终无法落地。但架构师也不能完全不做预测,否则可能系统刚上线,马上来新的需求就需要重构,这同样意味着前期很多投入的工作量也白费了。
同时,“预测”这个词,本身就暗示了不可能每次预测都是准确的。如果预测的事情出错,我们期望中的需求迟迟不来,甚至被明确否定,那么基于预测做的架构设计就没什么作用,投入的工作量也就白费了。
综合分析,预测变化的复杂性在于:
不能每个设计点都考虑可扩展性。
不能完全不考虑可扩展性。
所有的预测都存在出错的可能性。
对于架构师来说,如何把握预测的程度和提升预测结果的准确性,是一件很复杂的事情,而且没有通用的标准可以简单套上去,更多是靠自己的经验、直觉。所以架构设计评审的时候,经常会出现两个设计师对某个判断争得面红耳赤的情况,原因就在于没有明确标准,不同的人理解和判断有偏差,而最终又只能选择其中一个判断。
2年法则
那么我们设计架构的时候要怎么办呢根据以往的职业经历和思考我提炼出一个“2年法则”供你参考只预测2年内的可能变化不要试图预测5年甚至10年后的变化。
当然你可能会有疑问为什么一定是2年呢有的行业变化快有的行业变化慢不应该是按照行业特点来选择具体的预测周期吗
理论上来说确实如此,但实际操作的时候你会发现,如果你要给出一个让大家都信服的行业预测周期,其实是很难的。
我之所以说要预测2年是因为变化快的行业你能够预测2年已经足够了而变化慢的行业本身就变化慢预测本身的意义不大预测5年和预测2年的结果是差不多的。所以“2年法则”在大部分场景下都是适用的。
应对变化
假设架构师经验非常丰富,目光非常敏锐,看问题非常准,所有的变化都能准确预测,是否意味着可扩展性就很容易实现了呢?也没那么理想!因为预测变化是一回事,采取什么方案来应对变化,又是另外一个复杂的事情。即使预测很准确,如果方案不合适,则系统扩展一样很麻烦。
方案一:提炼出“变化层”和“稳定层”
第一种应对变化的常见方案是:将不变的部分封装在一个独立的“稳定层”,将“变化”封装在一个“变化层”(也叫“适配层”)。这种方案的核心思想是通过变化层来隔离变化。
无论是变化层依赖稳定层,还是稳定层依赖变化层都是可以的,需要根据具体业务情况来设计。
如果系统需要支持XML、JSON、ProtocolBuffer三种接入方式那么最终的架构就是“形式1”架构如果系统需要支持MySQL、Oracle、DB2数据库存储那么最终的架构就变成了“形式2”的架构了。
无论采取哪种形式,通过剥离变化层和稳定层的方式应对变化,都会带来两个主要的复杂性相关的问题。
变化层和稳定层如何拆分?
对于哪些属于变化层,哪些属于稳定层,很多时候并不是像前面的示例(不同接口协议或者不同数据库)那样明确,不同的人有不同的理解,导致架构设计评审的时候可能吵翻天。
变化层和稳定层之间的接口如何设计?
对于稳定层来说,接口肯定是越稳定越好;但对于变化层来说,在有差异的多个实现方式中找出共同点,并且还要保证当加入新的功能时,原有的接口不需要太大修改,这是一件很复杂的事情,所以接口设计同样至关重要。
例如MySQL的REPLACE INTO和Oracle的MERGE INTO语法和功能有一些差异那么存储层如何向稳定层提供数据访问接口呢是采取MySQL的方式还是采取Oracle的方式还是自适应判断如果再考虑DB2的情况呢
看到这里,相信你已经能够大致体会到接口设计的复杂性了。
方案二:提炼出“抽象层”和“实现层”
第二种常见的应对变化的方案是:提炼出一个“抽象层”和一个“实现层”。如果说方案一的核心思想是通过变化层来隔离变化,那么方案二的核心思想就是通过实现层来封装变化。
因为抽象层的接口是稳定的不变的,我们可以基于抽象层的接口来实现统一的处理规则,而实现层可以根据具体业务需求定制开发不同的实现细节,所以当加入新的功能时,只要遵循处理规则然后修改实现层,增加新的实现细节就可以了,无须修改抽象层。
方案二典型的实践就是设计模式和规则引擎。考虑到绝大部分技术人员对设计模式都非常熟悉,我以设计模式为例来说明这种方案的复杂性。
下面是设计模式的“装饰者”模式的类关系图。
图中的Component和Decorator就是抽象出来的规则这个规则包括几部分
Component和Decorator类。
Decorator类继承Component类。
Decorator类聚合了Component类。
这个规则一旦抽象出来后就固定了不能轻易修改。例如把规则3去掉就无法实现装饰者模式的目的了。
装饰者模式相比传统的继承来实现功能确实灵活很多。例如《设计模式》中装饰者模式的样例“TextView”类的实现用了装饰者之后能够灵活地给TextView增加额外更多功能包括可以增加边框、滚动条和背景图片等。这些功能上的组合不影响规则只需要按照规则实现即可。
但装饰者模式相对普通的类实现模式,明显要复杂多了。本来一个函数或者一个类就能搞定的事情,现在要拆分成多个类,而且多个类之间必须按照装饰者模式来设计和调用。
规则引擎和设计模式类似都是通过灵活的设计来达到可扩展的目的但“灵活的设计”本身就是一件复杂的事情不说别的光是把23种设计模式全部理解和备注都是一件很困难的事情。
1写2抄3重构原则
那么我们在实际工作中具体如何来应对变化呢Martin Fowler在他的经典书籍《重构》中给出一个“Rule of three”的原则原文是“Three Strikes And You Refactor”中文一般翻译为“事不过三三则重构”。
而我将其翻译为“1写2抄3重构”也就是说你不要一开始就考虑复杂的可扩展性应对方法而是等到第三次遇到类似的实现的时候再来重构重构的时候采取隔离或者封装的方案。
举个最简单的例子,假设你们的创新业务要对接第三方钱包,按照这个原则,就可以这样做:
1写最开始你们选择了微信钱包对接此时不需要考虑太多可扩展性直接快速对照微信支付的API对接即可因为业务是否能做起来还不确定。
2抄后来你们发现业务发展不错决定要接入支付宝此时还是可以不考虑可扩展直接把原来微信支付接入的代码拷贝过来然后对照支付宝的API快速修改上线。
3重构因为业务发展不错为了方便更多用户你们决定接入银联云闪付此时就需要考虑重构参考设计模式的模板方法和策略模式将支付对接的功能进行封装。
小结
今天我从预测变化和应对变化这两个设计可扩展性系统的条件,以及它们实现起来本身的复杂性,为你讲了复杂度来源之一的可扩展性,希望对你有所帮助。
这就是今天的全部内容,留一道思考题给你吧。你在具体代码中使用过哪些可扩展的技术?最终的效果如何?

View File

@@ -0,0 +1,137 @@
因收到Google相关通知网站将会择期关闭。相关通知内容
07 复杂度来源:低成本、安全、规模
关于复杂度来源,前面的专栏已经讲了高性能、高可用和可扩展性,今天我来聊聊复杂度另外三个来源低成本、安全和规模。
低成本
当我们的架构方案只涉及几台或者十几台服务器时一般情况下成本并不是我们重点关注的目标但如果架构方案涉及几百上千甚至上万台服务器成本就会变成一个非常重要的架构设计考虑点。例如A方案需要10000台机器B方案只需要8000台机器单从比例来看也就节省了20%的成本但从数量来看B方案能节省2000台机器1台机器成本预算每年大约2万元这样一年下来就能节省4000万元4000万元成本不是小数目给100人的团队发奖金每人可以发40万元了这可是算得上天价奖金了。通过一个架构方案的设计就能轻松节约几千万元不但展现了技术的强大力量也带来了可观的收益对于技术人员来说最有满足感的事情莫过于如此了。
当我们设计“高性能”“高可用”的架构时,通用的手段都是增加更多服务器来满足“高性能”和“高可用”的要求;而低成本正好与此相反,我们需要减少服务器的数量才能达成低成本的目标。因此,低成本本质上是与高性能和高可用冲突的,所以低成本很多时候不会是架构设计的首要目标,而是架构设计的附加约束。也就是说,我们首先设定一个成本目标,当我们根据高性能、高可用的要求设计出方案时,评估一下方案是否能满足成本目标,如果不行,就需要重新设计架构;如果无论如何都无法设计出满足成本要求的方案,那就只能找老板调整成本目标了。
低成本给架构设计带来的主要复杂度体现在,往往只有“创新”才能达到低成本目标。这里的“创新”既包括开创一个全新的技术领域(这个要求对绝大部分公司太高),也包括引入新技术,如果没有找到能够解决自己问题的新技术,那么就真的需要自己创造新技术了。
类似的新技术例子很多,我来举几个。
NoSQLMemcache、Redis等的出现是为了解决关系型数据库无法应对高并发访问带来的访问压力。
全文搜索引擎Sphinx、Elasticsearch、Solr的出现是为了解决关系型数据库like搜索的低效的问题。
Hadoop的出现是为了解决传统文件系统无法应对海量数据存储和计算的问题。
我再来举几个业界类似的例子。
Facebook为了解决PHP的低效问题刚开始的解决方案是HipHop PHP可以将PHP语言翻译为C++语言执行后来改为HHVM将PHP翻译为字节码然后由虚拟机执行和Java的JVM类似。
新浪微博将传统的Redis/MC + MySQL方式扩展为Redis/MC + SSD Cache + MySQL方式SSD Cache作为L2缓存使用既解决了MC/Redis成本过高容量小的问题也解决了穿透DB带来的数据库访问压力来源http://www.infoq.com/cn/articles/weibo-platform-archieture )。
Linkedin为了处理每天5千亿的事件开发了高效的Kafka消息系统。
其他类似将Ruby on Rails改为Java、Lua + redis改为Go语言实现的例子还有很多。
无论是引入新技术,还是自己创造新技术,都是一件复杂的事情。引入新技术的主要复杂度在于需要去熟悉新技术,并且将新技术与已有技术结合起来;创造新技术的主要复杂度在于需要自己去创造全新的理念和技术,并且新技术跟旧技术相比,需要有质的飞跃。
相比来说,创造新技术复杂度更高,因此一般中小公司基本都是靠引入新技术来达到低成本的目标;而大公司更有可能自己去创造新的技术来达到低成本的目标,因为大公司才有足够的资源、技术和时间去创造新技术。
安全
安全本身是一个庞大而又复杂的技术领域,并且一旦出问题,对业务和企业形象影响非常大。例如:
2016年雅虎爆出史上最大规模信息泄露事件逾5亿用户资料在2014年被窃取。
2016年10月美国遭史上最大规模DDoS攻击东海岸网站集体瘫痪。
2013年10月为全国4500多家酒店提供网络服务的浙江慧达驿站网络有限公司因安全漏洞问题致2千万条入住酒店的客户信息泄露由此导致很多敲诈、家庭破裂的后续事件。
正因为经常能够看到或者听到各类安全事件,所以大部分技术人员和架构师,对安全这部分会多一些了解和考虑。
从技术的角度来讲,安全可以分为两类:一类是功能上的安全,一类是架构上的安全。
1.功能安全
例如常见的XSS攻击、CSRF攻击、SQL注入、Windows漏洞、密码破解等本质上是因为系统实现有漏洞黑客有了可乘之机。黑客会利用各种漏洞潜入系统这种行为就像小偷一样黑客和小偷的手法都是利用系统或家中不完善的地方潜入并进行破坏或者盗取。因此形象地说功能安全其实就是“防小偷”。
从实现的角度来看功能安全更多地是和具体的编码相关与架构关系不大。现在很多开发框架都内嵌了常见的安全功能能够大大减少安全相关功能的重复开发但框架只能预防常见的安全漏洞和风险常见的XSS攻击、CSRF攻击、SQL注入等无法预知新的安全问题而且框架本身很多时候也存在漏洞例如流行的Apache Struts2就多次爆出了调用远程代码执行的高危漏洞给整个互联网都造成了一定的恐慌。所以功能安全是一个逐步完善的过程而且往往都是在问题出现后才能有针对性的提出解决方案我们永远无法预测系统下一个漏洞在哪里也不敢说自己的系统肯定没有任何问题。换句话讲功能安全其实也是一个“攻”与“防”的矛盾只能在这种攻防大战中逐步完善不可能在系统架构设计的时候一劳永逸地解决。
2.架构安全
如果说功能安全是“防小偷”,那么架构安全就是“防强盗”。强盗会直接用大锤将门砸开,或者用炸药将围墙炸倒;小偷是偷东西,而强盗很多时候就是故意搞破坏,对系统的影响也大得多。因此架构设计时需要特别关注架构安全,尤其是互联网时代,理论上来说系统部署在互联网上时,全球任何地方都可以发起攻击。
传统的架构安全主要依靠防火墙,防火墙最基本的功能就是隔离网络,通过将网络划分成不同的区域,制定出不同区域之间的访问控制策略来控制不同信任程度区域间传送的数据流。例如,下图是一个典型的银行系统的安全架构。
从图中你可以看到,整个系统根据不同的分区部署了多个防火墙来保证系统的安全。
防火墙的功能虽然强大但性能一般所以在传统的银行和企业应用领域应用较多。但在互联网领域防火墙的应用场景并不多。因为互联网的业务具有海量用户访问和高并发的特点防火墙的性能不足以支撑尤其是互联网领域的DDoS攻击轻则几GB重则几十GB。2016年知名安全研究人员布莱恩·克莱布斯Brian Krebs的安全博客网站遭遇DDoS攻击攻击带宽达665Gbps是目前在网络犯罪领域已知的最大的拒绝服务攻击。这种规模的攻击如果用防火墙来防则需要部署大量的防火墙成本会很高。例如中高端一些的防火墙价格10万元每秒能抗住大约25GB流量那么应对这种攻击就需要将近30台防火墙成本将近300万元这还不包括维护成本而这些防火墙设备在没有发生攻击的时候又没有什么作用。也就是说如果花费几百万元来买这么一套设备有可能几年都发挥不了任何作用。
就算是公司对钱不在乎一般也不会堆防火墙来防DDoS攻击因为DDoS攻击最大的影响是大量消耗机房的出口总带宽。不管防火墙处理能力有多强当出口带宽被耗尽时整个业务在用户看来就是不可用的因为用户的正常请求已经无法到达系统了。防火墙能够保证内部系统不受冲击但用户也是进不来的。对于用户来说业务都已经受到影响了至于是因为用户自己进不去还是因为系统出故障用户其实根本不会关心。
基于上述原因,互联网系统的架构安全目前并没有太好的设计手段来实现,更多地是依靠运营商或者云服务商强大的带宽和流量清洗的能力,较少自己来设计和实现。
规模
很多企业级的系统,既没有高性能要求,也没有双中心高可用要求,也不需要什么扩展性,但往往我们一说到这样的系统,很多人都会脱口而出:这个系统好复杂!为什么这样说呢?关键就在于这样的系统往往功能特别多,逻辑分支特别多。特别是有的系统,发展时间比较长,不断地往上面叠加功能,后来的人由于不熟悉整个发展历史,可能连很多功能的应用场景都不清楚,或者细节根本无法掌握,面对的就是一个黑盒系统,看不懂、改不动、不敢改、修不了,复杂度自然就感觉很高了。
规模带来复杂度的主要原因就是“量变引起质变”,当数量超过一定的阈值后,复杂度会发生质的变化。常见的规模带来的复杂度有:
1.功能越来越多,导致系统复杂度指数级上升
例如某个系统开始只有3大功能后来不断增加到8大功能虽然还是同一个系统但复杂度已经相差很大了具体相差多大呢
我以一个简单的抽象模型来计算一下,假设系统间的功能都是两两相关的,系统的复杂度=功能数量+功能之间的连接数量,通过计算我们可以看出:
3个功能的系统复杂度= 3 + 3 = 6
8个功能的系统复杂度= 8 + 28 = 36
可以看出具备8个功能的系统的复杂度不是比具备3个功能的系统的复杂度多5而是多了30基本是指数级增长的主要原因在于随着系统功能数量增多功能之间的连接呈指数级增长。下图形象地展示了功能数量的增多带来了复杂度。
通过肉眼就可以很直观地看出具备8个功能的系统复杂度要高得多。
2.数据越来越多,系统复杂度发生质变
与功能类似系统数据越来越多时也会由量变带来质变最近几年火热的“大数据”就是在这种背景下诞生的。大数据单独成为了一个热门的技术领域主要原因就是数据太多以后传统的数据收集、加工、存储、分析的手段和工具已经无法适应必须应用新的技术才能解决。目前的大数据理论基础是Google发表的三篇大数据相关论文其中Google File System是大数据文件存储的技术理论Google Bigtable是列式数据存储的技术理论Google MapReduce是大数据运算的技术理论这三篇技术论文各自开创了一个新的技术领域。
即使我们的数据没有达到大数据规模数据的增长也可能给系统带来复杂性。最典型的例子莫过于使用关系数据库存储数据我以MySQL为例MySQL单表的数据因不同的业务和应用场景会有不同的最优值但不管怎样都肯定是有一定的限度的一般推荐在5000万行左右。如果因为业务的发展单表数据达到了10亿行就会产生很多问题例如
添加索引会很慢,可能需要几个小时,这几个小时内数据库表是无法插入数据的,相当于业务停机了。
修改表结构和添加索引存在类似的问题,耗时可能会很长。
即使有索引,索引的性能也可能会很低,因为数据量太大。
数据库备份耗时很长。
……
因此当MySQL单表数据量太大时我们必须考虑将单表拆分为多表这个拆分过程也会引入更多复杂性例如
拆表的规则是什么?
以用户表为例是按照用户id拆分表还是按照用户注册时间拆表
拆完表后查询如何处理?
以用户表为例假设按照用户id拆表当业务需要查询学历为“本科”以上的用户时要去很多表查询才能得到最终结果怎么保证性能
还有很多类似的问题这里不一一展开,后面的专栏还会讨论。
小结
今天我为你分析了低成本给架构设计带来的主要复杂度体现在引入新技术或创造新技术,讨论了从功能安全和架构安全引入的复杂度,以及规模带来复杂度的主要原因是“量变引起质变”,希望对你有所帮助。
这就是今天的全部内容留一道思考题给你吧。学习了6大复杂度来源后结合你所在的业务分析一下主要的复杂度是这其中的哪些部分是否还有其他复杂度原因

View File

@@ -0,0 +1,208 @@
因收到Google相关通知网站将会择期关闭。相关通知内容
08 架构设计三原则
前面几期专栏我跟你系统的聊了架构设计的主要目的是为了解决软件系统复杂度带来的问题并分析了复杂度的来源。从今天开始我会分两期讲讲架构设计的3个原则以及架构设计原则的案例。
成为架构师是每个程序员的梦想,但并不意味着把编程做好就能够自然而然地成为一个架构师,优秀程序员和架构师之间还有一个明显的鸿沟需要跨越,这个鸿沟就是“不确定性”。
对于编程来说本质上是不能存在不确定的对于同样一段代码不管是谁写的不管什么时候执行执行的结果应该都是确定的注意“确定的”并不等于“正确的”有bug也是确定的。而对于架构设计来说本质上是不确定的同样的一个系统A公司和B公司做出来的架构可能差异很大但最后都能正常运转同样一个方案A设计师认为应该这样做B设计师认为应该那样做看起来好像都有道理……相比编程来说架构设计并没有像编程语言那样的语法来进行约束更多的时候是面对多种可能性时进行选择。
可是一旦涉及“选择”,就很容易让架构师陷入两难的境地,例如:
是要选择业界最先进的技术,还是选择团队目前最熟悉的技术?如果选了最先进的技术后出了问题怎么办?如果选了目前最熟悉的技术,后续技术演进怎么办?
是要选择Google的Angular的方案来做还是选择Facebook的React来做Angular看起来更强大但React看起来更灵活
是要选MySQL还是MongoDB团队对MySQL很熟悉但是MongoDB更加适合业务场景
淘宝的电商网站架构很完善,我们新做一个电商网站,是否简单地照搬淘宝就可以了?
还有很多类似的问题和困惑,关键原因在于架构设计领域并没有一套通用的规范来指导架构师进行架构设计,更多是依赖架构师的经验和直觉,因此架构设计有时候也会被看作一项比较神秘的工作。
业务千变万化技术层出不穷设计理念也是百花齐放看起来似乎很难有一套通用的规范来适用所有的架构设计场景。但是在研究了架构设计的发展历史、多个公司的架构发展过程QQ、淘宝、Facebook等、众多的互联网公司架构设计后我发现有几个共性的原则隐含其中这就是合适原则、简单原则、演化原则架构设计时遵循这几个原则有助于你做出最好的选择。
合适原则
合适原则宣言:“合适优于业界领先”。
优秀的技术人员都有很强的技术情结当他们做方案或者架构时总想不断地挑战自己想达到甚至优于业界领先水平是其中一个典型表现因为这样才能够展现自己的优秀才能在年终KPI绩效总结里面骄傲地写上“设计了XX方案达到了和Google相同的技术水平”“XX方案的性能测试结果大大优于阿里集团的YY方案”。
但现实是大部分这样想和这样做的架构最后可能都以失败告终我在互联网行业见过“亿级用户平台”的失败案例2011年的时候某个几个人规模的业务团队雄心勃勃的提出要做一个和腾讯QQ那时候微信还没起来一拼高下的“亿级用户平台”最后结果当然是不出所料的失败了。
为什么会这样呢?
再好的梦想,也需要脚踏实地实现!这里的“脚踏实地”主要体现在下面几个方面。
1.将军难打无兵之仗
大公司的分工比较细一个小系统可能就是一个小组负责比如说某个通信大厂做一个OM管理系统就有十几个人阿里的中间件团队有几十个人而大部分公司整个研发团队可能就100多人某个业务团队可能就十几个人。十几个人的团队想做几十个人的团队的事情而且还要做得更好不能说绝对不可能但难度是可想而知的。
没那么多人,却想干那么多活,是失败的第一个主要原因。
2.罗马不是一天建成的
业界领先的很多方案其实并不是一堆天才某个时期灵机一动然后加班加点就做出来的而是经过几年时间的发展才逐步完善和初具规模的。阿里中间件团队2008年成立发展到现在已经有十年了。我们只知道他们抗住了多少次“双11”做了多少优秀的系统但经历了什么样的挑战、踩了什么样的坑只有他们自己知道这些挑战和踩坑都是架构设计非常关键的促进因素单纯靠拍脑袋或者头脑风暴是不可能和真正实战相比的。
没有那么多积累,却想一步登天,是失败的第二个主要原因。
3.冰山下面才是关键
可能有人认为业界领先的方案都是天才创造出来的所以自己也要造一个业界领先的方案以此来证明自己也是天才。确实有这样的天才但更多的时候业界领先的方案其实都是“逼”出来的简单来说“业务”发展到一定阶段量变导致了质变出现了新的问题已有的方式已经不能应对这些问题需要用一种新的方案来解决通过创新和尝试才有了业界领先的方案。GFS为何在Google诞生而不是在Microsoft诞生我认为Google有那么庞大的数据是一个主要的因素而不是因为Google的工程师比Microsoft的工程师更加聪明。
没有那么卓越的业务场景,却幻想灵光一闪成为天才,是失败的第三个主要原因。
回到我前面提到的“亿级用户平台”失败的例子分析一下原因。没有腾讯那么多的人当然钱差得更多没有QQ那样海量用户的积累没有QQ那样的业务这个项目失败其实是在一开始就注定的。注意这里的失败不是说系统做不出来而是系统没有按照最初的目标来实现上面提到的3个失败原因也全占了。
所以真正优秀的架构都是在企业当前人力、条件、业务等各种约束下设计出来的能够合理地将资源整合在一起并发挥出最大功效并且能够快速落地。这也是很多BAT出来的架构师到了小公司或者创业团队反而做不出成绩的原因因为没有了大公司的平台、资源、积累只是生搬硬套大公司的做法失败的概率非常高。
简单原则
简单原则宣言:“简单优于复杂”。
软件架构设计是一门技术活。所谓技术活,从历史上看,无论是瑞士的钟表,还是瓦特的蒸汽机;无论是莱特兄弟发明的飞机,还是摩托罗拉发明的手机,无一不是越来越精细、越来越复杂。因此当我们进行架构设计时,会自然而然地想把架构做精美、做复杂,这样才能体现我们的技术实力,也才能够将架构做成一件艺术品。
由于软件架构和建筑架构表面上的相似性,我们也会潜意识地将对建筑的审美观点移植到软件架构上面。我们惊叹于长城的宏伟、泰姬陵的精美、悉尼歌剧院的艺术感、迪拜帆船酒店的豪华感,因此,对于我们自己亲手打造的软件架构,我们也希望它宏伟、精美、艺术、豪华……总之就是不能寒酸、不能简单。
团队的压力有时也会有意无意地促进我们走向复杂的方向因为大部分人在评价一个方案水平高低的时候复杂性是其中一个重要的参考指标。例如设计一个主备方案如果你用心跳来实现可能大家都认为这太简单了。但如果你引入ZooKeeper来做主备决策可能很多人会认为这个方案更加“高大上”一些毕竟ZooKeeper使用的是ZAB协议而ZAB协议本身就很复杂。其实真正理解ZAB协议的人很少我也不懂但并不妨碍我们都知道ZAB协议很优秀。
刚才我聊的这些原因,会在潜意识层面促使初出茅庐的架构师,不自觉地追求架构的复杂性。然而,“复杂”在制造领域代表先进,在建筑领域代表领先,但在软件领域,却恰恰相反,代表的是“问题”。
软件领域的复杂性体现在两个方面:
1.结构的复杂性
结构复杂的系统几乎毫无例外具备两个特点:
组成复杂系统的组件数量更多;
同时这些组件之间的关系也更加复杂。
我以图形的方式来说明复杂性:
2个组件组成的系统
3个组件组成的系统
4个组件组成的系统
5个组件组成的系统
结构上的复杂性存在的第一个问题是组件越多就越有可能其中某个组件出现故障从而导致系统故障。这个概率可以算出来假设组件的故障率是10%有10%的时间不可用那么有3个组件的系统可用性是1-10%×1-10%×1-10%= 72.9%有5个组件的系统可用性是1-10%×1-10%×1-10%×1-10%×1-10%=59%两者的可用性相差13%。
结构上的复杂性存在的第二个问题是某个组件改动会影响关联的所有组件这些被影响的组件同样会继续递归影响更多的组件。还以上面图中5个组件组成的系统为例组件A修改或者异常时会影响组件B/C/ED又会影响E。这个问题会影响整个系统的开发效率因为一旦变更涉及外部系统需要协调各方统一进行方案评估、资源协调、上线配合。
结构上的复杂性存在的第三个问题是,定位一个复杂系统中的问题总是比简单系统更加困难。首先是组件多,每个组件都有嫌疑,因此要逐一排查;其次组件间的关系复杂,有可能表现故障的组件并不是真正问题的根源。
2.逻辑的复杂性
意识到结构的复杂性后,我们的第一反应可能就是“降低组件数量”,毕竟组件数量越少,系统结构越简。最简单的结构当然就是整个系统只有一个组件,即系统本身,所有的功能和逻辑都在这一个组件中实现。
不幸的是,这样做是行不通的,原因在于除了结构的复杂性,还有逻辑的复杂性,即如果某个组件的逻辑太复杂,一样会带来各种问题。
逻辑复杂的组件,一个典型特征就是单个组件承担了太多的功能。以电商业务为例,常见的功能有:商品管理、商品搜索、商品展示、订单管理、用户管理、支付、发货、客服……把这些功能全部在一个组件中实现,就是典型的逻辑复杂性。
逻辑复杂几乎会导致软件工程的每个环节都有问题,假设现在淘宝将这些功能全部在单一的组件中实现,可以想象一下这个恐怖的场景:
系统会很庞大可能是上百万、上千万的代码规模“clone”一次代码要30分钟。
几十、上百人维护这一套代码,某个“菜鸟”不小心改了一行代码,导致整站崩溃。
需求像雪片般飞来,为了应对,开几十个代码分支,然后各种分支合并、各种分支覆盖。
产品、研发、测试、项目管理不停地开会讨论版本计划,协调资源,解决冲突。
版本太多每天都要上线几十个版本系统每隔1个小时重启一次。
线上运行出现故障,几十个人扑上去定位和处理,一间小黑屋都装不下所有人,整个办公区闹翻天。
……
不用多说,肯定谁都无法忍受这样的场景。
但是,为什么复杂的电路就意味更强大的功能,而复杂的架构却有很多问题呢?根本原因在于电路一旦设计好后进入生产,就不会再变,复杂性只是在设计时带来影响;而一个软件系统在投入使用后,后续还有源源不断的需求要实现,因此要不断地修改系统,复杂性在整个系统生命周期中都有很大影响。
功能复杂的组件,另外一个典型特征就是采用了复杂的算法。复杂算法导致的问题主要是难以理解,进而导致难以实现、难以修改,并且出了问题难以快速解决。
以ZooKeeper为例ZooKeeper本身的功能主要就是选举为了实现分布式下的选举采用了ZAB协议所以ZooKeeper功能虽然相对简单但系统实现却比较复杂。相比之下etcd就要简单一些因为etcd采用的是Raft算法相比ZAB协议Raft算法更加容易理解更加容易实现。
综合前面的分析我们可以看到无论是结构的复杂性还是逻辑的复杂性都会存在各种问题所以架构设计时如果简单的方案和复杂的方案都可以满足需求最好选择简单的方案。《UNIX编程艺术》总结的KISSKeep It Simple, Stupid!)原则一样适应于架构设计。
演化原则
演化原则宣言:“演化优于一步到位”。
软件架构从字面意思理解和建筑结构非常类似,事实上“架构”这个词就是建筑领域的专业名词,维基百科对“软件架构”的定义中有一段话描述了这种相似性:
从和目的、主题、材料和结构的联系上来说,软件架构可以和建筑物的架构相比拟。
例如,软件架构描述的是一个软件系统的结构,包括各个模块,以及这些模块的关系;建筑架构描述的是一幢建筑的结构,包括各个部件,以及这些部件如何有机地组成成一幢完美的建筑。
然而,字面意思上的相似性却掩盖了一个本质上的差异:建筑一旦完成(甚至一旦开建)就不可再变,而软件却需要根据业务的发展不断地变化!
古埃及的吉萨大金字塔4000多年前完成的到现在还是当初的架构。
中国的明长城600多年前完成的现在保存下来的长城还是当年的结构。
美国白宫1800年建成200年来进行了几次扩展但整体结构并无变化只是在旁边的空地扩建或者改造内部的布局。
对比一下,我们来看看软件架构。
Windows系统的发展历史
如果对比Windows 8的架构和Windows 1.0的架构,就会发现它们其实是两个不同的系统了!
Android的发展历史
http://www.dappworld.com/wp-content/uploads/2015/09/Android-History-Dappworld.jpg
同样Android 6.0和Android 1.6的差异也很大。
对于建筑来说永恒是主题而对于软件来说变化才是主题。软件架构需要根据业务的发展而不断变化。设计Windows和Android的人都是顶尖的天才即便如此他们也不可能在1985年设计出Windows 8不可能在2009年设计出Android 6.0。
如果没有把握“软件架构需要根据业务发展不断变化”这个本质,在做架构设计的时候就很容易陷入一个误区:试图一步到位设计一个软件架构,期望不管业务如何变化,架构都稳如磐石。
为了实现这样的目标,要么照搬业界大公司公开发表的方案;要么投入庞大的资源和时间来做各种各样的预测、分析、设计。无论哪种做法,后果都很明显:投入巨大,落地遥遥无期。更让人沮丧的是,就算跌跌撞撞拼死拼活终于落地,却发现很多预测和分析都是不靠谱的。
考虑到软件架构需要根据业务发展不断变化这个本质特点,软件架构设计其实更加类似于大自然“设计”一个生物,通过演化让生物适应环境,逐步变得更加强大:
首先,生物要适应当时的环境。
其次,生物需要不断地繁殖,将有利的基因传递下去,将不利的基因剔除或者修复。
第三,当环境变化时,生物要能够快速改变以适应环境变化;如果生物无法调整就被自然淘汰;新的生物会保留一部分原来被淘汰生物的基因。
软件架构设计同样是类似的过程:
首先,设计出来的架构要满足当时的业务需要。
其次,架构要不断地在实际应用过程中迭代,保留优秀的设计,修复有缺陷的设计,改正错误的设计,去掉无用的设计,使得架构逐渐完善。
第三,当业务发生变化时,架构要扩展、重构,甚至重写;代码也许会重写,但有价值的经验、教训、逻辑、设计等(类似生物体内的基因)却可以在新架构中延续。
架构师在进行架构设计时需要牢记这个原则,时刻提醒自己不要贪大求全,或者盲目照搬大公司的做法。应该认真分析当前业务的特点,明确业务面临的主要问题,设计合理的架构,快速落地以满足业务需要,然后在运行过程中不断完善架构,不断随着业务演化架构。
即使是大公司的团队,在设计一个新系统的架构时,也需要遵循演化的原则,而不应该认为团队人员多、资源多,不管什么系统上来就要一步到位,因为业务的发展和变化是很快的,不管多牛的团队,也不可能完美预测所有的业务发展和变化路径。
小结
今天我为你讲了面对“不确定性”时架构设计的三原则,分别是合适优于业界领先、简单优于复杂、演化优于一步到位,希望对你有所帮助。
这就是今天的全部内容,留一道思考题给你吧。我讲的这三条架构设计原则是否每次都要全部遵循?是否有优先级?谈谈你的理解,并说说为什么。
欢迎你把答案写到留言区,和我一起讨论。相信经过深度思考的回答,也会让你对知识的理解更加深刻。(编辑乱入:精彩的留言有机会获得丰厚福利哦!)

View File

@@ -0,0 +1,210 @@
因收到Google相关通知网站将会择期关闭。相关通知内容
09 架构设计原则案例
周二我给你介绍了架构设计的三条核心原则先复习一下合适原则、简单原则和演化原则。我们在架构设计实践中应该时刻谨记这三条设计原则指导我们设计出合适的架构即使是代表中国互联网技术最顶尖水平的BAT其架构的发展历程也同样遵循这三条原则。
今天我就以大家耳熟能详的淘宝和手机QQ作为案例来简单分析一下。
淘宝
注:以下部分内容摘自《淘宝技术发展》。
淘宝技术发展主要经历了“个人网站”→“Oracle/支付宝/旺旺”→“Java时代1.0”→“Java时代2.0”→“Java时代3.0”→“分布式时代”。我们看看每个阶段的主要驱动力是什么。
1.个人网站
2003年4月7日马云提出成立淘宝2003年5月10日淘宝就上线了中间只有1个月怎么办淘宝的答案就是买一个。
估计大部分人很难想象如今技术牛气冲天的阿里最初的淘宝竟然是买来的,我们看看当初决策的依据:
当时对整个项目组来说压力最大的就是时间,怎么在最短的时间内把一个从来就没有的网站从零开始建立起来?了解淘宝历史的人知道淘宝是在 2003 年 5 月 10 日上线的,这之间只有一个月。要是你在这个团队里,你怎么做?我们的答案就是:买一个来。
淘宝当时在初创时,没有过多考虑技术是否优越、性能是否海量以及稳定性如何,主要的考虑因素就是:快!
因为此时业务要求快速上线,时间不等人,等你花几个月甚至十几个月搞出一个强大的系统出来,可能市场机会就没有了,黄花菜都凉了。
同样,在考虑如何买的时候,淘宝的决策依据主要也是“快”。
买一个网站显然比做一个网站要省事一些,但是他们的梦想可不是做一个小网站而已,要做大,就不是随便买个就行的,要有比较低的维护成本,要能够方便地扩展和二次开发。
那接下来就是第二个问题:买一个什么样的网站?答案是:轻量一点的,简单一点的。
买一个系统是为了“快速可用”,而买一个轻量级的系统是为了“快速开发”。因为系统上线后肯定有大量的需求需要做,这时能够快速开发就非常重要。
从这个实例我们可以看到:淘宝最开始的时候业务要求就是“快”,因此反过来要求技术同样要“快”,业务决定技术,这里架构设计和选择主要遵循的是“合适原则”和“简单原则”。
第一代的技术架构如图所示。
2.Oracle/支付宝/旺旺
淘宝网推出后由于正好碰到“非典”网购很火爆加上采取了成功的市场运作流量和交易量迅速上涨业务发展很快在2003年底MySQL已经撑不住了。
一般人或者团队在这个时候,可能就开始优化系统、优化架构、分拆业务了,因为这些是大家耳熟能详也很拿手的动作。那我们来看看淘宝这个时候怎么采取的措施:
技术的替代方案非常简单就是换成Oracle。换Oracle的原因除了它容量大、稳定、安全、性能高还有人才方面的原因。
可以看出这个时候淘宝的策略主要还是“买”买更高配置的Oracle这个是当时情况下最快的方法。
除了购买Oracle后来为了优化又买了更强大的存储
后来数据量变大了本地存储不行了。买了NASNetwork Attached Storage网络附属存储NetApp的NAS存储作为了数据库的存储设备加上Oracle RACReal Application Clusters实时应用集群来实现负载均衡。
为什么淘宝在这个时候继续采取“买”的方式来快速解决问题呢?我们可以从时间上看出端倪:此时离刚上线才半年不到,业务飞速发展,最快的方式支撑业务的发展还是去买。如果说第一阶段买的是“方案”,这个阶段买的就是“性能”,这里架构设计和选择主要遵循的还是“合适原则”和“简单原则”。
换上Oracle和昂贵的存储后第二代架构如图所示。
3.脱胎换骨的Java时代1.0
淘宝切换到Java的原因很有趣主要因为找了一个PHP的开源连接池SQL Relay连接到Oracle而这个代理经常死锁死锁了就必须重启而数据库又必须用Oracle于是决定换个开发语言。最后淘宝挑选了Java而且当时挑选Java也是请Sun公司的人这帮人很厉害先是将淘宝网站从PHP热切换到了Java后来又做了支付宝。
这次切换的最主要原因是因为技术影响了业务的发展,频繁的死锁和重启对用户业务产生了严重的影响,从业务的角度来看这是不得不解决的技术问题。
但这次淘宝为什么没有去“买”呢我们看最初选择SQL Relay的原因
但对于PHP语言来说它是放在Apache上的每一个请求都会对数据库产生一个连接它没有连接池这种功能Java语言有Servlet容器可以存放连接池。那如何是好呢这帮人打探到eBay在PHP下面用了一个连接池的工具是BEA卖给他们的。我们知道BEA的东西都很贵我们买不起于是多隆在网上寻寻觅觅找到一个开源的连接池代理服务SQL Relay。
不清楚当时到底有多贵Oracle都可以买连接池买不起 所以我个人感觉这次切换语言更多是为以后业务发展做铺垫毕竟当时PHP语言远远没有Java那么火、那么好招人。淘宝选择Java语言的理由可以从侧面验证这点
Java是当时最成熟的网站开发语言它有比较良好的企业开发框架被世界上主流的大规模网站普遍采用另外有Java开发经验的人才也比较多后续维护成本会比较低。
综合来看,这次架构的变化没有再简单通过“买”来解决,而是通过重构来解决,架构设计和选择遵循了“演化原则”。
从PHP改为Java后第三代技术架构如图所示。
4.坚若磐石的Java时代2.0
Java时代2.0淘宝做了很多优化工作数据分库、放弃EJB、引入Spring、加入缓存、加入CDN、采用开源的JBoss。为什么在这个时候要做这些动作原文作者很好地概括了做这些动作的原因
这些杂七杂八的修改我们对数据分库、放弃EJB、引入Spring、加入缓存、加入CDN、采用开源的JBoss看起来没有章法可循其实都是围绕着提高容量、提高性能、节约成本来做的。
我们思考一下,为什么在前面的阶段,淘宝考虑的都是“快”,而现在开始考虑“容量、性能、成本”了呢?而且为什么这个时候不采取“买”的方式来解决容量、性能、成本问题呢?
简单来说,就是“买”也搞不定了,此时的业务发展情况是这样的:
随着数据量的继续增长到了2005年商品数有1663万PV有8931万注册会员有1390万这给数据和存储带来的压力依然很大数据量大性能就慢。
原有的方案存在固有缺陷随着业务的发展已经不是靠“买”就能够解决问题了此时必须从整个架构上去进行调整和优化。比如说Oracle再强大在做like类搜索的时候也不可能做到纯粹的搜索系统如Solr、Sphinx等的性能因为这是机制决定的。
另外随着规模的增大纯粹靠买的一个典型问题开始成为重要的考虑因素那就是成本。当买一台两台Oracle的时候可能对成本并不怎么关心但如果要买100台Oracle成本就是一个关键因素了。这就是“量变带来质变”的一个典型案例业务和系统发生质变后架构设计遵循“演化原则”的思想需要再一次重构甚至重写。
Java架构经过各种优化第四代技术架构如图所示。
5.Java 时代3.0和分布式时代
Java时代3.0我个人认为是淘宝技术飞跃的开始简单来说就是淘宝技术从商用转为“自研”典型的就是去IOE化。
分布式时代我认为是淘宝技术的修炼成功,到了这个阶段,自研技术已经自成一派,除了支撑本身的海量业务,也开始影响整个互联网的技术发展。
到了这个阶段业务规模急剧上升后原来并不是主要复杂度的IOE成本开始成为了主要的问题因此通过自研系统来降低IOE的成本去IOE也是系统架构的再一次演化。
手机QQ
以下部分内容摘自《QQ 1.4亿在线背后的故事》。
手机QQ的发展历程按照用户规模可以粗略划分为4个阶段十万级、百万级、千万级、亿级不同的用户规模IM后台的架构也不同而且基本上都是用户规模先上去然后产生各种问题倒逼技术架构升级。
1.十万级IM 1.X
最开始的手机QQ后台是这样的可以说是简单得不能再简单、普通得不能再普通的一个架构了因为当时业务刚开始架构设计遵循的是“合适原则”和“简单原则”。
2.百万级IM 2.X
随着业务发展到2001年QQ同时在线人数也突破了一百万。第一代架构很简单明显不可能支撑百万级的用户规模主要的问题有
以接入服务器的内存为例单个在线用户的存储量约为2KB索引和在线状态为50字节好友表400个好友 × 5字节/好友 = 2000字节大致来说2GB内存只能支持一百万在线用户。
CPU/网卡包量和流量/交换机流量等瓶颈。
单台服务器支撑不下所有在线用户/注册用户。
于是针对这些问题做架构改造按照“演化原则”的指导进行了重构重构的方案相比现在来说也还是简单得多因此当时做架构设计时也遵循了“合适原则”和“简单原则”。IM 2.X的最终架构如图所示。
3.千万级IM 3.X
业务发展到2005年QQ同时在线人数突破了一千万。第二代架构支撑百万级用户是没问题的但支撑千万级用户又会产生新问题表现有
同步流量太大,状态同步服务器遇到单机瓶颈。
所有在线用户的在线状态信息量太大,单台接入服务器存不下,如果在线数进一步增加,甚至单台状态同步服务器也存不下。
单台状态同步服务器支撑不下所有在线用户。
单台接入服务器支撑不下所有在线用户的在线状态信息。
针对这些问题架构需要继续改造升级再一次“演化”。IM 3.X的最终架构如下图可以看到这次的方案相比之前的方案来说并不简单了这是业务特性决定的。
4.亿级IM 4.X
业务发展到2010年3月QQ同时在线人数过亿。第三代架构此时也不适应了主要问题有
灵活性很差比如“昵称”长度增加一半需要两个月增加“故乡”字段需要两个月最大好友数从500变成1000需要三个月。
无法支撑某些关键功能比如好友数上万、隐私权限控制、PC QQ与手机QQ不可互踢、微信与QQ互通、异地容灾。
除了不适应,还有一个更严重的问题:
IM后台从1.0到3.5都是在原来基础上做改造升级的但是持续打补丁已经难以支撑亿级在线IM后台4.0必须从头开始,重新设计实现!
这里再次遵循了“演化原则”,决定重新打造一个这么复杂的系统,不得不佩服当时决策人的勇气和魄力!
重新设计的IM 4.0架构如图所示,和之前的架构相比,架构本身都拆分为两个主要的架构:存储架构和通信架构。
存储架构
通信架构
小结
今天我给你讲了淘宝和手机QQ两个典型互联网业务的架构发展历程通过这两个案例我们可以看出即使是现在非常复杂、非常强大的架构也并不是一开始就进行了复杂设计而是首先采取了简单的方式简单原则满足了当时的业务需要合适原则随着业务的发展逐步演化而来的演化原则。罗马不是一天建成的架构也不是一开始就设计成完美的样子然后可以一劳永逸一直用下去。
这就是今天的全部内容留一道思考题给你吧。搜索一个互联网大厂BATJ、TMD等的架构发展案例分析一下其发展过程看看哪些地方体现了这三条架构设计原则。

View File

@@ -0,0 +1,102 @@
因收到Google相关通知网站将会择期关闭。相关通知内容
10 架构设计流程:识别复杂度
从今天开始我将分4期结合复杂度来源和架构设计原则通过一个模拟的设计场景“前浪微博”和你一起看看在实践中究竟如何进行架构设计。今天先来看架构设计流程第1步识别复杂度。
架构设计第1步识别复杂度
我在前面讲过,架构设计的本质目的是为了解决软件系统的复杂性,所以在我们设计架构时,首先就要分析系统的复杂性。只有正确分析出了系统的复杂性,后续的架构设计方案才不会偏离方向;否则,如果对系统的复杂性判断错误,即使后续的架构设计方案再完美再先进,都是南辕北辙,做的越好,错的越多、越离谱。
例如如果一个系统的复杂度本来是业务逻辑太复杂功能耦合严重架构师却设计了一个TPS达到50000/秒的高性能架构,即使这个架构最终的性能再优秀也没有任何意义,因为架构没有解决正确的复杂性问题。
架构的复杂度主要来源于“高性能”“高可用”“可扩展”等几个方面,但架构师在具体判断复杂性的时候,不能生搬硬套,认为任何时候架构都必须同时满足这三方面的要求。实际上大部分场景下,复杂度只是其中的某一个,少数情况下包含其中两个,如果真的出现同时需要解决三个或者三个以上的复杂度,要么说明这个系统之前设计的有问题,要么可能就是架构师的判断出现了失误,即使真的认为要同时满足这三方面的要求,也必须要进行优先级排序。
例如专栏前面提到过的“亿级用户平台”失败的案例设计对标腾讯的QQ按照腾讯QQ的用户量级和功能复杂度进行设计高性能、高可用、可扩展、安全等技术一应俱全一开始就设计出了40多个子系统然后投入大量人力开发了将近1年时间才跌跌撞撞地正式上线。上线后发现之前的过度设计完全是多此一举而且带来很多问题
系统复杂无比,运维效率低下,每次业务版本升级都需要十几个子系统同步升级,操作步骤复杂,容易出错,出错后回滚还可能带来二次问题。
每次版本开发和升级都需要十几个子系统配合,开发效率低下。
子系统数量太多,关系复杂,小问题不断,而且出问题后定位困难。
开始设计的号称TPS 50000/秒的系统实际TPS连500都不到。
由于业务没有发展最初的设计人员陆续离开后来接手的团队无奈又花了2年时间将系统重构合并很多子系统将原来40多个子系统合并成不到20个子系统整个系统才逐步稳定下来。
如果运气真的不好,接手了一个每个复杂度都存在问题的系统,那应该怎么办呢?答案是一个个来解决问题,不要幻想一次架构重构解决所有问题。例如这个“亿级用户平台”的案例,后来接手的团队其实面临几个主要的问题:系统稳定性不高,经常出各种莫名的小问题;系统子系统数量太多,系统关系复杂,开发效率低;不支持异地多活,机房级别的故障会导致业务整体不可用。如果同时要解决这些问题,就可能会面临这些困境:
要做的事情太多,反而感觉无从下手。
设计方案本身太复杂,落地时间遥遥无期。
同一个方案要解决不同的复杂性,有的设计点是互相矛盾的。例如,要提升系统可用性,就需要将数据及时存储到硬盘上,而硬盘刷盘反过来又会影响系统性能。
因此,正确的做法是将主要的复杂度问题列出来,然后根据业务、技术、团队等综合情况进行排序,优先解决当前面临的最主要的复杂度问题。“亿级用户平台”这个案例,团队就优先选择将子系统的数量降下来,后来发现子系统数量降下来后,不但开发效率提升了,原来经常发生的小问题也基本消失了,于是团队再在这个基础上做了异地多活方案,也取得了非常好的效果。
对于按照复杂度优先级解决的方式,存在一个普遍的担忧:如果按照优先级来解决复杂度,可能会出现解决了优先级排在前面的复杂度后,解决后续复杂度的方案需要将已经落地的方案推倒重来。这个担忧理论上是可能的,但现实中几乎是不可能出现的,原因在于软件系统的可塑性和易变性。对于同一个复杂度问题,软件系统的方案可以有多个,总是可以挑出综合来看性价比最高的方案。
即使架构师决定要推倒重来这个新的方案也必须能够同时解决已经被解决的复杂度问题一般来说能够达到这种理想状态的方案基本都是依靠新技术的引入。例如Hadoop能够将高可用、高性能、大容量三个大数据处理的复杂度问题同时解决。
识别复杂度对架构师来说是一项挑战,因为原始的需求中并没有哪个地方会明确地说明复杂度在哪里,需要架构师在理解需求的基础上进行分析。有经验的架构师可能一看需求就知道复杂度大概在哪里;如果经验不足,那只能采取“排查法”,从不同的角度逐一进行分析。
识别复杂度实战
我们假想一个创业公司,名称叫作“前浪微博”。前浪微博的业务发展很快,系统也越来越多,系统间协作的效率很低,例如:
用户发一条微博后,微博子系统需要通知审核子系统进行审核,然后通知统计子系统进行统计,再通知广告子系统进行广告预测,接着通知消息子系统进行消息推送……一条微博有十几个通知,目前都是系统间通过接口调用的。每通知一个新系统,微博子系统就要设计接口、进行测试,效率很低,问题定位很麻烦,经常和其他子系统的技术人员产生分岐,微博子系统的开发人员不胜其烦。
用户等级达到VIP后等级子系统要通知福利子系统进行奖品发放要通知客服子系统安排专属服务人员要通知商品子系统进行商品打折处理……等级子系统的开发人员也是不胜其烦。
新来的架构师在梳理这些问题时,结合自己的经验,敏锐地发现了这些问题背后的根源在于架构上各业务子系统强耦合,而消息队列系统正好可以完成子系统的解耦,于是提议要引入消息队列系统。经过一分析二讨论三开会四汇报五审批等一系列操作后,消息队列系统终于立项了。其他背景信息还有:
中间件团队规模不大大约6人左右。
中间件团队熟悉Java语言但有一个新同事C/C++很牛。
开发平台是Linux数据库是MySQL。
目前整个业务系统是单机房部署,没有双机房。
针对前浪微博的消息队列系统,采用“排查法”来分析复杂度,具体分析过程是:
这个消息队列是否需要高性能
我们假设前浪微博系统用户每天发送1000万条微博那么微博子系统一天会产生1000万条消息我们再假设平均一条消息有10个子系统读取那么其他子系统读取的消息大约是1亿次。
1000万和1亿看起来很吓人但对于架构师来说关注的不是一天的数据而是1秒的数据即TPS和QPS。我们将数据按照秒来计算一天内平均每秒写入消息数为115条每秒读取的消息数是1150条再考虑系统的读写并不是完全平均的设计的目标应该以峰值来计算。峰值一般取平均值的3倍那么消息队列系统的TPS是345QPS是3450这个量级的数据意味着并不要求高性能。
虽然根据当前业务规模计算的性能要求并不高但业务会增长因此系统设计需要考虑一定的性能余量。由于现在的基数较低为了预留一定的系统容量应对后续业务的发展我们将设计目标设定为峰值的4倍因此最终的性能要求是TPS为1380QPS为13800。TPS为1380并不高但QPS为13800已经比较高了因此高性能读取是复杂度之一。注意这里的设计目标设定为峰值的4倍是根据业务发展速度来预估的不是固定为4倍不同的业务可以是2倍也可以是8倍但一般不要设定在10倍以上更不要一上来就按照100倍预估。
这个消息队列是否需要高可用性
对于微博子系统来说如果消息丢了导致没有审核然后触犯了国家法律法规则是非常严重的事情对于等级子系统来说如果用户达到相应等级后系统没有给他奖品和专属服务则VIP用户会很不满意导致用户流失从而损失收入虽然也比较关键但没有审核子系统丢消息那么严重。
综合来看,消息队列需要高可用性,包括消息写入、消息存储、消息读取都需要保证高可用性。
这个消息队列是否需要高可扩展性
消息队列的功能很明确,基本无须扩展,因此可扩展性不是这个消息队列的复杂度关键。
为了方便理解这里我只排查“高性能”“高可用”“扩展性”这3个复杂度在实际应用中不同的公司或者团队可能还有一些其他方面的复杂度分析。例如金融系统可能需要考虑安全性有的公司会考虑成本等。
综合分析下来,消息队列的复杂性主要体现在这几个方面:高性能消息读取、高可用消息写入、高可用消息存储、高可用消息读取。
“前浪微博”的消息队列设计才刚完成第1步专栏下一期会根据今天识别的复杂度设计备选方案前面提到的场景在下一期还会用到哦。
小结
今天我为你讲了架构设计流程的第一个步骤“识别复杂度”,并且通过一个模拟的场景讲述了“排查法”的具体分析方式,希望对你有所帮助。
这就是今天的全部内容,留一道思考题给你吧。尝试用排查法分析一下你参与过或者研究过的系统的复杂度,然后与你以前的理解对比一下,看看是否有什么新发现?

View File

@@ -0,0 +1,117 @@
因收到Google相关通知网站将会择期关闭。相关通知内容
11 架构设计流程:设计备选方案
上一期我讲了架构设计流程第1步识别复杂度确定了系统面临的主要复杂度问题后方案设计就有了明确的目标我们就可以开始真正进行架构方案设计了。今天我来讲讲架构设计流程第2步设计备选方案同样还会结合上期“前浪微博”的场景谈谈消息队列设计备选方案的实战。
架构设计第2步设计备选方案
架构师的工作并不神秘,成熟的架构师需要对已经存在的技术非常熟悉,对已经经过验证的架构模式烂熟于心,然后根据自己对业务的理解,挑选合适的架构模式进行组合,再对组合后的方案进行修改和调整。
虽然软件技术经过几十年的发展,新技术层出不穷,但是经过时间考验,已经被各种场景验证过的成熟技术其实更多。例如,高可用的主备方案、集群方案,高性能的负载均衡、多路复用,可扩展的分层、插件化等技术,绝大部分时候我们有了明确的目标后,按图索骥就能够找到可选的解决方案。
只有当这种方式完全无法满足需求的时候,才会考虑进行方案的创新,而事实上方案的创新绝大部分情况下也都是基于已有的成熟技术。
NoSQLKey-Value的存储和数据库的索引其实是类似的Memcache只是把数据库的索引独立出来做成了一个缓存系统。
Hadoop大文件存储方案基础其实是集群方案+ 数据复制方案。
Docker虚拟化基础是LXCLinux Containers
LevelDB的文件存储结构是Skip List。
在《技术的本质》一书中,对技术的组合有清晰的阐述:
新技术都是在现有技术的基础上发展起来的,现有技术又来源于先前的技术。将技术进行功能性分组,可以大大简化设计过程,这是技术“模块化”的首要原因。技术的“组合”和“递归”特征,将彻底改变我们对技术本质的认识。
虽说基于已有的技术或者架构模式进行组合,然后调整,大部分情况下就能够得到我们需要的方案,但并不意味着架构设计是一件很简单的事情。因为可选的模式有很多,组合的方案更多,往往一个问题的解决方案有很多个;如果再在组合的方案上进行一些创新,解决方案会更多。因此,如何设计最终的方案,并不是一件容易的事情,这个阶段也是很多架构师容易犯错的地方。
第一种常见的错误:设计最优秀的方案。
很多架构师在设计架构方案时心里会默认有一种技术情结我要设计一个优秀的架构才能体现我的技术能力例如高可用的方案中集群方案明显比主备方案要优秀和强大高性能的方案中淘宝的XX方案是业界领先的方案……
根据架构设计原则中“合适原则”和“简单原则“的要求挑选合适自己业务、团队、技术能力的方案才是好方案否则要么浪费大量资源开发了无用的系统例如之前提过的“亿级用户平台”的案例设计了TPS 50000的系统实际TPS只有500要么根本无法实现例如10个人的团队要开发现在的整个淘宝系统
第二种常见的错误:只做一个方案。
很多架构师在做方案设计时,可能心里会简单地对几个方案进行初步的设想,再简单地判断哪个最好,然后就基于这个判断开始进行详细的架构设计了。
这样做有很多弊端:
心里评估过于简单,可能没有想得全面,只是因为某一个缺点就把某个方案给否决了,而实际上没有哪个方案是完美的,某个地方有缺点的方案可能是综合来看最好的方案。
架构师再怎么牛,经验知识和技能也有局限,有可能某个评估的标准或者经验是不正确的,或者是老的经验不适合新的情况,甚至有的评估标准是架构师自己原来就理解错了。
单一方案设计会出现过度辩护的情况,即架构评审时,针对方案存在的问题和疑问,架构师会竭尽全力去为自己的设计进行辩护,经验不足的设计人员可能会强词夺理。
因此,架构师需要设计多个备选方案,但方案的数量可以说是无穷无尽的,架构师也不可能穷举所有方案,那合理的做法应该是什么样的呢?
备选方案的数量以3 ~ 5个为最佳。少于3个方案可能是因为思维狭隘考虑不周全多于5个则需要耗费大量的精力和时间并且方案之间的差别可能不明显。
备选方案的差异要比较明显。例如主备方案和集群方案差异就很明显或者同样是主备方案用ZooKeeper做主备决策和用Keepalived做主备决策的差异也很明显。但是都用ZooKeeper做主备决策一个检测周期是1分钟一个检测周期是5分钟这就不是架构上的差异而是细节上的差异了不适合做成两个方案。
备选方案的技术不要只局限于已经熟悉的技术。设计架构时架构师需要将视野放宽考虑更多可能性。很多架构师或者设计师积累了一些成功的经验出于快速完成任务和降低风险的目的可能自觉或者不自觉地倾向于使用自己已经熟悉的技术对于新的技术有一种不放心的感觉。就像那句俗语说的“如果你手里有一把锤子所有的问题在你看来都是钉子”。例如架构师对MySQL很熟悉因此不管什么存储都基于MySQL去设计方案系统性能不够了首先考虑的就是MySQL分库分表而事实上也许引入一个Memcache缓存就能够解决问题。
第三种常见的错误:备选方案过于详细。
有的架构师或者设计师在写备选方案时,错误地将备选方案等同于最终的方案,每个备选方案都写得很细。这样做的弊端显而易见:
耗费了大量的时间和精力。
将注意力集中到细节中,忽略了整体的技术设计,导致备选方案数量不够或者差异不大。
评审的时候其他人会被很多细节给绕进去评审效果很差。例如评审的时候针对某个定时器应该是1分钟还是30秒争论得不可开交。
正确的做法是备选阶段关注的是技术选型而不是技术细节技术选型的差异要比较明显。例如采用ZooKeeper和Keepalived两种不同的技术来实现主备差异就很大而同样都采用ZooKeeper一个方案的节点设计是/service/node/master另一个方案的节点设计是/company/service/master这两个方案并无明显差异无须在备选方案设计阶段作为两个不同的备选方案至于节点路径究竟如何设计只要在最终的方案中挑选一个进行细化即可。
设计备选方案实战
还是回到“前浪微博”的场景上期我们通过“排查法”识别了消息队列的复杂性主要体现在高性能消息读取、高可用消息写入、高可用消息存储、高可用消息读取。接下来进行第2步设计备选方案。
1.备选方案1采用开源的Kafka
Kafka是成熟的开源消息队列方案功能强大性能非常高而且已经比较成熟很多大公司都在使用。
2.备选方案2集群 + MySQL存储
首先考虑单服务器高性能。高性能消息读取属于“计算高可用”的范畴单服务器高性能备选方案有很多种。考虑到团队的开发语言是Java虽然有人觉得C/C++语言更加适合写高性能的中间件系统但架构师综合来看认为无须为了语言的性能优势而让整个团队切换语言消息队列系统继续用Java开发。由于Netty是Java领域成熟的高性能网络库因此架构师选择基于Netty开发消息队列系统。
由于系统设计的QPS是13800即使单机采用Netty来构建高性能系统单台服务器支撑这么高的QPS还是有很大风险的因此架构师选择采取集群方式来满足高性能消息读取集群的负载均衡算法采用简单的轮询即可。
同理,“高可用写入”和“高性能读取”一样,可以采取集群的方式来满足。因为消息只要写入集群中一台服务器就算成功写入,因此“高可用写入”的集群分配算法和“高性能读取”也一样采用轮询,即正常情况下,客户端将消息依次写入不同的服务器;某台服务器异常的情况下,客户端直接将消息写入下一台正常的服务器即可。
整个系统中最复杂的是“高可用存储”和“高可用读取”“高可用存储”要求已经写入的消息在单台服务器宕机的情况下不丢失“高可用读取”要求已经写入的消息在单台服务器宕机的情况下可以继续读取。架构师第一时间想到的就是可以利用MySQL的主备复制功能来达到“高可用存储“的目的通过服务器的主备方案来达到“高可用读取”的目的。
具体方案:
简单描述一下方案:
采用数据分散集群的架构,集群中的服务器进行分组,每个分组存储一部分消息数据。
每个分组包含一台主MySQL和一台备MySQL分组内主备数据复制分组间数据不同步。
正常情况下,分组内的主服务器对外提供消息写入和消息读取服务,备服务器不对外提供服务;主服务器宕机的情况下,备服务器对外提供消息读取的服务。
客户端采取轮询的策略写入和读取消息。
3.备选方案3集群 + 自研存储方案
在备选方案2的基础上将MySQL存储替换为自研实现存储方案因为MySQL的关系型数据库的特点并不是很契合消息队列的数据特点参考Kafka的做法可以自己实现一套文件存储和复制方案此处省略具体的方案描述实际设计时需要给出方案
可以看出高性能消息读取单机系统设计这部分时并没有多个备选方案可选备选方案2和备选方案3都采取基于Netty的网络库用Java语言开发原因就在于团队的Java背景约束了备选的范围。通常情况下成熟的团队不会轻易改变技术栈反而是新成立的技术团队更加倾向于采用新技术。
上面简单地给出了3个备选方案用来示范如何操作实践中要比上述方案复杂一些。架构师的技术储备越丰富、经验越多备选方案也会更多从而才能更好地设计备选方案。例如开源方案选择可能就包括Kafka、ActiveMQ、RabbitMQ集群方案的存储既可以考虑用MySQL也可以考虑用HBase还可以考虑用Redis与MySQL结合等自研文件系统也可以有多个可以参考Kafka也可以参考LevelDB还可以参考HBase等。限于篇幅这里就不一一展开了。
小结
今天我为你讲了架构设计流程的第二个步骤:设计备选方案,基于我们模拟的“前浪微博”消息系统,给出了备选方案的设计样例,希望对你有所帮助。
这就是今天的全部内容,留一道思考题给你吧,除了这三个备选方案,如果让你来设计第四个备选方案,你的方案是什么?

View File

@@ -0,0 +1,153 @@
因收到Google相关通知网站将会择期关闭。相关通知内容
12 架构设计流程:评估和选择备选方案
上一期我讲了设计备选方案,在完成备选方案设计后,如何挑选出最终的方案也是一个很大的挑战,主要原因有:
每个方案都是可行的,如果方案不可行就根本不应该作为备选方案。
没有哪个方案是完美的。例如A方案有性能的缺点B方案有成本的缺点C方案有新技术不成熟的风险。
评价标准主观性比较强比如设计师说A方案比B方案复杂但另外一个设计师可能会认为差不多因为比较难将“复杂”一词进行量化。因此方案评审的时候我们经常会遇到几个设计师针对某个方案或者某个技术点争论得面红耳赤。
正因为选择备选方案存在这些困难,所以实践中很多设计师或者架构师就采取了下面几种指导思想:
最简派
设计师挑选一个看起来最简单的方案。例如我们要做全文搜索功能方案1基于MySQL方案2基于Elasticsearch。MySQL的查询功能比较简单而Elasticsearch的倒排索引设计要复杂得多写入数据到Elasticsearch要设计Elasticsearch的索引要设计Elasticsearch的分布式……全套下来复杂度很高所以干脆就挑选MySQL来做吧。
最牛派
最牛派的做法和最简派正好相反设计师会倾向于挑选技术上看起来最牛的方案。例如性能最高的、可用性最好的、功能最强大的或者淘宝用的、微信开源的、Google出品的等。
我们以缓存方案中的Memcache和Redis为例假如我们要挑选一个搭配MySQL使用的缓存Memcache是纯内存缓存支持基于一致性hash的集群而Redis同时支持持久化、支持数据字典、支持主备、支持集群看起来比Memcache好很多啊所以就选Redis好了。
最熟派
设计师基于自己的过往经验挑选自己最熟悉的方案。我以编程语言为例假如设计师曾经是一个C++经验丰富的开发人员现在要设计一个运维管理系统由于对Python或者Ruby on Rails不熟悉因此继续选择C++来做运维管理系统。
领导派
领导派就更加聪明了,列出备选方案,设计师自己拿捏不定,然后就让领导来定夺,反正最后方案选的对那是领导厉害,方案选的不对怎么办?那也是领导“背锅”。
其实这些不同的做法本身并不存在绝对的正确或者绝对的错误关键是不同的场景应该采取不同的方式。也就是说有时候我们要挑选最简单的方案有时候要挑选最优秀的方案有时候要挑选最熟悉的方案甚至有时候真的要领导拍板。因此关键问题是这里的“有时候”到底应该怎么判断今天我就来讲讲架构设计流程的第3步评估和选择备选方案。
架构设计第3步评估和选择备选方案
前面提到了那么多指导思想真正应该选择哪种方法来评估和选择备选方案呢我的答案就是“360度环评”具体的操作方式为列出我们需要关注的质量属性点然后分别从这些质量属性的维度去评估每个方案再综合挑选适合当时情况的最优方案。
常见的方案质量属性点有性能、可用性、硬件成本、项目投入、复杂度、安全性、可扩展性等。在评估这些质量属性时需要遵循架构设计原则1“合适原则”和原则2“简单原则”避免贪大求全基本上某个质量属性能够满足一定时期内业务发展就可以了。
假如我们做一个购物网站现在的TPS是1000如果我们预期1年内能够发展到TPS 2000业务一年翻倍已经是很好的情况了在评估方案的性能时只要能超过2000的都是合适的方案而不是说淘宝的网站TPS是每秒10万我们的购物网站就要按照淘宝的标准也实现TPS 10万。
有的设计师会有这样的担心如果我们运气真的很好业务直接一年翻了10倍TPS从1000上升到10000那岂不是按照TPS 2000做的方案不合适了又要重新做方案
这种情况确实有可能存在但概率很小如果每次做方案都考虑这种小概率事件我们的方案会出现过度设计导致投入浪费。考虑这个问题的时候需要遵循架构设计原则3“演化原则”避免过度设计、一步到位的想法。按照原则3的思想即使真的出现这种情况那就算是重新做方案代价也是可以接受的因为业务如此迅猛发展钱和人都不是问题。例如淘宝和微信的发展历程中有过多次这样大规模重构系统的经历。
通常情况下如果某个质量属性评估和业务发展有关系例如性能、硬件成本等需要评估未来业务发展的规模时一种简单的方式是将当前的业务规模乘以2 ~4即可如果现在的基数较低可以乘以4如果现在基数较高可以乘以2。例如现在的TPS是1000则按照TPS 4000来设计方案如果现在TPS是10000则按照TPS 20000来设计方案。
当然最理想的情况是设计一个方案能够简单地扩容就能够跟上业务的发展。例如我们设计一个方案TPS 2000的时候只要2台机器TPS 20000的时候只需要简单地将机器扩展到20台即可。但现实往往没那么理想因为量变会引起质变具体哪些地方质变是很难提前很长时间能预判到的。举一个最简单的例子一个开发团队5个人开发了一套系统能够从TPS 2000平滑扩容到TPS 20000但是当业务规模真的达到TPS 20000的时候团队规模已经扩大到了20个人此时系统发生了两个质变
首先是团队规模扩大20个人的团队在同一个系统上开发开发效率变将很低系统迭代速度很慢经常出现某个功能开发完了要等另外的功能开发完成才能一起测试上线此时如果要解决问题就需要将系统拆分为更多子系统。
其次是原来单机房的集群设计不满足业务需求了,需要升级为异地多活的架构。
如果团队一开始就预测到这两个问题系统架构提前就拆分为多个子系统并且支持异地多活呢这种“事后诸葛亮”也是不行的因为最开始的时候团队只有5个人5个人在有限的时间内要完成后来20个人才能完成的高性能、异地多活、可扩展的架构项目时间会遥遥无期业务很难等待那么长的时间。
完成方案的360度环评后我们可以基于评估结果整理出360度环评表一目了然地看到各个方案的优劣点。但是360度环评表也只能帮助我们分析各个备选方案还是没有告诉我们具体选哪个方案原因就在于没有哪个方案是完美的极少出现某个方案在所有对比维度上都是最优的。例如引入开源方案工作量小但是可运维性和可扩展性差自研工作量大但是可运维和可维护性好使用C语言开发性能高但是目前团队C语言技术积累少使用Java技术积累多但是性能没有C语言开发高成本会高一些……诸如此类。
面临这种选择上的困难,有几种看似正确但实际错误的做法。
数量对比法简单地看哪个方案的优点多就选哪个。例如总共5个质量属性的对比其中A方案占优的有3个B方案占优的有2个所以就挑选A方案。
这种方案主要的问题在于把所有质量属性的重要性等同而没有考虑质量属性的优先级。例如对于BAT这类公司来说方案的成本都不是问题可用性和可扩展性比成本要更重要得多但对于创业公司来说成本可能就会变得很重要。
其次有时候会出现两个方案的优点数量是一样的情况。例如我们对比6个质量属性很可能出现两个方案各有3个优点这种情况下也没法选如果为了数量上的不对称强行再增加一个质量属性进行对比这个最后增加的不重要的属性反而成了影响方案选择的关键因素这又犯了没有区分质量属性的优先级的问题。
加权法每个质量属性给一个权重。例如性能的权重高中低分别得10分、5分、3分成本权重高中低分别是5分、3分、1分然后将每个方案的权重得分加起来最后看哪个方案的权重得分最高就选哪个。
这种方案主要的问题是无法客观地给出每个质量属性的权重得分。例如性能权重得分为何是10分、5分、3分而不是5分、3分、1分或者是100分、80分、60分这个分数是很难确定的没有明确的标准甚至会出现为了选某个方案设计师故意将某些权重分值调高而降低另外一些权重分值最后方案的选择就变成了一个数字游戏了。
正确的做法是按优先级选择,即架构师综合当前的业务发展情况、团队人员规模和技能、业务发展预测等因素,将质量属性按照优先级排序,首先挑选满足第一优先级的,如果方案都满足,那就再看第二优先级……以此类推。那会不会出现两个或者多个方案,每个质量属性的优缺点都一样的情况呢?理论上是可能的,但实际上是不可能的。前面我提到,在做备选方案设计时,不同的备选方案之间的差异要比较明显,差异明显的备选方案不可能所有的优缺点都是一样的。
评估和选择备选方案实战
再回到我们设计的场景“前浪微博”。针对上期提出的3个备选方案架构师组织了备选方案评审会议参加的人有研发、测试、运维、还有几个核心业务的主管。
1.备选方案1采用开源Kafka方案
业务主管倾向于采用Kafka方案因为Kafka已经比较成熟各个业务团队或多或少都了解过Kafka。
中间件团队部分研发人员也支持使用Kafka因为使用Kafka能节省大量的开发投入但部分人员认为Kafka可能并不适合我们的业务场景因为Kafka的设计目的是为了支撑大容量的日志消息传输而我们的消息队列是为了业务数据的可靠传输。
运维代表提出了强烈的反对意见首先Kafka是Scala语言编写的运维团队没有维护Scala语言开发的系统的经验出问题后很难快速处理其次目前运维团队已经有一套成熟的运维体系包括部署、监控、应急等使用Kafka无法融入这套体系需要单独投入运维人力。
测试代表也倾向于引入Kafka因为Kafka比较成熟无须太多测试投入。
2.备选方案2集群 + MySQL存储
中间件团队的研发人员认为这个方案比较简单但部分研发人员对于这个方案的性能持怀疑态度毕竟使用MySQL来存储消息数据性能肯定不如使用文件系统并且有的研发人员担心做这样的方案是否会影响中间件团队的技术声誉毕竟用MySQL来做消息队列看起来比较“土”、比较另类。
运维代表赞同这个方案因为这个方案可以融入到现有的运维体系中而且使用MySQL存储数据可靠性有保证运维团队也有丰富的MySQL运维经验但运维团队认为这个方案的成本比较高一个数据分组就需要4台机器2台服务器 + 2台数据库
测试代表认为这个方案测试人力投入较大,包括功能测试、性能测试、可靠性测试等都需要大量地投入人力。
业务主管对这个方案既不肯定也不否定,因为反正都不是业务团队来投入人力来开发,系统维护也是中间件团队负责,对业务团队来说,只要保证消息队列系统稳定和可靠即可。
3.备选方案3集群 + 自研存储系统
中间件团队部分研发人员认为这是一个很好的方案既能够展现中间件团队的技术实力性能上相比MySQL也要高但另外的研发人员认为这个方案复杂度太高按照目前的团队人力和技术实力要做到稳定可靠的存储系统需要耗时较长的迭代这个过程中消息队列系统可能因为存储出现严重问题例如文件损坏导致丢失大量数据。
运维代表不太赞成这个方案因为运维之前遇到过几次类似的存储系统故障导致数据丢失的问题损失惨重。例如MongoDB丢数据、Tokyo Tyrant丢数据无法恢复等。运维团队并不相信目前的中间件团队的技术实力足以支撑自己研发一个存储系统这让中间件团队的人员感觉有点不爽
测试代表赞同运维代表的意见,并且自研存储系统的测试难度也很高,投入也很大。
业务主管对自研存储系统也持保留意见因为从历史经验来看新系统上线肯定有bug而存储系统出bug是最严重的一旦出bug导致大量消息丢失对系统的影响会严重。
针对3个备选方案的讨论初步完成后架构师列出了3个方案的360度环评表
列出这个表格后,无法一眼看出具体哪个方案更合适,于是大家都把目光投向架构师,决策的压力现在集中在架构师身上了。
架构师经过思考后给出了最终选择备选方案2原因有
排除备选方案1的主要原因是可运维性因为再成熟的系统上线后都可能出问题如果出问题无法快速解决则无法满足业务的需求并且Kafka的主要设计目标是高性能日志传输而我们的消息队列设计的主要目标是业务消息的可靠传输。
排除备选方案3的主要原因是复杂度目前团队技术实力和人员规模总共6人还有其他中间件系统需要开发和维护无法支撑自研存储系统参考架构设计原则2简单原则
备选方案2的优点就是复杂度不高也可以很好地融入现有运维体系可靠性也有保障。
针对备选方案2的缺点架构师解释是
备选方案2的第一个缺点是性能业务目前需要的性能并不是非常高方案2能够满足即使后面性能需求增加方案2的数据分组方案也能够平行扩展进行支撑参考架构设计原则3演化原则
备选方案2的第二个缺点是成本一个分组就需要4台机器支撑目前的业务需求可能需要12台服务器但实际上备机包括服务器和数据库主要用作备份可以和其他系统并行部署在同一台机器上。
备选方案2的第三个缺点是技术上看起来并不很优越但我们的设计目的不是为了证明自己参考架构设计原则1合适原则而是更快更好地满足业务需求。
最后大家针对一些细节再次讨论后确定了选择备选方案2。
通过“前浪微博”这个案例我们可以看出备选方案的选择和很多因素相关并不单单考虑性能高低、技术是否优越这些纯技术因素。业务的需求特点、运维团队的经验、已有的技术体系、团队人员的技术水平都会影响备选方案的选择。因此同样是上述3个备选方案有的团队会选择引入Kafka例如很多创业公司的初创团队人手不够需要快速上线支撑业务有的会选择自研存储系统例如阿里开发了RocketMQ人多力量大业务复杂是主要原因
小结
今天我为你讲了架构设计流程的第三个步骤:评估和选择备选方案,并且基于模拟的“前浪微博”消息队列系统,给出了具体的评估和选择示例,希望对你有所帮助。
这就是今天的全部内容留一道思考题给你吧RocketMQ和Kafka有什么区别阿里为何选择了自己开发RocketMQ

View File

@@ -0,0 +1,132 @@
因收到Google相关通知网站将会择期关闭。相关通知内容
13 架构设计流程:详细方案设计
完成备选方案的设计和选择后我们终于可以长出一口气因为整个架构设计最难的一步已经完成了但整体方案尚未完成架构师还需继续努力。接下来我们需要再接再励将最终确定的备选方案进行细化使得备选方案变成一个可以落地的设计方案。所以今天我来讲讲架构设计流程第4步详细方案设计。
架构设计第4步详细方案设计
简单来说,详细方案设计就是将方案涉及的关键技术细节给确定下来。
假如我们确定使用Elasticsearch来做全文搜索那么就需要确定Elasticsearch的索引是按照业务划分还是一个大索引就可以了副本数量是2个、3个还是4个集群节点数量是3个还是6个等。
假如我们确定使用MySQL分库分表那么就需要确定哪些表要分库分表按照什么维度来分库分表分库分表后联合查询怎么处理等。
假如我们确定引入Nginx来做负载均衡那么Nginx的主备怎么做Nginx的负载均衡策略用哪个权重分配轮询ip_hash等。
可以看到详细设计方案里面其实也有一些技术点和备选方案类似。例如Nginx的负载均衡策略备选有轮询、权重分配、ip_hash、fair、url_hash五个具体选哪个呢看起来和备选方案阶段面临的问题类似但实际上这里的技术方案选择是很轻量级的我们无须像备选方案阶段那样操作而只需要简单根据这些技术的适用场景选择就可以了。
例如Nginx的负载均衡策略简单按照下面的规则选择就可以了。
轮询(默认)
每个请求按时间顺序逐一分配到不同的后端服务器后端服务器分配的请求数基本一致如果后端服务器“down掉”能自动剔除。
加权轮询
根据权重来进行轮询,权重高的服务器分配的请求更多,主要适应于后端服务器性能不均的情况,如新老服务器混用。
ip_hash
每个请求按访问IP的hash结果分配这样每个访客固定访问一个后端服务器主要用于解决session的问题如购物车类的应用。
fair
按后端服务器的响应时间来分配请求,响应时间短的优先分配,能够最大化地平衡各后端服务器的压力,可以适用于后端服务器性能不均衡的情况,也可以防止某台后端服务器性能不足的情况下还继续接收同样多的请求从而造成雪崩效应。
url_hash
按访问URL的hash结果来分配请求每个URL定向到同一个后端服务器适用于后端服务器能够将URL的响应结果缓存的情况。
这几个策略的适用场景区别还是比较明显的根据我们的业务需要挑选一个合适的即可。例如比如一个电商架构由于和session比较强相关因此如果用Nginx来做集群负载均衡那么选择ip_hash策略是比较合适的。
详细设计方案阶段可能遇到的一种极端情况就是在详细设计阶段发现备选方案不可行一般情况下主要的原因是备选方案设计时遗漏了某个关键技术点或者关键的质量属性。例如我曾经参与过一个项目在备选方案阶段确定是可行的但在详细方案设计阶段发现由于细节点太多方案非常庞大整个项目可能要开发长达1年时间最后只得废弃原来的备选方案重新调整项目目标、计划和方案。这个项目的主要失误就是在备选方案评估时忽略了开发周期这个质量属性。
幸运的是,这种情况可以通过下面方式有效地避免:
架构师不但要进行备选方案设计和选型还需要对备选方案的关键细节有较深入的理解。例如架构师选择了Elasticsearch作为全文搜索解决方案前提必须是架构师自己对Elasticsearch的设计原理有深入的理解比如索引、副本、集群等技术点而不能道听途说Elasticsearch很牛所以选择它更不能成为把“细节我们不讨论”这句话挂在嘴边的“PPT架构师”。
通过分步骤、分阶段、分系统等方式,尽量降低方案复杂度,方案本身的复杂度越高,某个细节推翻整个方案的可能性就越高,适当降低复杂性,可以减少这种风险。
如果方案本身就很复杂那就采取设计团队的方式来进行设计博采众长汇集大家的智慧和经验防止只有1~2个架构师可能出现的思维盲点或者经验盲区。
详细方案设计实战
虽然我们上期在“前浪微博”消息队列的架构设计挑选了备选方案2作为最终方案但备选方案设计阶段的方案粒度还比较粗无法真正指导开发人员进行后续的设计和开发因此需要在备选方案的基础上进一步细化。
下面我列出一些备选方案2典型的需要细化的点供参考有兴趣的同学可以自己尝试细化更多的设计点。
细化设计点1数据库表如何设计
数据库设计两类表一类是日志表用于消息写入时快速存储到MySQL中另一类是消息表每个消息队列一张表。
业务系统发布消息时,首先写入到日志表,日志表写入成功就代表消息写入成功;后台线程再从日志表中读取消息写入记录,将消息内容写入到消息表中。
业务系统读取消息时,从消息表中读取。
日志表表名为MQ_LOG包含的字段日志ID、发布者信息、发布时间、队列名称、消息内容。
消息表表名就是队列名称包含的字段消息ID递增生成、消息内容、消息发布时间、消息发布者。
日志表需要及时清除已经写入消息表的日志数据消息表最多保存30天的消息数据。
细化设计点2数据如何复制
直接采用MySQL主从复制即可只复制消息存储表不复制日志表。
细化设计点3主备服务器如何倒换
采用ZooKeeper来做主备决策主备服务器都连接到ZooKeeper建立自己的节点主服务器的路径规则为“/MQ/server/分区编号/master”备机为“/MQ/server/分区编号/slave”节点类型为EPHEMERAL。
备机监听主机的节点消息,当发现主服务器节点断连后,备服务器修改自己的状态,对外提供消息读取服务。
细化设计点4业务服务器如何写入消息
消息队列系统设计两个角色:生产者和消费者,每个角色都有唯一的名称。
消息队列系统提供SDK供各业务系统调用SDK从配置中读取所有消息队列系统的服务器信息SDK采取轮询算法发起消息写入请求给主服务器。如果某个主服务器无响应或者返回错误SDK将发起请求发送到下一台服务器。
细化设计点5业务服务器如何读取消息
消息队列系统提供SDK供各业务系统调用SDK从配置中读取所有消息队列系统的服务器信息轮流向所有服务器发起消息读取请求。
消息队列服务器需要记录每个消费者的消费状态,即当前消费者已经读取到了哪条消息,当收到消息读取请求时,返回下一条未被读取的消息给消费者。
细化设计点6业务服务器和消息队列服务器之间的通信协议如何设计
考虑到消息队列系统后续可能会对接多种不同编程语言编写的系统为了提升兼容性传输协议用TCP数据格式为ProtocolBuffer。
当然还有更多设计细节就不再一一列举,因此这还不是一个完整的设计方案,我希望可以通过这些具体实例来说明细化方案具体如何去做。
小结
今天我为你讲了架构设计流程的第四个步骤:详细方案设计,并且基于模拟的“前浪微博”消息队列系统,给出了具体的详细设计示例,希望对你有所帮助。这个示例并不完整,有兴趣的同学可以自己再详细思考一下还有哪些细节可以继续完善。
这就是今天的全部内容留一道思考题给你吧你见过“PPT架构师”么他们一般都具备什么特点

View File

@@ -0,0 +1,117 @@
因收到Google相关通知网站将会择期关闭。相关通知内容
14 高性能数据库集群:读写分离
“从0开始学架构”专栏已经更新了13期从各个方面阐述了架构设计相关的理论和流程包括架构设计起源、架构设计的目的、常见架构复杂度分析、架构设计原则、架构设计流程等掌握这些知识是做好架构设计的基础。
在具体的实践过程中,为了更快、更好地设计出优秀的架构,除了掌握这些基础知识外,还需要掌握业界已经成熟的各种架构模式。大部分情况下,我们做架构设计主要都是基于已有的成熟模式,结合业务和团队的具体情况,进行一定的优化或者调整;即使少部分情况我们需要进行较大的创新,前提也是需要对已有的各种架构模式和技术非常熟悉。
接下来,我将逐一介绍最常见的“高性能架构模式”“高可用架构模式”“可扩展架构模式”,这些模式可能你之前大概了解过,但其实每个方案里面都有很多细节,只有深入的理解这些细节才能理解常见的架构模式,进而设计出优秀的架构。
虽然近十年来各种存储技术飞速发展但关系数据库由于其ACID的特性和功能强大的SQL查询目前还是各种业务系统中关键和核心的存储系统很多场景下高性能的设计最核心的部分就是关系数据库的设计。
不管是为了满足业务发展的需要还是为了提升自己的竞争力关系数据库厂商Oracle、DB2、MySQL等在优化和提升单个数据库服务器的性能方面也做了非常多的技术优化和改进。但业务发展速度和数据增长速度远远超出数据库厂商的优化速度尤其是互联网业务兴起之后海量用户加上海量数据的特点单个数据库服务器已经难以满足业务需要必须考虑数据库集群的方式来提升性能。
从今天开始,我会分几期来介绍高性能数据库集群。高性能数据库集群的第一种方式是“读写分离”,其本质是将访问压力分散到集群中的多个节点,但是没有分散存储压力;第二种方式是“分库分表”,既可以分散访问压力,又可以分散存储压力。先来看看“读写分离”,下一期我再介绍“分库分表”。
读写分离原理
读写分离的基本原理是将数据库读写操作分散到不同的节点上,下面是其基本架构图。
读写分离的基本实现是:
数据库服务器搭建主从集群,一主一从、一主多从都可以。
数据库主机负责读写操作,从机只负责读操作。
数据库主机通过复制将数据同步到从机,每台数据库服务器都存储了所有的业务数据。
业务服务器将写操作发给数据库主机,将读操作发给数据库从机。
需要注意的是,这里用的是“主从集群”,而不是“主备集群”。“从机”的“从”可以理解为“仆从”,仆从是要帮主人干活的,“从机”是需要提供读数据的功能的;而“备机”一般被认为仅仅提供备份功能,不提供访问功能。所以使用“主从”还是“主备”,是要看场景的,这两个词并不是完全等同的。
读写分离的实现逻辑并不复杂,但有两个细节点将引入设计复杂度:主从复制延迟和分配机制。
复制延迟
以MySQL为例主从复制延迟可能达到1秒如果有大量数据同步延迟1分钟也是有可能的。主从复制延迟会带来一个问题如果业务服务器将数据写入到数据库主服务器后立刻1秒内进行读取此时读操作访问的是从机主机还没有将数据复制过来到从机读取数据是读不到最新数据的业务上就可能出现问题。例如用户刚注册完后立刻登录业务服务器会提示他“你还没有注册”而用户明明刚才已经注册成功了。
解决主从复制延迟有几种常见的方法:
1.写操作后的读操作指定发给数据库主服务器
例如注册账号完成后登录时读取账号的读操作也发给数据库主服务器。这种方式和业务强绑定对业务的侵入和影响较大如果哪个新来的程序员不知道这样写代码就会导致一个bug。
2.读从机失败后再读一次主机
这就是通常所说的“二次读取”二次读取和业务无绑定只需要对底层数据库访问的API进行封装即可实现代价较小不足之处在于如果有很多二次读取将大大增加主机的读操作压力。例如黑客暴力破解账号会导致大量的二次读取操作主机可能顶不住读操作的压力从而崩溃。
3.关键业务读写操作全部指向主机,非关键业务采用读写分离
例如,对于一个用户管理系统来说,注册+登录的业务读写操作全部访问主机,用户的介绍、爱好、等级等业务,可以采用读写分离,因为即使用户改了自己的自我介绍,在查询时却看到了自我介绍还是旧的,业务影响与不能登录相比就小很多,还可以忍受。
分配机制
将读写操作区分开来,然后访问不同的数据库服务器,一般有两种方式:程序代码封装和中间件封装。
1.程序代码封装
程序代码封装指在代码中抽象一个数据访问层所以有的文章也称这种方式为“中间层封装”实现读写操作分离和数据库服务器连接的管理。例如基于Hibernate进行简单封装就可以实现读写分离基本架构是
程序代码封装的方式具备几个特点:
实现简单,而且可以根据业务做较多定制化的功能。
每个编程语言都需要自己实现一次,无法通用,如果一个业务包含多个编程语言写的多个子系统,则重复开发的工作量比较大。
故障情况下,如果主从发生切换,则可能需要所有系统都修改配置并重启。
目前开源的实现方案中淘宝的TDDLTaobao Distributed Data Layer外号:头都大了是比较有名的。它是一个通用数据访问层所有功能封装在jar包中提供给业务代码调用。其基本原理是一个基于集中式配置的 jdbc datasource实现具有主备、读写分离、动态数据库配置等功能基本架构是
2.中间件封装
中间件封装指的是独立一套系统出来实现读写操作分离和数据库服务器连接的管理。中间件对业务服务器提供SQL兼容的协议业务服务器无须自己进行读写分离。对于业务服务器来说访问中间件和访问数据库没有区别事实上在业务服务器看来中间件就是一个数据库服务器。其基本架构是
数据库中间件的方式具备的特点是:
能够支持多种编程语言因为数据库中间件对业务服务器提供的是标准SQL接口。
数据库中间件要支持完整的SQL语法和数据库服务器的协议例如MySQL客户端和服务器的连接协议实现比较复杂细节特别多很容易出现bug需要较长的时间才能稳定。
数据库中间件自己不执行真正的读写操作,但所有的数据库操作请求都要经过中间件,中间件的性能要求也很高。
数据库主从切换对业务服务器无感知,数据库中间件可以探测数据库服务器的主从状态。例如,向某个测试表写入一条数据,成功的就是主机,失败的就是从机。
由于数据库中间件的复杂度要比程序代码封装高出一个数量级,一般情况下建议采用程序语言封装的方式,或者使用成熟的开源数据库中间件。如果是大公司,可以投入人力去实现数据库中间件,因为这个系统一旦做好,接入的业务系统越多,节省的程序开发投入就越多,价值也越大。
目前的开源数据库中间件方案中MySQL官方先是提供了MySQL Proxy但MySQL Proxy一直没有正式GA现在MySQL官方推荐MySQL Router。MySQL Router的主要功能有读写分离、故障自动切换、负载均衡、连接池等其基本架构如下
奇虎360公司也开源了自己的数据库中间件AtlasAtlas是基于MySQL Proxy实现的基本架构如下
以下是官方介绍,更多内容你可以参考这里。
Atlas是一个位于应用程序与MySQL之间中间件。在后端DB看来Atlas相当于连接它的客户端在前端应用看来Atlas相当于一个DB。Atlas作为服务端与应用程序通信它实现了MySQL的客户端和服务端协议同时作为客户端与MySQL通信。它对应用程序屏蔽了DB的细节同时为了降低MySQL负担它还维护了连接池。
小结
今天我为你讲了读写分离方式的原理,以及两个设计复杂度:复制延迟和分配机制,希望对你有所帮助。
这就是今天的全部内容,留一道思考题给你吧,数据库读写分离一般应用于什么场景?能支撑多大的业务规模?

View File

@@ -0,0 +1,164 @@
因收到Google相关通知网站将会择期关闭。相关通知内容
15 高性能数据库集群:分库分表
上期我讲了“读写分离”,读写分离分散了数据库读写操作的压力,但没有分散存储压力,当数据量达到千万甚至上亿条的时候,单台数据库服务器的存储能力会成为系统的瓶颈,主要体现在这几个方面:
数据量太大,读写的性能会下降,即使有索引,索引也会变得很大,性能同样会下降。
数据文件会变得很大,数据库备份和恢复需要耗费很长时间。
数据文件越大,极端情况下丢失数据的风险越高(例如,机房火灾导致数据库主备机都发生故障)。
基于上述原因,单个数据库服务器存储的数据量不能太大,需要控制在一定的范围内。为了满足业务数据存储的需求,就需要将存储分散到多台数据库服务器上。
今天我来介绍常见的分散存储的方法“分库分表”,其中包括“分库”和“分表”两大类。
业务分库
业务分库指的是按照业务模块将数据分散到不同的数据库服务器。例如,一个简单的电商网站,包括用户、商品、订单三个业务模块,我们可以将用户数据、商品数据、订单数据分开放到三台不同的数据库服务器上,而不是将所有数据都放在一台数据库服务器上。
虽然业务分库能够分散存储和访问压力,但同时也带来了新的问题,接下来我进行详细分析。
1.join操作问题
业务分库后原本在同一个数据库中的表分散到不同数据库中导致无法使用SQL的join查询。
例如“查询购买了化妆品的用户中女性用户的列表”这个功能虽然订单数据中有用户的ID信息但是用户的性别数据在用户数据库中如果在同一个库中简单的join查询就能完成但现在数据分散在两个不同的数据库中无法做join查询只能采取先从订单数据库中查询购买了化妆品的用户ID列表然后再到用户数据库中查询这批用户ID中的女性用户列表这样实现就比简单的join查询要复杂一些。
2.事务问题
原本在同一个数据库中不同的表可以在同一个事务中修改业务分库后表分散到不同的数据库中无法通过事务统一修改。虽然数据库厂商提供了一些分布式事务的解决方案例如MySQL的XA但性能实在太低与高性能存储的目标是相违背的。
例如,用户下订单的时候需要扣商品库存,如果订单数据和商品数据在同一个数据库中,我们可以使用事务来保证扣减商品库存和生成订单的操作要么都成功要么都失败,但分库后就无法使用数据库事务了,需要业务程序自己来模拟实现事务的功能。例如,先扣商品库存,扣成功后生成订单,如果因为订单数据库异常导致生成订单失败,业务程序又需要将商品库存加上;而如果因为业务程序自己异常导致生成订单失败,则商品库存就无法恢复了,需要人工通过日志等方式来手工修复库存异常。
3.成本问题
业务分库同时也带来了成本的代价本来1台服务器搞定的事情现在要3台如果考虑备份那就是2台变成了6台。
基于上述原因,对于小公司初创业务,并不建议一开始就这样拆分,主要有几个原因:
初创业务存在很大的不确定性,业务不一定能发展起来,业务开始的时候并没有真正的存储和访问压力,业务分库并不能为业务带来价值。
业务分库后表之间的join查询、数据库事务无法简单实现了。
业务分库后,因为不同的数据要读写不同的数据库,代码中需要增加根据数据类型映射到不同数据库的逻辑,增加了工作量。而业务初创期间最重要的是快速实现、快速验证,业务分库会拖慢业务节奏。
有的架构师可能会想:如果业务真的发展很快,岂不是很快就又要进行业务分库了?那为何不一开始就设计好呢?
其实这个问题很好回答,按照我前面提到的“架构设计三原则”,简单分析一下。
首先这里的“如果”事实上发生的概率比较低做10个业务有1个业务能活下去就很不错了更何况快速发展和中彩票的概率差不多。如果我们每个业务上来就按照淘宝、微信的规模去做架构设计不但会累死自己还会害死业务。
其次,如果业务真的发展很快,后面进行业务分库也不迟。因为业务发展好,相应的资源投入就会加大,可以投入更多的人和更多的钱,那业务分库带来的代码和业务复杂的问题就可以通过增加人来解决,成本问题也可以通过增加资金来解决。
第三单台数据库服务器的性能其实也没有想象的那么弱一般来说单台数据库服务器能够支撑10万用户量量级的业务初创业务从0发展到10万级用户并不是想象得那么快。
而对于业界成熟的大公司来说,由于已经有了业务分库的成熟解决方案,并且即使是尝试性的新业务,用户规模也是海量的,这与前面提到的初创业务的小公司有本质区别,因此最好在业务开始设计时就考虑业务分库。例如,在淘宝上做一个新的业务,由于已经有成熟的数据库解决方案,用户量也很大,需要在一开始就设计业务分库甚至接下来介绍的分表方案。
分表
将不同业务数据分散存储到不同的数据库服务器,能够支撑百万甚至千万用户规模的业务,但如果业务继续发展,同一业务的单表数据也会达到单台数据库服务器的处理瓶颈。例如,淘宝的几亿用户数据,如果全部存放在一台数据库服务器的一张表中,肯定是无法满足性能要求的,此时就需要对单表数据进行拆分。
单表数据拆分有两种方式:垂直分表和水平分表。示意图如下:
为了形象地理解垂直拆分和水平拆分的区别,可以想象你手里拿着一把刀,面对一个蛋糕切一刀:
从上往下切就是垂直切分因为刀的运行轨迹与蛋糕是垂直的这样可以把蛋糕切成高度相等面积可以相等也可以不相等的两部分对应到表的切分就是表记录数相同但包含不同的列。例如示意图中的垂直切分会把表切分为两个表一个表包含ID、name、age、sex列另外一个表包含ID、nickname、description列。
从左往右切就是水平切分因为刀的运行轨迹与蛋糕是平行的这样可以把蛋糕切成面积相等高度可以相等也可以不相等的两部分对应到表的切分就是表的列相同但包含不同的行数据。例如示意图中的水平切分会把表分为两个表两个表都包含ID、name、age、sex、nickname、description列但是一个表包含的是ID从1到999999的行数据另一个表包含的是ID从1000000到9999999的行数据。
上面这个示例比较简单,只考虑了一次切分的情况,实际架构设计过程中并不局限切分的次数,可以切两次,也可以切很多次,就像切蛋糕一样,可以切很多刀。
单表进行切分后,是否要将切分后的多个表分散在不同的数据库服务器中,可以根据实际的切分效果来确定,并不强制要求单表切分为多表后一定要分散到不同数据库中。原因在于单表切分为多表后,新的表即使在同一个数据库服务器中,也可能带来可观的性能提升,如果性能能够满足业务要求,是可以不拆分到多台数据库服务器的,毕竟我们在上面业务分库的内容看到业务分库也会引入很多复杂性的问题;如果单表拆分为多表后,单台服务器依然无法满足性能要求,那就不得不再次进行业务分库的设计了。
分表能够有效地分散存储压力和带来性能提升,但和分库一样,也会引入各种复杂性。
1.垂直分表
垂直分表适合将表中某些不常用且占了大量空间的列拆分出去。例如前面示意图中的nickname和description字段假设我们是一个婚恋网站用户在筛选其他用户的时候主要是用age和sex两个字段进行查询而nickname和description两个字段主要用于展示一般不会在业务查询中用到。description本身又比较长因此我们可以将这两个字段独立到另外一张表中这样在查询age和sex时就能带来一定的性能提升。
垂直分表引入的复杂性主要体现在表操作的数量要增加。例如原来只要一次查询就可以获取name、age、sex、nickname、description现在需要两次查询一次查询获取name、age、sex另外一次查询获取nickname、description。
不过相比接下来要讲的水平分表,这个复杂性就是小巫见大巫了。
2.水平分表
水平分表适合表行数特别大的表有的公司要求单表行数超过5000万就必须进行分表这个数字可以作为参考但并不是绝对标准关键还是要看表的访问性能。对于一些比较复杂的表可能超过1000万就要分表了而对于一些简单的表即使存储数据超过1亿行也可以不分表。但不管怎样当看到表的数据量达到千万级别时作为架构师就要警觉起来因为这很可能是架构的性能瓶颈或者隐患。
水平分表相比垂直分表,会引入更多的复杂性,主要表现在下面几个方面:
路由
水平分表后,某条数据具体属于哪个切分后的子表,需要增加路由算法进行计算,这个算法会引入一定的复杂性。
常见的路由算法有:
范围路由选取有序的数据列例如整形、时间戳等作为路由的条件不同分段分散到不同的数据库表中。以最常见的用户ID为例路由算法可以按照1000000的范围大小进行分段1 ~ 999999放到数据库1的表中1000000 ~ 1999999放到数据库2的表中以此类推。
范围路由设计的复杂点主要体现在分段大小的选取上分段太小会导致切分后子表数量过多增加维护复杂度分段太大可能会导致单表依然存在性能问题一般建议分段大小在100万至2000万之间具体需要根据业务选取合适的分段大小。
范围路由的优点是可以随着数据的增加平滑地扩充新的表。例如现在的用户是100万如果增加到1000万只需要增加新的表就可以了原有的数据不需要动。
范围路由的一个比较隐含的缺点是分布不均匀假如按照1000万来进行分表有可能某个分段实际存储的数据量只有1000条而另外一个分段实际存储的数据量有900万条。
Hash路由选取某个列或者某几个列组合也可以的值进行Hash运算然后根据Hash结果分散到不同的数据库表中。同样以用户ID为例假如我们一开始就规划了10个数据库表路由算法可以简单地用user_id % 10的值来表示数据所属的数据库表编号ID为985的用户放到编号为5的子表中ID为10086的用户放到编号为6的字表中。
Hash路由设计的复杂点主要体现在初始表数量的选取上表数量太多维护比较麻烦表数量太少又可能导致单表性能存在问题。而用了Hash路由后增加子表数量是非常麻烦的所有数据都要重分布。
Hash路由的优缺点和范围路由基本相反Hash路由的优点是表分布比较均匀缺点是扩充新的表很麻烦所有数据都要重分布。
配置路由配置路由就是路由表用一张独立的表来记录路由信息。同样以用户ID为例我们新增一张user_router表这个表包含user_id和table_id两列根据user_id就可以查询对应的table_id。
配置路由设计简单,使用起来非常灵活,尤其是在扩充表的时候,只需要迁移指定的数据,然后修改路由表就可以了。
配置路由的缺点就是必须多查询一次,会影响整体性能;而且路由表本身如果太大(例如,几亿条数据),性能同样可能成为瓶颈,如果我们再次将路由表分库分表,则又面临一个死循环式的路由算法选择问题。
join操作
水平分表后数据分散在多个表中如果需要与其他表进行join查询需要在业务代码或者数据库中间件中进行多次join查询然后将结果合并。
count()操作
水平分表后虽然物理上数据分散到多个表中但某些业务逻辑上还是会将这些表当作一个表来处理。例如获取记录总数用于分页或者展示水平分表前用一个count()就能完成的操作,在分表后就没那么简单了。常见的处理方式有下面两种:
count()相加具体做法是在业务代码或者数据库中间件中对每个表进行count()操作然后将结果相加。这种方式实现简单缺点就是性能比较低。例如水平分表后切分为20张表则要进行20次count(*)操作,如果串行的话,可能需要几秒钟才能得到结果。
记录数表具体做法是新建一张表假如表名为“记录数表”包含table_name、row_count两个字段每次插入或者删除子表数据成功后都更新“记录数表”。
这种方式获取表记录数的性能要大大优于count()相加的方式,因为只需要一次简单查询就可以获取数据。缺点是复杂度增加不少,对子表的操作要同步操作“记录数表”,如果有一个业务逻辑遗漏了,数据就会不一致;且针对“记录数表”的操作和针对子表的操作无法放在同一事务中进行处理,异常的情况下会出现操作子表成功了而操作记录数表失败,同样会导致数据不一致。
此外记录数表的方式也增加了数据库的写压力因为每次针对子表的insert和delete操作都要update记录数表所以对于一些不要求记录数实时保持精确的业务也可以通过后台定时更新记录数表。定时更新实际上就是“count()相加”和“记录数表”的结合即定时通过count()相加计算表的记录数,然后更新记录数表中的数据。
order by操作
水平分表后,数据分散到多个子表中,排序操作无法在数据库中完成,只能由业务代码或者数据库中间件分别查询每个子表中的数据,然后汇总进行排序。
实现方法
和数据库读写分离类似分库分表具体的实现方式也是“程序代码封装”和“中间件封装”但实现会更复杂。读写分离实现时只要识别SQL操作是读操作还是写操作通过简单的判断SELECT、UPDATE、INSERT、DELETE几个关键字就可以做到而分库分表的实现除了要判断操作类型外还要判断SQL中具体需要操作的表、操作函数例如count函数)、order by、group by操作等然后再根据不同的操作进行不同的处理。例如order by操作需要先从多个库查询到各个库的数据然后再重新order by才能得到最终的结果。
小结
今天我为你讲了高性能数据库集群的分库分表架构,包括业务分库产生的问题和分表的两种方式及其带来的复杂度,希望对你有所帮助。
这就是今天的全部内容,留一道思考题给你吧,你认为什么时候引入分库分表是合适的?是数据库性能不够的时候就开始分库分表么?

View File

@@ -0,0 +1,255 @@
因收到Google相关通知网站将会择期关闭。相关通知内容
16 高性能NoSQL
关系数据库经过几十年的发展后已经非常成熟强大的SQL功能和ACID的属性使得关系数据库广泛应用于各式各样的系统中但这并不意味着关系数据库是完美的关系数据库存在如下缺点。
关系数据库存储的是行记录,无法存储数据结构
以微博的关注关系为例“我关注的人”是一个用户ID列表使用关系数据库存储只能将列表拆成多行然后再查询出来组装无法直接存储一个列表。
关系数据库的schema扩展很不方便
关系数据库的表结构schema是强约束操作不存在的列会报错业务变化时扩充列也比较麻烦需要执行DDLdata definition language如CREATE、ALTER、DROP等语句修改而且修改时可能会长时间锁表例如MySQL可能将表锁住1个小时
关系数据库在大数据场景下I/O较高
如果对一些大量数据的表进行统计之类的运算关系数据库的I/O会很高因为即使只针对其中某一列进行运算关系数据库也会将整行数据从存储设备读入内存。
关系数据库的全文搜索功能比较弱
关系数据库的全文搜索只能使用like进行整表扫描匹配性能非常低在互联网这种搜索复杂的场景下无法满足业务要求。
针对上述问题分别诞生了不同的NoSQL解决方案这些方案与关系数据库相比在某些应用场景下表现更好。但世上没有免费的午餐NoSQL方案带来的优势本质上是牺牲ACID中的某个或者某几个特性因此我们不能盲目地迷信NoSQL是银弹而应该将NoSQL作为SQL的一个有力补充NoSQL != No SQL而是NoSQL = Not Only SQL。
常见的NoSQL方案分为4类。
K-V存储解决关系数据库无法存储数据结构的问题以Redis为代表。
文档数据库解决关系数据库强schema约束的问题以MongoDB为代表。
列式数据库解决关系数据库大数据场景下的I/O问题以HBase为代表。
全文搜索引擎解决关系数据库的全文搜索性能问题以Elasticsearch为代表。
今天我来介绍一下各种高性能NoSQL方案的典型特征和应用场景。
K-V存储
K-V存储的全称是Key-Value存储其中Key是数据的标识和关系数据库中的主键含义一样Value就是具体的数据。
Redis是K-V存储的典型代表它是一款开源基于BSD许可的高性能K-V缓存和存储系统。Redis的Value是具体的数据结构包括string、hash、list、set、sorted set、bitmap和hyperloglog所以常常被称为数据结构服务器。
以List数据结构为例Redis提供了下面这些典型的操作更多请参考链接http://redis.cn/commands.html#list
LPOP key从队列的左边出队一个元素。
LINDEX key index获取一个元素通过其索引列表。
LLEN key获得队列List的长度。
RPOP key从队列的右边出队一个元素。
以上这些功能如果用关系数据库来实现就会变得很复杂。例如LPOP操作是移除并返回 key对应的list的第一个元素。如果用关系数据库来存储为了达到同样目的需要进行下面的操作
每条数据除了数据编号例如行ID还要有位置编号否则没有办法判断哪条数据是第一条。注意这里不能用行ID作为位置编号因为我们会往列表头部插入数据。
查询出第一条数据。
删除第一条数据。
更新从第二条开始的所有数据的位置编号。
可以看出关系数据库的实现很麻烦而且需要进行多次SQL操作性能很低。
Redis的缺点主要体现在并不支持完整的ACID事务Redis虽然提供事务功能但Redis的事务和关系数据库的事务不可同日而语Redis的事务只能保证隔离性和一致性I和C无法保证原子性和持久性A和D
虽然Redis并没有严格遵循ACID原则但实际上大部分业务也不需要严格遵循ACID原则。以上面的微博关注操作为例即使系统没有将A加入B的粉丝列表其实业务影响也非常小因此我们在设计方案时需要根据业务特性和要求来确定是否可以用Redis而不能因为Redis不遵循ACID原则就直接放弃。
文档数据库
为了解决关系数据库schema带来的问题文档数据库应运而生。文档数据库最大的特点就是no-schema可以存储和读取任意的数据。目前绝大部分文档数据库存储的数据格式是JSON或者BSON因为JSON数据是自描述的无须在使用前定义字段读取一个JSON中不存在的字段也不会导致SQL那样的语法错误。
文档数据库的no-schema特性给业务开发带来了几个明显的优势。
1.新增字段简单
业务上增加新的字段无须再像关系数据库一样要先执行DDL语句修改表结构程序代码直接读写即可。
2.历史数据不会出错
对于历史数据,即使没有新增的字段,也不会导致错误,只会返回空值,此时代码进行兼容处理即可。
3.可以很容易存储复杂数据
JSON是一种强大的描述语言能够描述复杂的数据结构。例如我们设计一个用户管理系统用户的信息有ID、姓名、性别、爱好、邮箱、地址、学历信息。其中爱好是列表因为可以有多个爱好地址是一个结构包括省市区楼盘地址学历包括学校、专业、入学毕业年份信息等。如果我们用关系数据库来存储需要设计多张表包括基本信息ID、姓名、性别、邮箱、爱好ID、爱好、地址省、市、区、详细地址、学历入学时间、毕业时间、学校名称、专业而使用文档数据库一个JSON就可以全部描述。
{
"id": 10000,
"name": "James",
"sex": "male",
"hobbies": [
"football",
"playing",
"singing"
],
"email": "[email protected]",
"address": {
"province": "GuangDong",
"city": "GuangZhou",
"district": "Tianhe",
"detail": "PingYun Road 163"
},
"education": [
{
"begin": "2000-09-01",
"end": "2004-07-01",
"school": "UESTC",
"major": "Computer Science & Technology"
},
{
"begin": "2004-09-01",
"end": "2007-07-01",
"school": "SCUT",
"major": "Computer Science & Technology"
}
]
}
通过这个样例我们看到使用JSON来描述数据比使用关系型数据库表来描述数据方便和容易得多而且更加容易理解。
文档数据库的这个特点,特别适合电商和游戏这类的业务场景。以电商为例,不同商品的属性差异很大。例如,冰箱的属性和笔记本电脑的属性差异非常大,如下图所示。
即使是同类商品也有不同的属性。例如LCD和LED显示器两者有不同的参数指标。这种业务场景如果使用关系数据库来存储数据就会很麻烦而使用文档数据库会简单、方便许多扩展新的属性也更加容易。
文档数据库no-schema的特性带来的这些优势也是有代价的最主要的代价就是不支持事务。例如使用MongoDB来存储商品库存系统创建订单的时候首先需要减扣库存然后再创建订单。这是一个事务操作用关系数据库来实现就很简单但如果用MongoDB来实现就无法做到事务性。异常情况下可能出现库存被扣减了但订单没有创建的情况。因此某些对事务要求严格的业务场景是不能使用文档数据库的。
文档数据库另外一个缺点就是无法实现关系数据库的join操作。例如我们有一个用户信息表和一个订单表订单表中有买家用户id。如果要查询“购买了苹果笔记本用户中的女性用户”用关系数据库来实现一个简单的join操作就搞定了而用文档数据库是无法进行join查询的需要查两次一次查询订单表中购买了苹果笔记本的用户然后再查询这些用户哪些是女性用户。
列式数据库
顾名思义,列式数据库就是按照列来存储数据的数据库,与之对应的传统关系数据库被称为“行式数据库”,因为关系数据库是按照行来存储数据的。
关系数据库按照行式来存储数据,主要有以下几个优势:
业务同时读取多个列时效率高,因为这些列都是按行存储在一起的,一次磁盘操作就能够把一行数据中的各个列都读取到内存中。
能够一次性完成对一行中的多个列的写操作,保证了针对行数据写操作的原子性和一致性;否则如果采用列存储,可能会出现某次写操作,有的列成功了,有的列失败了,导致数据不一致。
我们可以看到行式存储的优势是在特定的业务场景下才能体现如果不存在这样的业务场景那么行式存储的优势也将不复存在甚至成为劣势典型的场景就是海量数据进行统计。例如计算某个城市体重超重的人员数据实际上只需要读取每个人的体重这一列并进行统计即可而行式存储即使最终只使用一列也会将所有行数据都读取出来。如果单行用户信息有1KB其中体重只有4个字节行式存储还是会将整行1KB数据全部读取到内存中这是明显的浪费。而如果采用列式存储每个用户只需要读取4字节的体重数据即可I/O将大大减少。
除了节省I/O列式存储还具备更高的存储压缩比能够节省更多的存储空间。普通的行式数据库一般压缩率在3:1到5:1左右而列式数据库的压缩率一般在8:1到30:1左右因为单个列的数据相似度相比行来说更高能够达到更高的压缩率。
同样,如果场景发生变化,列式存储的优势又会变成劣势。典型的场景是需要频繁地更新多个列。因为列式存储将不同列存储在磁盘上不连续的空间,导致更新多个列时磁盘是随机写操作;而行式存储时同一行多个列都存储在连续的空间,一次磁盘写操作就可以完成,列式存储的随机写效率要远远低于行式存储的写效率。此外,列式存储高压缩率在更新场景下也会成为劣势,因为更新时需要将存储数据解压后更新,然后再压缩,最后写入磁盘。
基于上述列式存储的优缺点,一般将列式存储应用在离线的大数据分析和统计场景中,因为这种场景主要是针对部分列单列进行操作,且数据写入后就无须再更新删除。
全文搜索引擎
传统的关系型数据库通过索引来达到快速查询的目的,但是在全文搜索的业务场景下,索引也无能为力,主要体现在:
全文搜索的条件可以随意排列组合,如果通过索引来满足,则索引的数量会非常多。
全文搜索的模糊匹配方式索引无法满足只能用like查询而like查询是整表扫描效率非常低。
我举一个具体的例子来看看关系型数据库为何无法满足全文搜索的要求。假设我们做一个婚恋网站,其主要目的是帮助程序员找朋友,但模式与传统婚恋网站不同,是“程序员发布自己的信息,用户来搜索程序员”。程序员的信息表设计如下:
我们来看一下这个简单业务的搜索场景:
美女1听说PHP是世界上最好的语言那么PHP的程序员肯定是钱最多的而且我妈一定要我找一个上海的。
美女1的搜索条件是“性别 + PHP + 上海”其中“PHP”要用模糊匹配查询“语言”列“上海”要查询“地点”列如果用索引支撑则需要建立“地点”这个索引。
美女2我好崇拜这些技术哥哥啊要是能找一个鹅厂技术哥哥陪我旅游就更好了。
美女2的搜索条件是“性别 + 鹅厂 + 旅游”,其中“旅游”要用模糊匹配查询“爱好”列,“鹅厂”需要查询“单位”列,如果要用索引支撑,则需要建立“单位”索引。
美女3我是一个“女程序员”想在北京找一个猫厂的Java技术专家。
美女3的搜索条件是“性别 + 猫厂 + 北京 + Java + 技术专家”,其中“猫厂 + 北京”可以通过索引来查询但“Java”“技术专家”都只能通过模糊匹配来查询。
帅哥4程序员妹子有没有漂亮的呢试试看看。
帅哥4的搜索条件是“性别 + 美丽 + 美女”,只能通过模糊匹配搜索“自我介绍”列。
以上只是简单举个例子,实际上搜索条件是无法列举完全的,各种排列组合非常多,通过这个简单的样例我们就可以看出关系数据库在支撑全文搜索时的不足。
1.全文搜索基本原理
全文搜索引擎的技术原理被称为“倒排索引”Inverted index也常被称为反向索引、置入档案或反向档案是一种索引方法其基本原理是建立单词到文档的索引。之所以被称为“倒排”索引是和“正排“索引相对的“正排索引”的基本原理是建立文档到单词的索引。我们通过一个简单的样例来说明这两种索引的差异。
假设我们有一个技术文章的网站,里面收集了各种技术文章,用户可以在网站浏览或者搜索文章。
正排索引示例:
正排索引适用于根据文档名称来查询文档内容。例如,用户在网站上单击了“面向对象葵花宝典是什么”,网站根据文章标题查询文章的内容展示给用户。
倒排索引示例:
倒排索引适用于根据关键词来查询文档内容。例如,用户只是想看“设计”相关的文章,网站需要将文章内容中包含“设计”一词的文章都搜索出来展示给用户。
2.全文搜索的使用方式
全文搜索引擎的索引对象是单词和文档,而关系数据库的索引对象是键和行,两者的术语差异很大,不能简单地等同起来。因此,为了让全文搜索引擎支持关系型数据的全文搜索,需要做一些转换操作,即将关系型数据转换为文档数据。
目前常用的转换方式是将关系型数据按照对象的形式转换为JSON文档然后将JSON文档输入全文搜索引擎进行索引。我同样以程序员的基本信息表为例看看如何转换。
将前面样例中的程序员表格转换为JSON文档可以得到3个程序员信息相关的文档我以程序员1为例
{
"id": 1,
"姓名": "多隆",
"性别": "男",
"地点": "北京",
"单位": "猫厂",
"爱好": "写代码,旅游,马拉松",
"语言": "Java、C++、PHP",
"自我介绍": "技术专家,简单,为人热情"
}
全文搜索引擎能够基于JSON文档建立全文索引然后快速进行全文搜索。以Elasticsearch为例其索引基本原理如下
Elastcisearch是分布式的文档存储方式。它能存储和检索复杂的数据结构——序列化成为JSON文档——以实时的方式。
在Elasticsearch中每个字段的所有数据都是默认被索引的。即每个字段都有为了快速检索设置的专用倒排索引。而且不像其他多数的数据库它能在相同的查询中使用所有倒排索引并以惊人的速度返回结果。
https://www.elastic.co/guide/cn/elasticsearch/guide/current/data-in-data-out.html
小结
今天我为你讲了为了弥补关系型数据库缺陷而产生的NoSQL技术希望对你有所帮助。
这就是今天的全部内容留一道思考题给你吧因为NoSQL的方案功能都很强大有人认为NoSQL = No SQL架构设计的时候无需再使用关系数据库对此你怎么看

View File

@@ -0,0 +1,105 @@
因收到Google相关通知网站将会择期关闭。相关通知内容
17 高性能缓存架构
虽然我们可以通过各种手段来提升存储系统的性能,但在某些复杂的业务场景下,单纯依靠存储系统的性能提升不够的,典型的场景有:
需要经过复杂运算后得出的数据,存储系统无能为力
例如一个论坛需要在首页展示当前有多少用户同时在线如果使用MySQL来存储当前用户状态则每次获取这个总数都要“count(*)”大量数据这样的操作无论怎么优化MySQL性能都不会太高。如果要实时展示用户同时在线数则MySQL性能无法支撑。
读多写少的数据,存储系统有心无力
绝大部分在线业务都是读多写少。例如微博、淘宝、微信这类互联网业务读业务占了整体业务量的90%以上。以微博为例一个明星发一条微博可能几千万人来浏览。如果使用MySQL来存储微博用户写微博只有一条insert语句但每个用户浏览时都要select一次即使有索引几千万条select语句对MySQL数据库的压力也会非常大。
缓存就是为了弥补存储系统在这些复杂业务场景下的不足,其基本原理是将可能重复使用的数据放到内存中,一次生成、多次使用,避免每次使用都去访问存储系统。
缓存能够带来性能的大幅提升以Memcache为例单台Memcache服务器简单的key-value查询能够达到TPS 50000以上其基本的架构是
缓存虽然能够大大减轻存储系统的压力,但同时也给架构引入了更多复杂性。架构设计时如果没有针对缓存的复杂性进行处理,某些场景下甚至会导致整个系统崩溃。今天,我来逐一分析缓存的架构设计要点。
缓存穿透
缓存穿透是指缓存没有发挥作用,业务系统虽然去缓存查询数据,但缓存中没有数据,业务系统需要再次去存储系统查询数据。通常情况下有两种情况:
1.存储数据不存在
第一种情况是被访问的数据确实不存在。一般情况下,如果存储系统中没有某个数据,则不会在缓存中存储相应的数据,这样就导致用户查询的时候,在缓存中找不到对应的数据,每次都要去存储系统中再查询一遍,然后返回数据不存在。缓存在这个场景中并没有起到分担存储系统访问压力的作用。
通常情况下,业务上读取不存在的数据的请求量并不会太大,但如果出现一些异常情况,例如被黑客攻击,故意大量访问某些读取不存在数据的业务,有可能会将存储系统拖垮。
这种情况的解决办法比较简单,如果查询存储系统的数据没有找到,则直接设置一个默认值(可以是空值,也可以是具体的值)存到缓存中,这样第二次读取缓存时就会获取到默认值,而不会继续访问存储系统。
2.缓存数据生成耗费大量时间或者资源
第二种情况是存储系统中存在数据,但生成缓存数据需要耗费较长时间或者耗费大量资源。如果刚好在业务访问的时候缓存失效了,那么也会出现缓存没有发挥作用,访问压力全部集中在存储系统上的情况。
典型的就是电商的商品分页,假设我们在某个电商平台上选择“手机”这个类别查看,由于数据巨大,不能把所有数据都缓存起来,只能按照分页来进行缓存,由于难以预测用户到底会访问哪些分页,因此业务上最简单的就是每次点击分页的时候按分页计算和生成缓存。通常情况下这样实现是基本满足要求的,但是如果被竞争对手用爬虫来遍历的时候,系统性能就可能出现问题。
具体的场景有:
分页缓存的有效期设置为1天因为设置太长时间的话缓存不能反应真实的数据。
通常情况下用户不会从第1页到最后1页全部看完一般用户访问集中在前10页因此第10页以后的缓存过期失效的可能性很大。
竞争对手每周来爬取数据爬虫会将所有分类的所有数据全部遍历从第1页到最后1页全部都会读取此时很多分页缓存可能都失效了。
由于很多分页都没有缓存数据从数据库中生成缓存数据又非常耗费性能order by limit操作因此爬虫会将整个数据库全部拖慢。
这种情况并没有太好的解决方案因为爬虫会遍历所有的数据而且什么时候来爬取也是不确定的可能是每天都来也可能是每周也可能是一个月来一次我们也不可能为了应对爬虫而将所有数据永久缓存。通常的应对方案要么就是识别爬虫然后禁止访问但这可能会影响SEO和推广要么就是做好监控发现问题后及时处理因为爬虫不是攻击不会进行暴力破坏对系统的影响是逐步的监控发现问题后有时间进行处理。
缓存雪崩
缓存雪崩是指当缓存失效(过期)后引起系统性能急剧下降的情况。当缓存过期被清除后,业务系统需要重新生成缓存,因此需要再次访问存储系统,再次进行运算,这个处理步骤耗时几十毫秒甚至上百毫秒。而对于一个高并发的业务系统来说,几百毫秒内可能会接到几百上千个请求。由于旧的缓存已经被清除,新的缓存还未生成,并且处理这些请求的线程都不知道另外有一个线程正在生成缓存,因此所有的请求都会去重新生成缓存,都会去访问存储系统,从而对存储系统造成巨大的性能压力。这些压力又会拖慢整个系统,严重的会造成数据库宕机,从而形成一系列连锁反应,造成整个系统崩溃。
缓存雪崩的常见解决方法有两种:更新锁机制和后台更新机制。
1.更新锁
对缓存更新操作进行加锁保护,保证只有一个线程能够进行缓存更新,未能获取更新锁的线程要么等待锁释放后重新读取缓存,要么就返回空值或者默认值。
对于采用分布式集群的业务系统由于存在几十上百台服务器即使单台服务器只有一个线程更新缓存但几十上百台服务器一起算下来也会有几十上百个线程同时来更新缓存同样存在雪崩的问题。因此分布式集群的业务系统要实现更新锁机制需要用到分布式锁如ZooKeeper。
2.后台更新
由后台线程来更新缓存,而不是由业务线程来更新缓存,缓存本身的有效期设置为永久,后台线程定时更新缓存。
后台定时机制需要考虑一种特殊的场景,当缓存系统内存不够时,会“踢掉”一些缓存数据,从缓存被“踢掉”到下一次定时更新缓存的这段时间内,业务线程读取缓存返回空值,而业务线程本身又不会去更新缓存,因此业务上看到的现象就是数据丢了。解决的方式有两种:
后台线程除了定时更新缓存还要频繁地去读取缓存例如1秒或者100毫秒读取一次如果发现缓存被“踢了”就立刻更新缓存这种方式实现简单但读取时间间隔不能设置太长因为如果缓存被踢了缓存读取间隔时间又太长这段时间内业务访问都拿不到真正的数据而是一个空的缓存值用户体验一般。
业务线程发现缓存失效后,通过消息队列发送一条消息通知后台线程更新缓存。可能会出现多个业务线程都发送了缓存更新消息,但其实对后台线程没有影响,后台线程收到消息后更新缓存前可以判断缓存是否存在,存在就不执行更新操作。这种方式实现依赖消息队列,复杂度会高一些,但缓存更新更及时,用户体验更好。
后台更新既适应单机多线程的场景,也适合分布式集群的场景,相比更新锁机制要简单一些。
后台更新机制还适合业务刚上线的时候进行缓存预热。缓存预热指系统上线后,将相关的缓存数据直接加载到缓存系统,而不是等待用户访问才来触发缓存加载。
缓存热点
虽然缓存系统本身的性能比较高,但对于一些特别热点的数据,如果大部分甚至所有的业务请求都命中同一份缓存数据,则这份数据所在的缓存服务器的压力也很大。例如,某明星微博发布“我们”来宣告恋爱了,短时间内上千万的用户都会来围观。
缓存热点的解决方案就是复制多份缓存副本将请求分散到多个缓存服务器上减轻缓存热点导致的单台缓存服务器压力。以微博为例对于粉丝数超过100万的明星每条微博都可以生成100份缓存缓存的数据是一样的通过在缓存的key里面加上编号进行区分每次读缓存时都随机读取其中某份缓存。
缓存副本设计有一个细节需要注意,就是不同的缓存副本不要设置统一的过期时间,否则就会出现所有缓存副本同时生成同时失效的情况,从而引发缓存雪崩效应。正确的做法是设定一个过期时间范围,不同的缓存副本的过期时间是指定范围内的随机值。
实现方式
由于缓存的各种访问策略和存储的访问策略是相关的,因此上面的各种缓存设计方案通常情况下都是集成在存储访问方案中,可以采用“程序代码实现”的中间层方式,也可以采用独立的中间件来实现。
小结
今天我为你讲了高性能架构设计中缓存设计需要注意的几个关键点,这些关键点本身在技术上都不复杂,但可能对业务产生很大的影响,轻则系统响应变慢,重则全站宕机,架构师在设计架构的时候要特别注意这些细节,希望这些设计关键点和技术方案对你有所帮助。
这就是今天的全部内容,留一道思考题给你吧,分享一下你所在的业务发生过哪些因为缓存导致的线上问题?采取了什么样的解决方案?效果如何?

View File

@@ -0,0 +1,123 @@
因收到Google相关通知网站将会择期关闭。相关通知内容
18 单服务器高性能模式PPC与TPC
高性能是每个程序员的追求无论我们是做一个系统还是写一行代码都希望能够达到高性能的效果而高性能又是最复杂的一环磁盘、操作系统、CPU、内存、缓存、网络、编程语言、架构等每个都有可能影响系统达到高性能一行不恰当的debug日志就可能将服务器的性能从TPS 30000降低到8000一个tcp_nodelay参数就可能将响应时间从2毫秒延长到40毫秒。因此要做到高性能计算是一件很复杂很有挑战的事情软件系统开发过程中的不同阶段都关系着高性能最终是否能够实现。
站在架构师的角度,当然需要特别关注高性能架构的设计。高性能架构设计主要集中在两方面:
尽量提升单服务器的性能,将单服务器的性能发挥到极致。
如果单服务器无法支撑性能,设计服务器集群方案。
除了以上两点,最终系统能否实现高性能,还和具体的实现及编码相关。但架构设计是高性能的基础,如果架构设计没有做到高性能,则后面的具体实现和编码能提升的空间是有限的。形象地说,架构设计决定了系统性能的上限,实现细节决定了系统性能的下限。
单服务器高性能的关键之一就是服务器采取的并发模型,并发模型有如下两个关键设计点:
服务器如何管理连接。
服务器如何处理请求。
以上两个设计点最终都和操作系统的I/O模型及进程模型相关。
I/O模型阻塞、非阻塞、同步、异步。
进程模型:单进程、多进程、多线程。
在下面详细介绍并发模型时会用到上面这些基础的知识点所以我建议你先检测一下对这些基础知识的掌握情况更多内容你可以参考《UNIX网络编程》三卷本。今天我们先来看看单服务器高性能模式PPC与TPC。
PPC
PPC是Process Per Connection的缩写其含义是指每次有新的连接就新建一个进程去专门处理这个连接的请求这是传统的UNIX网络服务器所采用的模型。基本的流程图是
父进程接受连接图中accept
父进程“fork”子进程图中fork
子进程处理连接的读写请求图中子进程read、业务处理、write
子进程关闭连接图中子进程中的close
注意图中有一个小细节父进程“fork”子进程后直接调用了close看起来好像是关闭了连接其实只是将连接的文件描述符引用计数减一真正的关闭连接是等子进程也调用close后连接对应的文件描述符引用计数变为0后操作系统才会真正关闭连接更多细节请参考《UNIX网络编程卷一》。
PPC模式实现简单比较适合服务器的连接数没那么多的情况例如数据库服务器。对于普通的业务服务器在互联网兴起之前由于服务器的访问量和并发量并没有那么大这种模式其实运作得也挺好世界上第一个web服务器CERN httpd就采用了这种模式具体你可以参考https://en.wikipedia.org/wiki/CERN_httpd。互联网兴起后服务器的并发和访问量从几十剧增到成千上万这种模式的弊端就凸显出来了主要体现在这几个方面
fork代价高站在操作系统的角度创建一个进程的代价是很高的需要分配很多内核资源需要将内存映像从父进程复制到子进程。即使现在的操作系统在复制内存映像时用到了Copy on Write写时复制技术总体来说创建进程的代价还是很大的。
父子进程通信复杂父进程“fork”子进程时文件描述符可以通过内存映像复制从父进程传到子进程但“fork”完成后父子进程通信就比较麻烦了需要采用IPCInterprocess Communication之类的进程通信方案。例如子进程需要在close之前告诉父进程自己处理了多少个请求以支撑父进程进行全局的统计那么子进程和父进程必须采用IPC方案来传递信息。
支持的并发连接数量有限如果每个连接存活时间比较长而且新的连接又源源不断的进来则进程数量会越来越多操作系统进程调度和切换的频率也越来越高系统的压力也会越来越大。因此一般情况下PPC方案能处理的并发连接数量最大也就几百。
prefork
PPC模式中当连接进来时才fork新进程来处理连接请求由于fork进程代价高用户访问时可能感觉比较慢prefork模式的出现就是为了解决这个问题。
顾名思义prefork就是提前创建进程pre-fork。系统在启动的时候就预先创建好进程然后才开始接受用户的请求当有新的连接进来的时候就可以省去fork进程的操作让用户访问更快、体验更好。prefork的基本示意图是
prefork的实现关键就是多个子进程都accept同一个socket当有新的连接进入时操作系统保证只有一个进程能最后accept成功。但这里也存在一个小小的问题“惊群”现象就是指虽然只有一个子进程能accept成功但所有阻塞在accept上的子进程都会被唤醒这样就导致了不必要的进程调度和上下文切换了。幸运的是操作系统可以解决这个问题例如Linux 2.6版本后内核已经解决了accept惊群问题。
prefork模式和PPC一样还是存在父子进程通信复杂、支持的并发连接数量有限的问题因此目前实际应用也不多。Apache服务器提供了MPM prefork模式推荐在需要可靠性或者与旧软件兼容的站点时采用这种模式默认情况下最大支持256个并发连接。
TPC
TPC是Thread Per Connection的缩写其含义是指每次有新的连接就新建一个线程去专门处理这个连接的请求。与进程相比线程更轻量级创建线程的消耗比进程要少得多同时多线程是共享进程内存空间的线程通信相比进程通信更简单。因此TPC实际上是解决或者弱化了PPC fork代价高的问题和父子进程通信复杂的问题。
TPC的基本流程是
父进程接受连接图中accept
父进程创建子线程图中pthread
子线程处理连接的读写请求图中子线程read、业务处理、write
子线程关闭连接图中子线程中的close
注意和PPC相比主进程不用“close”连接了。原因是在于子线程是共享主进程的进程空间的连接的文件描述符并没有被复制因此只需要一次close即可。
TPC虽然解决了fork代价高和进程通信复杂的问题但是也引入了新的问题具体表现在
创建线程虽然比创建进程代价低,但并不是没有代价,高并发时(例如每秒上万连接)还是有性能问题。
无须进程间通信,但是线程间的互斥和共享又引入了复杂度,可能一不小心就导致了死锁问题。
多线程会出现互相影响的情况,某个线程出现异常时,可能导致整个进程退出(例如内存越界)。
除了引入了新的问题TPC还是存在CPU线程调度和切换代价的问题。因此TPC方案本质上和PPC方案基本类似在并发几百连接的场景下反而更多地是采用PPC的方案因为PPC方案不会有死锁的风险也不会多进程互相影响稳定性更高。
prethread
TPC模式中当连接进来时才创建新的线程来处理连接请求虽然创建线程比创建进程要更加轻量级但还是有一定的代价而prethread模式就是为了解决这个问题。
和prefork类似prethread模式会预先创建线程然后才开始接受用户的请求当有新的连接进来的时候就可以省去创建线程的操作让用户感觉更快、体验更好。
由于多线程之间数据共享和通信比较方便因此实际上prethread的实现方式相比prefork要灵活一些常见的实现方式有下面几种
主进程accept然后将连接交给某个线程处理。
子线程都尝试去accept最终只有一个线程accept成功方案的基本示意图如下
Apache服务器的MPM worker模式本质上就是一种prethread方案但稍微做了改进。Apache服务器会首先创建多个进程每个进程里面再创建多个线程这样做主要是为了考虑稳定性即使某个子进程里面的某个线程异常导致整个子进程退出还会有其他子进程继续提供服务不会导致整个服务器全部挂掉。
prethread理论上可以比prefork支持更多的并发连接Apache服务器MPM worker模式默认支持16 × 25 = 400 个并发处理线程。
小结
今天我为你讲了传统的单服务器高性能模式PPC与TPC希望对你有所帮助。
这就是今天的全部内容,留一道思考题给你吧,什么样的系统比较适合本期所讲的高性能模式?原因是什么?

View File

@@ -0,0 +1,161 @@
因收到Google相关通知网站将会择期关闭。相关通知内容
19 单服务器高性能模式Reactor与Proactor
专栏上一期我介绍了单服务器高性能的PPC和TPC模式它们的优点是实现简单缺点是都无法支撑高并发的场景尤其是互联网发展到现在各种海量用户业务的出现PPC和TPC完全无能为力。今天我将介绍可以应对高并发场景的单服务器高性能架构模式Reactor和Proactor。
Reactor
PPC模式最主要的问题就是每个连接都要创建进程为了描述简洁这里只以PPC和进程为例实际上换成TPC和线程原理是一样的连接结束后进程就销毁了这样做其实是很大的浪费。为了解决这个问题一个自然而然的想法就是资源复用即不再单独为每个连接创建进程而是创建一个进程池将连接分配给进程一个进程可以处理多个连接的业务。
引入资源池的处理方式后会引出一个新的问题进程如何才能高效地处理多个连接的业务当一个连接一个进程时进程可以采用“read -> 业务处理 -> write”的处理流程如果当前连接没有数据可以读则进程就阻塞在read操作上。这种阻塞的方式在一个连接一个进程的场景下没有问题但如果一个进程处理多个连接进程阻塞在某个连接的read操作上此时即使其他连接有数据可读进程也无法去处理很显然这样是无法做到高性能的。
解决这个问题的最简单的方式是将read操作改为非阻塞然后进程不断地轮询多个连接。这种方式能够解决阻塞的问题但解决的方式并不优雅。首先轮询是要消耗CPU的其次如果一个进程处理几千上万的连接则轮询的效率是很低的。
为了能够更好地解决上述问题很容易可以想到只有当连接上有数据的时候进程才去处理这就是I/O多路复用技术的来源。
I/O多路复用技术归纳起来有两个关键实现点
当多条连接共用一个阻塞对象后进程只需要在一个阻塞对象上等待而无须再轮询所有连接常见的实现方式有select、epoll、kqueue等。
当某条连接有新的数据可以处理时,操作系统会通知进程,进程从阻塞状态返回,开始进行业务处理。
I/O多路复用结合线程池完美地解决了PPC和TPC的问题而且“大神们”给它取了一个很牛的名字Reactor中文是“反应堆”。联想到“核反应堆”听起来就很吓人实际上这里的“反应”不是聚变、裂变反应的意思而是“事件反应”的意思可以通俗地理解为“来了一个事件我就有相应的反应”这里的“我”就是Reactor具体的反应就是我们写的代码Reactor会根据事件类型来调用相应的代码进行处理。Reactor模式也叫Dispatcher模式在很多开源的系统里面会看到这个名称的类其实就是实现Reactor模式的更加贴近模式本身的含义即I/O多路复用统一监听事件收到事件后分配Dispatch给某个进程。
Reactor模式的核心组成部分包括Reactor和处理资源池进程池或线程池其中Reactor负责监听和分配事件处理资源池负责处理事件。初看Reactor的实现是比较简单的但实际上结合不同的业务场景Reactor模式的具体实现方案灵活多变主要体现在
Reactor的数量可以变化可以是一个Reactor也可以是多个Reactor。
资源池的数量可以变化:以进程为例,可以是单个进程,也可以是多个进程(线程类似)。
将上面两个因素排列组合一下理论上可以有4种选择但由于“多Reactor单进程”实现方案相比“单Reactor单进程”方案既复杂又没有性能优势因此“多Reactor单进程”方案仅仅是一个理论上的方案实际没有应用。
最终Reactor模式有这三种典型的实现方案
单Reactor单进程/线程。
单Reactor多线程。
多Reactor多进程/线程。
以上方案具体选择进程还是线程更多地是和编程语言及平台相关。例如Java语言一般使用线程例如NettyC语言使用进程和线程都可以。例如Nginx使用进程Memcache使用线程。
1.单Reactor单进程/线程
单Reactor单进程/线程的方案示意图如下(以进程为例):
注意select、accept、read、send是标准的网络编程APIdispatch和“业务处理”是需要完成的操作其他方案示意图类似。
详细说明一下这个方案:
Reactor对象通过select监控连接事件收到事件后通过dispatch进行分发。
如果是连接建立的事件则由Acceptor处理Acceptor通过accept接受连接并创建一个Handler来处理连接后续的各种事件。
如果不是连接建立事件则Reactor会调用连接对应的Handler第2步中创建的Handler来进行响应。
Handler会完成read->业务处理->send的完整业务流程。
单Reactor单进程的模式优点就是很简单没有进程间通信没有进程竞争全部都在同一个进程内完成。但其缺点也是非常明显具体表现有
只有一个进程无法发挥多核CPU的性能只能采取部署多个系统来利用多核CPU但这样会带来运维复杂度本来只要维护一个系统用这种方式需要在一台机器上维护多套系统。
Handler在处理某个连接上的业务时整个进程无法处理其他连接的事件很容易导致性能瓶颈。
因此单Reactor单进程的方案在实践中应用场景不多只适用于业务处理非常快速的场景目前比较著名的开源软件中使用单Reactor单进程的是Redis。
需要注意的是C语言编写系统的一般使用单Reactor单进程因为没有必要在进程中再创建线程而Java语言编写的一般使用单Reactor单线程因为Java虚拟机是一个进程虚拟机中有很多线程业务线程只是其中的一个线程而已。
2.单Reactor多线程
为了克服单Reactor单进程/线程方案的缺点,引入多进程/多线程是显而易见的这就产生了第2个方案单Reactor多线程。
单Reactor多线程方案示意图是
我来介绍一下这个方案:
主线程中Reactor对象通过select监控连接事件收到事件后通过dispatch进行分发。
如果是连接建立的事件则由Acceptor处理Acceptor通过accept接受连接并创建一个Handler来处理连接后续的各种事件。
如果不是连接建立事件则Reactor会调用连接对应的Handler第2步中创建的Handler来进行响应。
Handler只负责响应事件不进行业务处理Handler通过read读取到数据后会发给Processor进行业务处理。
Processor会在独立的子线程中完成真正的业务处理然后将响应结果发给主进程的Handler处理Handler收到响应后通过send将响应结果返回给client。
单Reator多线程方案能够充分利用多核多CPU的处理能力但同时也存在下面的问题
多线程数据共享和访问比较复杂。例如子线程完成业务处理后要把结果传递给主线程的Reactor进行发送这里涉及共享数据的互斥和保护机制。以Java的NIO为例Selector是线程安全的但是通过Selector.selectKeys()返回的键的集合是非线程安全的对selected keys的处理必须单线程处理或者采取同步措施进行保护。
Reactor承担所有事件的监听和响应只在主线程中运行瞬间高并发时会成为性能瓶颈。
你可能会发现我只列出了“单Reactor多线程”方案没有列出“单Reactor多进程”方案这是什么原因呢主要原因在于如果采用多进程子进程完成业务处理后将结果返回给父进程并通知父进程发送给哪个client这是很麻烦的事情。因为父进程只是通过Reactor监听各个连接上的事件然后进行分配子进程与父进程通信时并不是一个连接。如果要将父进程和子进程之间的通信模拟为一个连接并加入Reactor进行监听则是比较复杂的。而采用多线程时因为多线程是共享数据的因此线程间通信是非常方便的。虽然要额外考虑线程间共享数据时的同步问题但这个复杂度比进程间通信的复杂度要低很多。
3.多Reactor多进程/线程
为了解决单Reactor多线程的问题最直观的方法就是将单Reactor改为多Reactor这就产生了第3个方案多Reactor多进程/线程。
多Reactor多进程/线程方案示意图是(以进程为例):
方案详细说明如下:
父进程中mainReactor对象通过select监控连接建立事件收到事件后通过Acceptor接收将新的连接分配给某个子进程。
子进程的subReactor将mainReactor分配的连接加入连接队列进行监听并创建一个Handler用于处理连接的各种事件。
当有新的事件发生时subReactor会调用连接对应的Handler即第2步中创建的Handler来进行响应。
Handler完成read→业务处理→send的完整业务流程。
多Reactor多进程/线程的方案看起来比单Reactor多线程要复杂但实际实现时反而更加简单主要原因是
父进程和子进程的职责非常明确,父进程只负责接收新连接,子进程负责完成后续的业务处理。
父进程和子进程的交互很简单,父进程只需要把新连接传给子进程,子进程无须返回数据。
子进程之间是互相独立的无须同步共享之类的处理这里仅限于网络模型相关的select、read、send等无须同步共享“业务处理”还是有可能需要同步共享的
目前著名的开源系统Nginx采用的是多Reactor多进程采用多Reactor多线程的实现有Memcache和Netty。
我多说一句Nginx采用的是多Reactor多进程的模式但方案与标准的多Reactor多进程有差异。具体差异表现为主进程中仅仅创建了监听端口并没有创建mainReactor来“accept”连接而是由子进程的Reactor来“accept”连接通过锁来控制一次只有一个子进程进行“accept”子进程“accept”新连接后就放到自己的Reactor进行处理不会再分配给其他子进程更多细节请查阅相关资料或阅读Nginx源码。
Proactor
Reactor是非阻塞同步网络模型因为真正的read和send操作都需要用户进程同步操作。这里的“同步”指用户进程在执行read和send这类I/O操作的时候是同步的如果把I/O操作改为异步就能够进一步提升性能这就是异步网络模型Proactor。
Proactor中文翻译为“前摄器”比较难理解与其类似的单词是proactive含义为“主动的”因此我们照猫画虎翻译为“主动器”反而更好理解。Reactor可以理解为“来了事件我通知你你来处理”而Proactor可以理解为“来了事件我来处理处理完了我通知你”。这里的“我”就是操作系统内核“事件”就是有新连接、有数据可读、有数据可写的这些I/O事件“你”就是我们的程序代码。
Proactor模型示意图是
详细介绍一下Proactor方案
Proactor Initiator负责创建Proactor和Handler并将Proactor和Handler都通过Asynchronous Operation Processor注册到内核。
Asynchronous Operation Processor负责处理注册请求并完成I/O操作。
Asynchronous Operation Processor完成I/O操作后通知Proactor。
Proactor根据不同的事件类型回调不同的Handler进行业务处理。
Handler完成业务处理Handler也可以注册新的Handler到内核进程。
理论上Proactor比Reactor效率要高一些异步I/O能够充分利用DMA特性让I/O操作与计算重叠但要实现真正的异步I/O操作系统需要做大量的工作。目前Windows下通过IOCP实现了真正的异步I/O而在Linux系统下的AIO并不完善因此在Linux下实现高并发网络编程时都是以Reactor模式为主。所以即使Boost.Asio号称实现了Proactor模型其实它在Windows下采用IOCP而在Linux下是用Reactor模式采用epoll模拟出来的异步模型。
小结
今天我为你讲了单服务器支持高并发的高性能架构模式Reactor和Proactor希望对你有所帮助。
这就是今天的全部内容,留一道思考题给你吧,针对“前浪微博”消息队列架构的案例,你觉得采用何种并发模式是比较合适的,为什么?

View File

@@ -0,0 +1,118 @@
因收到Google相关通知网站将会择期关闭。相关通知内容
20 高性能负载均衡:分类及架构
单服务器无论如何优化,无论采用多好的硬件,总会有一个性能天花板,当单服务器的性能无法满足业务需求时,就需要设计高性能集群来提升系统整体的处理性能。
高性能集群的本质很简单,通过增加更多的服务器来提升系统整体的计算能力。由于计算本身存在一个特点:同样的输入数据和逻辑,无论在哪台服务器上执行,都应该得到相同的输出。因此高性能集群设计的复杂度主要体现在任务分配这部分,需要设计合理的任务分配策略,将计算任务分配到多台服务器上执行。
高性能集群的复杂性主要体现在需要增加一个任务分配器,以及为任务选择一个合适的任务分配算法。对于任务分配器,现在更流行的通用叫法是“负载均衡器”。但这个名称有一定的误导性,会让人潜意识里认为任务分配的目的是要保持各个计算单元的负载达到均衡状态。而实际上任务分配并不只是考虑计算单元的负载均衡,不同的任务分配算法目标是不一样的,有的基于负载考虑,有的基于性能(吞吐量、响应时间)考虑,有的基于业务考虑。考虑到“负载均衡”已经成为了事实上的标准术语,这里我也用“负载均衡”来代替“任务分配”,但请你时刻记住,负载均衡不只是为了计算单元的负载达到均衡状态。
今天我先来讲讲负载均衡的分类及架构,下一期会讲负载均衡的算法。
负载均衡分类
常见的负载均衡系统包括3种DNS负载均衡、硬件负载均衡和软件负载均衡。
DNS负载均衡
DNS是最简单也是最常见的负载均衡方式一般用来实现地理级别的均衡。例如北方的用户访问北京的机房南方的用户访问深圳的机房。DNS负载均衡的本质是DNS解析同一个域名可以返回不同的IP地址。例如同样是www.baidu.com北方用户解析后获取的地址是61.135.165.224这是北京机房的IP南方用户解析后获取的地址是14.215.177.38这是深圳机房的IP
下面是DNS负载均衡的简单示意图
DNS负载均衡实现简单、成本低但也存在粒度太粗、负载均衡算法少等缺点。仔细分析一下优缺点其优点有
简单、成本低负载均衡工作交给DNS服务器处理无须自己开发或者维护负载均衡设备。
就近访问提升访问速度DNS解析时可以根据请求来源IP解析成距离用户最近的服务器地址可以加快访问速度改善性能。
缺点有:
更新不及时DNS缓存的时间比较长修改DNS配置后由于缓存的原因还是有很多用户会继续访问修改前的IP这样的访问会失败达不到负载均衡的目的并且也影响用户正常使用业务。
扩展性差DNS负载均衡的控制权在域名商那里无法根据业务特点针对其做更多的定制化功能和扩展特性。
分配策略比较简单DNS负载均衡支持的算法少不能区分服务器的差异不能根据系统与服务的状态来判断负载也无法感知后端服务器的状态。
针对DNS负载均衡的一些缺点对于时延和故障敏感的业务有一些公司自己实现了HTTP-DNS的功能即使用HTTP协议实现一个私有的DNS系统。这样的方案和通用的DNS优缺点正好相反。
硬件负载均衡
硬件负载均衡是通过单独的硬件设备来实现负载均衡功能这类设备和路由器、交换机类似可以理解为一个用于负载均衡的基础网络设备。目前业界典型的硬件负载均衡设备有两款F5和A10。这类设备性能强劲、功能强大但价格都不便宜一般只有“土豪”公司才会考虑使用此类设备。普通业务量级的公司一是负担不起二是业务量没那么大用这些设备也是浪费。
硬件负载均衡的优点是:
功能强大:全面支持各层级的负载均衡,支持全面的负载均衡算法,支持全局负载均衡。
性能强大对比一下软件负载均衡支持到10万级并发已经很厉害了硬件负载均衡可以支持100万以上的并发。
稳定性高:商用硬件负载均衡,经过了良好的严格测试,经过大规模使用,稳定性高。
支持安全防护硬件均衡设备除具备负载均衡功能外还具备防火墙、防DDoS攻击等安全功能。
硬件负载均衡的缺点是:
价格昂贵最普通的一台F5就是一台“马6”好一点的就是“Q7”了。
扩展能力差:硬件设备,可以根据业务进行配置,但无法进行扩展和定制。
软件负载均衡
软件负载均衡通过负载均衡软件来实现负载均衡功能常见的有Nginx和LVS其中Nginx是软件的7层负载均衡LVS是Linux内核的4层负载均衡。4层和7层的区别就在于协议和灵活性Nginx支持HTTP、E-mail协议而LVS是4层负载均衡和协议无关几乎所有应用都可以做例如聊天、数据库等。
软件和硬件的最主要区别就在于性能硬件负载均衡性能远远高于软件负载均衡性能。Nginx的性能是万级一般的Linux服务器上装一个Nginx大概能到5万/秒LVS的性能是十万级据说可达到80万/秒而F5性能是百万级从200万/秒到800万/秒都有数据来源网络仅供参考如需采用请根据实际业务场景进行性能测试。当然软件负载均衡的最大优势是便宜一台普通的Linux服务器批发价大概就是1万元左右相比F5的价格那就是自行车和宝马的区别了。
除了使用开源的系统进行负载均衡如果业务比较特殊也可能基于开源系统进行定制例如Nginx插件甚至进行自研。
下面是Nginx的负载均衡架构示意图
软件负载均衡的优点:
简单:无论是部署还是维护都比较简单。
便宜只要买个Linux服务器装上软件即可。
灵活4层和7层负载均衡可以根据业务进行选择也可以根据业务进行比较方便的扩展例如可以通过Nginx的插件来实现业务的定制化功能。
其实下面的缺点都是和硬件负载均衡相比的,并不是说软件负载均衡没法用。
性能一般一个Nginx大约能支撑5万并发。
功能没有硬件负载均衡那么强大。
一般不具备防火墙和防DDoS攻击等安全功能。
负载均衡典型架构
前面我们介绍了3种常见的负载均衡机制DNS负载均衡、硬件负载均衡、软件负载均衡每种方式都有一些优缺点但并不意味着在实际应用中只能基于它们的优缺点进行非此即彼的选择反而是基于它们的优缺点进行组合使用。具体来说组合的基本原则为DNS负载均衡用于实现地理级别的负载均衡硬件负载均衡用于实现集群级别的负载均衡软件负载均衡用于实现机器级别的负载均衡。
我以一个假想的实例来说明一下这种组合方式,如下图所示。
整个系统的负载均衡分为三层。
地理级别负载均衡www.xxx.com部署在北京、广州、上海三个机房当用户访问时DNS会根据用户的地理位置来决定返回哪个机房的IP图中返回了广州机房的IP地址这样用户就访问到广州机房了。
集群级别负载均衡广州机房的负载均衡用的是F5设备F5收到用户请求后进行集群级别的负载均衡将用户请求发给3个本地集群中的一个我们假设F5将用户请求发给了“广州集群2”。
机器级别的负载均衡广州集群2的负载均衡用的是NginxNginx收到用户请求后将用户请求发送给集群里面的某台服务器服务器处理用户的业务请求并返回业务响应。
需要注意的是上图只是一个示例一般在大型业务场景下才会这样用如果业务量没这么大则没有必要严格照搬这套架构。例如一个大学的论坛完全可以不需要DNS负载均衡也不需要F5设备只需要用Nginx作为一个简单的负载均衡就足够了。
小结
今天我为你讲了负载均衡的常见分类以及典型架构,希望对你有所帮助。
这就是今天的全部内容留一道思考题给你吧假设你来设计一个日活跃用户1000万的论坛的负载均衡集群你的方案是什么设计理由是什么

View File

@@ -0,0 +1,96 @@
因收到Google相关通知网站将会择期关闭。相关通知内容
21 高性能负载均衡:算法
负载均衡算法数量较多,而且可以根据一些业务特性进行定制开发,抛开细节上的差异,根据算法期望达到的目的,大体上可以分为下面几类。
任务平分类:负载均衡系统将收到的任务平均分配给服务器进行处理,这里的“平均”可以是绝对数量的平均,也可以是比例或者权重上的平均。
负载均衡类负载均衡系统根据服务器的负载来进行分配这里的负载并不一定是通常意义上我们说的“CPU负载”而是系统当前的压力可以用CPU负载来衡量也可以用连接数、I/O使用率、网卡吞吐量等来衡量系统的压力。
性能最优类:负载均衡系统根据服务器的响应时间来进行任务分配,优先将新任务分配给响应最快的服务器。
Hash类负载均衡系统根据任务中的某些关键信息进行Hash运算将相同Hash值的请求分配到同一台服务器上。常见的有源地址Hash、目标地址Hash、session id hash、用户ID Hash等。
接下来我介绍一下负载均衡算法以及它们的优缺点。
轮询
负载均衡系统收到请求后,按照顺序轮流分配到服务器上。
轮询是最简单的一个策略,无须关注服务器本身的状态,例如:
某个服务器当前因为触发了程序bug进入了死循环导致CPU负载很高负载均衡系统是不感知的还是会继续将请求源源不断地发送给它。
集群中有新的机器是32核的老的机器是16核的负载均衡系统也是不关注的新老机器分配的任务数是一样的。
需要注意的是负载均衡系统无须关注“服务器本身状态”,这里的关键词是“本身”。也就是说,只要服务器在运行,运行状态是不关注的。但如果服务器直接宕机了,或者服务器和负载均衡系统断连了,这时负载均衡系统是能够感知的,也需要做出相应的处理。例如,将服务器从可分配服务器列表中删除,否则就会出现服务器都宕机了,任务还不断地分配给它,这明显是不合理的。
总而言之,“简单”是轮询算法的优点,也是它的缺点。
加权轮询
负载均衡系统根据服务器权重进行任务分配,这里的权重一般是根据硬件配置进行静态配置的,采用动态的方式计算会更加契合业务,但复杂度也会更高。
加权轮询是轮询的一种特殊形式其主要目的就是为了解决不同服务器处理能力有差异的问题。例如集群中有新的机器是32核的老的机器是16核的那么理论上我们可以假设新机器的处理能力是老机器的2倍负载均衡系统就可以按照2:1的比例分配更多的任务给新机器从而充分利用新机器的性能。
加权轮询解决了轮询算法中无法根据服务器的配置差异进行任务分配的问题,但同样存在无法根据服务器的状态差异进行任务分配的问题。
负载最低优先
负载均衡系统将任务分配给当前负载最低的服务器,这里的负载根据不同的任务类型和业务场景,可以用不同的指标来衡量。例如:
LVS这种4层网络负载均衡设备可以以“连接数”来判断服务器的状态服务器连接数越大表明服务器压力越大。
Nginx这种7层网络负载系统可以以“HTTP请求数”来判断服务器状态Nginx内置的负载均衡算法不支持这种方式需要进行扩展
如果我们自己开发负载均衡系统可以根据业务特点来选择指标衡量系统压力。如果是CPU密集型可以以“CPU负载”来衡量系统压力如果是I/O密集型可以以“I/O负载”来衡量系统压力。
负载最低优先的算法解决了轮询算法中无法感知服务器状态的问题,由此带来的代价是复杂度要增加很多。例如:
最少连接数优先的算法要求负载均衡系统统计每个服务器当前建立的连接其应用场景仅限于负载均衡接收的任何连接请求都会转发给服务器进行处理否则如果负载均衡系统和服务器之间是固定的连接池方式就不适合采取这种算法。例如LVS可以采取这种算法进行负载均衡而一个通过连接池的方式连接MySQL集群的负载均衡系统就不适合采取这种算法进行负载均衡。
CPU负载最低优先的算法要求负载均衡系统以某种方式收集每个服务器的CPU负载而且要确定是以1分钟的负载为标准还是以15分钟的负载为标准不存在1分钟肯定比15分钟要好或者差。不同业务最优的时间间隔是不一样的时间间隔太短容易造成频繁波动时间间隔太长又可能造成峰值来临时响应缓慢。
负载最低优先算法基本上能够比较完美地解决轮询算法的缺点因为采用这种算法后负载均衡系统需要感知服务器当前的运行状态。当然其代价是复杂度大幅上升。通俗来讲轮询可能是5行代码就能实现的算法而负载最低优先算法可能要1000行才能实现甚至需要负载均衡系统和服务器都要开发代码。负载最低优先算法如果本身没有设计好或者不适合业务的运行特点算法本身就可能成为性能的瓶颈或者引发很多莫名其妙的问题。所以负载最低优先算法虽然效果看起来很美好但实际上真正应用的场景反而没有轮询包括加权轮询那么多。
性能最优类
负载最低优先类算法是站在服务器的角度来进行分配的,而性能最优优先类算法则是站在客户端的角度来进行分配的,优先将任务分配给处理速度最快的服务器,通过这种方式达到最快响应客户端的目的。
和负载最低优先类算法类似,性能最优优先类算法本质上也是感知了服务器的状态,只是通过响应时间这个外部标准来衡量服务器状态而已。因此性能最优优先类算法存在的问题和负载最低优先类算法类似,复杂度都很高,主要体现在:
负载均衡系统需要收集和分析每个服务器每个任务的响应时间,在大量任务处理的场景下,这种收集和统计本身也会消耗较多的性能。
为了减少这种统计上的消耗,可以采取采样的方式来统计,即不统计所有任务的响应时间,而是抽样统计部分任务的响应时间来估算整体任务的响应时间。采样统计虽然能够减少性能消耗,但使得复杂度进一步上升,因为要确定合适的采样率,采样率太低会导致结果不准确,采样率太高会导致性能消耗较大,找到合适的采样率也是一件复杂的事情。
无论是全部统计还是采样统计都需要选择合适的周期是10秒内性能最优还是1分钟内性能最优还是5分钟内性能最优……没有放之四海而皆准的周期需要根据实际业务进行判断和选择这也是一件比较复杂的事情甚至出现系统上线后需要不断地调优才能达到最优设计。
Hash类
负载均衡系统根据任务中的某些关键信息进行Hash运算将相同Hash值的请求分配到同一台服务器上这样做的目的主要是为了满足特定的业务需求。例如
源地址Hash
将来源于同一个源IP地址的任务分配给同一个服务器进行处理适合于存在事务、会话的业务。例如当我们通过浏览器登录网上银行时会生成一个会话信息这个会话是临时的关闭浏览器后就失效。网上银行后台无须持久化会话信息只需要在某台服务器上临时保存这个会话就可以了但需要保证用户在会话存在期间每次都能访问到同一个服务器这种业务场景就可以用源地址Hash来实现。
ID Hash
将某个ID标识的业务分配到同一个服务器中进行处理这里的ID一般是临时性数据的ID如session id。例如上述的网上银行登录的例子用session id hash同样可以实现同一个会话期间用户每次都是访问到同一台服务器的目的。
小结
今天我为你讲了常见负载均衡算法的优缺点和应用场景,希望对你有所帮助。
这就是今天的全部内容,留一道思考题给你吧,微信抢红包的高并发架构,应该采取什么样的负载均衡算法?谈谈你的分析和理解。

View File

@@ -0,0 +1,210 @@
因收到Google相关通知网站将会择期关闭。相关通知内容
22 想成为架构师你必须知道CAP理论
CAP定理CAP theorem又被称作布鲁尔定理Brewers theorem是加州大学伯克利分校的计算机科学家埃里克·布鲁尔Eric Brewer在2000年的ACM PODC上提出的一个猜想。2002年麻省理工学院的赛斯·吉尔伯特Seth Gilbert和南希·林奇Nancy Lynch发表了布鲁尔猜想的证明使之成为分布式计算领域公认的一个定理。对于设计分布式系统的架构师来说CAP是必须掌握的理论。
布鲁尔在提出CAP猜想的时候并没有详细定义Consistency、Availability、Partition Tolerance三个单词的明确定义因此如果初学者去查询CAP定义的时候会感到比较困惑因为不同的资料对CAP的详细定义有一些细微的差别例如
Consistency: where all nodes see the same data at the same time.
Availability: which guarantees that every request receives a response about whether it succeeded or failed.
Partition tolerance: where the system continues to operate even if any one part of the system is lost or fails.
(https://console.bluemix.net/docs/services/Cloudant/guides/cap_theorem.html#cap-)
Consistency: Every read receives the most recent write or an error.
Availability: Every request receives a (non-error) response without guarantee that it contains the most recent write.
Partition tolerance: The system continues to operate despite an arbitrary number of messages being dropped (or delayed) by the network between nodes.
(https://en.wikipedia.org/wiki/CAP_theorem#cite_note-Brewer2012-6)
Consistency: all nodes have access to the same data simultaneously.
Availability: a promise that every request receives a response, at minimum whether the request succeeded or failed.
Partition tolerance: the system will continue to work even if some arbitrary node goes offline or cant communicate.
(https://www.teamsilverback.com/understanding-the-cap-theorem/)
为了更好地解释CAP理论我挑选了Robert Greinerhttp://robertgreiner.com/about/的文章作为参考基础。有趣的是Robert Greiner对CAP的理解也经历了一个过程他写了两篇文章来阐述CAP理论第一篇被标记为“outdated”有一些中文翻译文章正好参考了第一篇我将对比前后两篇解释的差异点通过对比帮助你更加深入地理解CAP理论。
CAP理论
第一版解释:
Any distributed system cannot guaranty C, A, and P simultaneously.
http://robertgreiner.com/2014/06/cap-theorem-explained/
简单翻译为对于一个分布式计算系统不可能同时满足一致性Consistence、可用性Availability、分区容错性Partition Tolerance三个设计约束。
第二版解释:
In a distributed system (a collection of interconnected nodes that share data.), you can only have two out of the following three guarantees across a write/read pair: Consistency, Availability, and Partition Tolerance - one of them must be sacrificed.
http://robertgreiner.com/2014/08/cap-theorem-revisited/
简单翻译为在一个分布式系统指互相连接并共享数据的节点的集合当涉及读写操作时只能保证一致性Consistence、可用性Availability、分区容错性Partition Tolerance三者中的两个另外一个必须被牺牲。
对比两个版本的定义,有几个很关键的差异点:
第二版定义了什么才是CAP理论探讨的分布式系统强调了两点interconnected和share data为何要强调这两点呢 因为分布式系统并不一定会互联和共享数据。最简单的例如Memcache的集群相互之间就没有连接和共享数据因此Memcache集群这类分布式系统就不符合CAP理论探讨的对象而MySQL集群就是互联和进行数据复制的因此是CAP理论探讨的对象。
第二版强调了write/read pair这点其实是和上一个差异点一脉相承的。也就是说CAP关注的是对数据的读写操作而不是分布式系统的所有功能。例如ZooKeeper的选举机制就不是CAP探讨的对象。
相比来说,第二版的定义更加精确。
虽然第二版的定义和解释更加严谨但内容相比第一版来说更加难记一些所以现在大部分技术人员谈论CAP理论时更多还是按照第一版的定义和解释来说的因为第一版虽然不严谨但非常简单和容易记住。
第二版除了基本概念,三个基本的设计约束也进行了重新阐述,我来详细分析一下。
1.一致性Consistency
第一版解释:
All nodes see the same data at the same time.
简单翻译为:所有节点在同一时刻都能看到相同的数据。
第二版解释:
A read is guaranteed to return the most recent write for a given client.
简单翻译为:对某个指定的客户端来说,读操作保证能够返回最新的写操作结果。
第一版解释和第二版解释的主要差异点表现在:
第一版从节点node的角度描述第二版从客户端client的角度描述。
相比来说,第二版更加符合我们观察和评估系统的方式,即站在客户端的角度来观察系统的行为和特征。
第一版的关键词是see第二版的关键词是read。
第一版解释中的see其实并不确切因为节点node是拥有数据而不是看到数据即使要描述也是用have第二版从客户端client的读写角度来描述一致性定义更加精确。
第一版强调同一时刻拥有相同数据same time + same data第二版并没有强调这点。
这就意味着实际上对于节点来说可能同一时刻拥有不同数据same time + different data这和我们通常理解的一致性是有差异的为何做这样的改动呢其实在第一版的详细解释中已经提到了具体内容如下
A system has consistency if a transaction starts with the system in a consistent state, and ends with the system in a consistent state. In this model, a system can (and does) shift into an inconsistent state during a transaction, but the entire transaction gets rolled back if there is an error during any stage in the process.
参考上述的解释对于系统执行事务来说在事务执行过程中系统其实处于一个不一致的状态不同的节点的数据并不完全一致因此第一版的解释“All nodes see the same data at the same time”是不严谨的。而第二版强调client读操作能够获取最新的写结果就没有问题因为事务在执行过程中client是无法读取到未提交的数据的只有等到事务提交后client才能读取到事务写入的数据而如果事务失败则会进行回滚client也不会读取到事务中间写入的数据。
2.可用性Availability
第一版解释:
Every request gets a response on success/failure.
简单翻译为:每个请求都能得到成功或者失败的响应。
第二版解释:
A non-failing node will return a reasonable response within a reasonable amount of time (no error or timeout).
简单翻译为:非故障的节点在合理的时间内返回合理的响应(不是错误和超时的响应)。
第一版解释和第二版解释主要差异点表现在:
第一版是every request第二版强调了A non-failing node。
第一版的every request是不严谨的因为只有非故障节点才能满足可用性要求如果节点本身就故障了发给节点的请求不一定能得到一个响应。
第一版的response分为success和failure第二版用了两个reasonablereasonable response 和reasonable time而且特别强调了no error or timeout。
第一版的success/failure的定义太泛了几乎任何情况无论是否符合CAP理论我们都可以说请求成功和失败因为超时也算失败、错误也算失败、异常也算失败、结果不正确也算失败即使是成功的响应也不一定是正确的。例如本来应该返回100但实际上返回了90这就是成功的响应但并没有得到正确的结果。相比之下第二版的解释明确了不能超时、不能出错结果是合理的注意没有说“正确”的结果。例如应该返回100但实际上返回了90肯定是不正确的结果但可以是一个合理的结果。
3.分区容忍性Partition Tolerance
第一版解释:
System continues to work despite message loss or partial failure.
简单翻译为:出现消息丢失或者分区错误时系统能够继续运行。
第二版解释:
The system will continue to function when network partitions occur.
简单翻译为:当出现网络分区后,系统能够继续“履行职责”。
第一版解释和第二版解释主要差异点表现在:
第一版用的是work第二版用的是function。
work强调“运行”只要系统不宕机我们都可以说系统在work返回错误也是work拒绝服务也是work而function强调“发挥作用”“履行职责”这点和可用性是一脉相承的。也就是说只有返回reasonable response才是function。相比之下第二版解释更加明确。
第一版描述分区用的是message loss or partial failure第二版直接用network partitions。
对比两版解释第一版是直接说原因即message loss造成了分区但message loss的定义有点狭隘因为通常我们说的message loss丢包只是网络故障中的一种第二版直接说现象即发生了分区现象不管是什么原因可能是丢包也可能是连接中断还可能是拥塞只要导致了网络分区就通通算在里面。
CAP应用
虽然CAP理论定义是三个要素中只能取两个但放到分布式环境下来思考我们会发现必须选择P分区容忍要素因为网络本身无法做到100%可靠有可能出故障所以分区是一个必然的现象。如果我们选择了CA而放弃了P那么当发生分区现象时为了保证C系统需要禁止写入当有写入请求时系统返回error例如当前系统不允许写入这又和A冲突了因为A要求返回no error和no timeout。因此分布式系统理论上不可能选择CA架构只能选择CP或者AP架构。
1.CP - Consistency/Partition Tolerance
如下图所示为了保证一致性当发生分区现象后N1节点上的数据已经更新到y但由于N1和N2之间的复制通道中断数据y无法同步到N2N2节点上的数据还是x。这时客户端C访问N2时N2需要返回Error提示客户端C“系统现在发生了错误”这种处理方式违背了可用性Availability的要求因此CAP三者只能满足CP。

2.AP - Availability/Partition Tolerance
如下图所示为了保证可用性当发生分区现象后N1节点上的数据已经更新到y但由于N1和N2之间的复制通道中断数据y无法同步到N2N2节点上的数据还是x。这时客户端C访问N2时N2将当前自己拥有的数据x返回给客户端C了而实际上当前最新的数据已经是y了这就不满足一致性Consistency的要求了因此CAP三者只能满足AP。注意这里N2节点返回x虽然不是一个“正确”的结果但是一个“合理”的结果因为x是旧的数据并不是一个错乱的值只是不是最新的数据而已。

小结
今天我为你讲了CAP理论通过对比两个不同版本的CAP理论解释详细地分析了CAP理论的准确定义希望对你有所帮助。
这就是今天的全部内容留一道思考题给你吧基于Paxos算法构建的分布式系统属于CAP架构中的哪一种谈谈你的分析和理解。

View File

@@ -0,0 +1,147 @@
因收到Google相关通知网站将会择期关闭。相关通知内容
23 想成为架构师你必须掌握的CAP细节
理论的优点在于清晰简洁、易于理解但缺点就是高度抽象化省略了很多细节导致在将理论应用到实践时由于各种复杂情况可能出现误解和偏差CAP理论也不例外。如果我们没有意识到这些关键的细节点那么在实践中应用CAP理论时就可能发现方案很难落地。
而且当谈到数据一致性时CAP、ACID、BASE难免会被我们拿出来讨论原因在于这三者都是和数据一致性相关的理论如果不仔细理解三者之间的差别则可能会陷入一头雾水的状态不知道应该用哪个才好。
今天我来讲讲CAP的具体细节简单对比一下ACID、BASE几个概念的关键区别点。
CAP关键细节点
埃里克·布鲁尔Eric Brewer在《CAP理论十二年回顾“规则”变了》http://www.infoq.com/cn/articles/cap-twelve-years-later-how-the-rules-have-changed一文中详细地阐述了理解和应用CAP的一些细节点可能是由于作者写作风格的原因对于一些非常关键的细节点一句话就带过了这里我特别提炼出来重点阐述。
CAP关注的粒度是数据而不是整个系统。
原文就只有一句话:
C与A之间的取舍可以在同一系统内以非常细小的粒度反复发生而每一次的决策可能因为具体的操作乃至因为牵涉到特定的数据或用户而有所不同。
但这句话是理解和应用CAP理论非常关键的一点。CAP理论的定义和解释中用的都是system、node这类系统级的概念这就给很多人造成了很大的误导认为我们在进行架构设计时整个系统要么选择CP要么选择AP。但在实际设计过程中每个系统不可能只处理一种数据而是包含多种类型的数据有的数据必须选择CP有的数据必须选择AP。而如果我们做设计时从整个系统的角度去选择CP还是AP就会发现顾此失彼无论怎么做都是有问题的。
以一个最简单的用户管理系统为例用户管理系统包含用户账号数据用户ID、密码、用户信息数据昵称、兴趣、爱好、性别、自我介绍等。通常情况下用户账号数据会选择CP而用户信息数据会选择AP如果限定整个系统为CP则不符合用户信息数据的应用场景如果限定整个系统为AP则又不符合用户账号数据的应用场景。
所以在CAP理论落地实践时我们需要将系统内的数据按照不同的应用场景和要求进行分类每类数据选择不同的策略CP还是AP而不是直接限定整个系统所有数据都是同一策略。
CAP是忽略网络延迟的。
这是一个非常隐含的假设布鲁尔在定义一致性时并没有将延迟考虑进去。也就是说当事务提交时数据能够瞬间复制到所有节点。但实际情况下从节点A复制数据到节点B总是需要花费一定时间的。如果是相同机房耗费时间可能是几毫秒如果是跨地域的机房例如北京机房同步到广州机房耗费的时间就可能是几十毫秒。这就意味着CAP理论中的C在实践中是不可能完美实现的在数据复制的过程中节点A和节点B的数据并不一致。
不要小看了这几毫秒或者几十毫秒的不一致对于某些严苛的业务场景例如和金钱相关的用户余额或者和抢购相关的商品库存技术上是无法做到分布式场景下完美的一致性的。而业务上必须要求一致性因此单个用户的余额、单个商品的库存理论上要求选择CP而实际上CP都做不到只能选择CA。也就是说只能单点写入其他节点做备份无法做到分布式情况下多点写入。
需要注意的是,这并不意味着这类系统无法应用分布式架构,只是说“单个用户余额、单个商品库存”无法做分布式,但系统整体还是可以应用分布式架构的。例如,下面的架构图是常见的将用户分区的分布式架构。
我们可以将用户id为0 ~ 100的数据存储在Node 1将用户id为101 ~ 200的数据存储在Node 2Client根据用户id来决定访问哪个Node。对于单个用户来说读写操作都只能在某个节点上进行对所有用户来说有一部分用户的读写操作在Node 1上有一部分用户的读写操作在Node 2上。
这样的设计有一个很明显的问题就是某个节点故障时这个节点上的用户就无法进行读写操作了但站在整体上来看这种设计可以降低节点故障时受影响的用户的数量和范围毕竟只影响20%的用户肯定要比影响所有用户要好。这也是为什么挖掘机挖断光缆后,支付宝只有一部分用户会出现业务异常,而不是所有用户业务异常的原因。
正常运行情况下不存在CP和AP的选择可以同时满足CA。
CAP理论告诉我们分布式系统只能选择CP或者AP但其实这里的前提是系统发生了“分区”现象。如果系统没有发生分区现象也就是说P不存在的时候节点间的网络连接一切正常我们没有必要放弃C或者A应该C和A都可以保证这就要求架构设计的时候既要考虑分区发生时选择CP还是AP也要考虑分区没有发生时如何保证CA。
同样以用户管理系统为例即使是实现CA不同的数据实现方式也可能不一样用户账号数据可以采用“消息队列”的方式来实现CA因为消息队列可以比较好地控制实时性但实现起来就复杂一些而用户信息数据可以采用“数据库同步”的方式来实现CA因为数据库的方式虽然在某些场景下可能延迟较高但使用起来简单。
放弃并不等于什么都不做,需要为分区恢复后做准备。
CAP理论告诉我们三者只能取两个需要“牺牲”sacrificed另外一个这里的“牺牲”是有一定误导作用的因为“牺牲”让很多人理解成什么都不做。实际上CAP理论的“牺牲”只是说在分区过程中我们无法保证C或者A但并不意味着什么都不做。因为在系统整个运行周期中大部分时间都是正常的发生分区现象的时间并不长。例如99.99%可用性俗称4个9的系统一年运行下来不可用的时间只有50分钟99.999%俗称5个9可用性的系统一年运行下来不可用的时间只有5分钟。分区期间放弃C或者A并不意味着永远放弃C和A我们可以在分区期间进行一些操作从而让分区故障解决后系统能够重新达到CA的状态。
最典型的就是在分区期间记录一些日志当分区故障解决后系统根据日志进行数据恢复使得重新达到CA状态。同样以用户管理系统为例对于用户账号数据假设我们选择了CP则分区发生后节点1可以继续注册新用户节点2无法注册新用户这里就是不符合A的原因因为节点2收到注册请求后会返回error此时节点1可以将新注册但未同步到节点2的用户记录到日志中。当分区恢复后节点1读取日志中的记录同步给节点2当同步完成后节点1和节点2就达到了同时满足CA的状态。
而对于用户信息数据假设我们选择了AP则分区发生后节点1和节点2都可以修改用户信息但两边可能修改不一样。例如用户在节点1中将爱好改为“旅游、美食、跑步”然后用户在节点2中将爱好改为“美食、游戏”节点1和节点2都记录了未同步的爱好数据当分区恢复后系统按照某个规则来合并数据。例如按照“最后修改优先规则”将用户爱好修改为“美食、游戏”按照“字数最多优先规则”则将用户爱好修改为“旅游美食、跑步”也可以完全将数据冲突报告出来由人工来选择具体应该采用哪一条。
ACID
ACID是数据库管理系统为了保证事务的正确性而提出来的一个理论ACID包含四个约束下面我来解释一下。
Atomicity原子性
一个事务中的所有操作,要么全部完成,要么全部不完成,不会在中间某个环节结束。事务在执行过程中发生错误,会被回滚到事务开始前的状态,就像这个事务从来没有执行过一样。
Consistency一致性
在事务开始之前和事务结束以后,数据库的完整性没有被破坏。
Isolation隔离性
数据库允许多个并发事务同时对数据进行读写和修改的能力。隔离性可以防止多个事务并发执行时由于交叉执行而导致数据的不一致。事务隔离分为不同级别包括读未提交Read uncommitted、读提交read committed、可重复读repeatable read和串行化Serializable
Durability持久性
事务处理结束后,对数据的修改就是永久的,即便系统故障也不会丢失。
可以看到ACID中的AAtomicity和CAP中的AAvailability意义完全不同而ACID中的C和CAP中的C名称虽然都是一致性但含义也完全不一样。ACID中的C是指数据库的数据完整性而CAP中的C是指分布式节点中的数据一致性。再结合ACID的应用场景是数据库事务CAP关注的是分布式系统数据读写这个差异点来看其实CAP和ACID的对比就类似关公战秦琼虽然关公和秦琼都是武将但其实没有太多可比性。
BASE
BASE是指基本可用Basically Available、软状态 Soft State、最终一致性 Eventual Consistency核心思想是即使无法做到强一致性CAP的一致性就是强一致性但应用可以采用适合的方式达到最终一致性。
基本可用Basically Available
分布式系统在出现故障时,允许损失部分可用性,即保证核心可用。
这里的关键词是“部分”和“核心”,具体选择哪些作为可以损失的业务,哪些是必须保证的业务,是一项有挑战的工作。例如,对于一个用户管理系统来说,“登录”是核心功能,而“注册”可以算作非核心功能。因为未注册的用户本来就还没有使用系统的业务,注册不了最多就是流失一部分用户,而且这部分用户数量较少。如果用户已经注册但无法登录,那就意味用户无法使用系统。例如,充了钱的游戏不能玩了、云存储不能用了……这些会对用户造成较大损失,而且登录用户数量远远大于新注册用户,影响范围更大。
软状态Soft State
允许系统存在中间状态而该中间状态不会影响系统整体可用性。这里的中间状态就是CAP理论中的数据不一致。
最终一致性Eventual Consistency
系统中的所有数据副本经过一定时间后,最终能够达到一致的状态。
这里的关键词是“一定时间” 和 “最终”“一定时间”和数据的特性是强关联的不同的数据能够容忍的不一致时间是不同的。举一个微博系统的例子用户账号数据最好能在1分钟内就达到一致状态因为用户在A节点注册或者登录后1分钟内不太可能立刻切换到另外一个节点但10分钟后可能就重新登录到另外一个节点了而用户发布的最新微博可以容忍30分钟内达到一致状态因为对于用户来说看不到某个明星发布的最新微博用户是无感知的会认为明星没有发布微博。“最终”的含义就是不管多长时间最终还是要达到一致性的状态。
BASE理论本质上是对CAP的延伸和补充更具体地说是对CAP中AP方案的一个补充。前面在剖析CAP理论时提到了其实和BASE相关的两点
CAP理论是忽略延时的而实际应用中延时是无法避免的。
这一点就意味着完美的CP场景是不存在的即使是几毫秒的数据复制延迟在这几毫秒时间间隔内系统是不符合CP要求的。因此CAP中的CP方案实际上也是实现了最终一致性只是“一定时间”是指几毫秒而已。
AP方案中牺牲一致性只是指分区期间而不是永远放弃一致性。
这一点其实就是BASE理论延伸的地方分区期间牺牲一致性但分区故障恢复后系统应该达到最终一致性。
综合上面的分析ACID是数据库事务完整性的理论CAP是分布式系统设计理论BASE是CAP理论中AP方案的延伸。
小结
今天我为你讲了深入理解CAP理论所需要特别关注的细节点以及ACID和BASE两个相似的术语这些技术细节在架构设计中非常关键希望对你有所帮助。
这就是今天的全部内容留一道思考题给你吧假如你来设计电商网站的高可用系统按照CAP理论的要求你会如何设计

View File

@@ -0,0 +1,198 @@
因收到Google相关通知网站将会择期关闭。相关通知内容
24 FMEA方法排除架构可用性隐患的利器
我在前面的专栏分析高可用复杂度的时候提出了一个问题:高可用和高性能哪个更复杂,大部分同学都分析出了正确的答案:高可用更复杂一些,主要原因在于异常的场景很多,只要有一个场景遗漏,架构设计就存在可用性隐患,而根据墨菲定律“可能出错的事情最终都会出错”,架构隐患总有一天会导致系统故障。因此,我们在进行架构设计的时候必须全面分析系统的可用性,那么如何才能做到“全面”呢?
我今天介绍的FMEA方法就是保证我们做到全面分析的一个非常简单但是非常有效的方法。
FMEA介绍
FMEAFailure mode and effects analysis故障模式与影响分析又称为失效模式与后果分析、失效模式与效应分析、故障模式与后果分析等专栏采用“故障模式与影响分析”因为这个中文翻译更加符合可用性的语境。FMEA是一种在各行各业都有广泛应用的可用性分析方法通过对系统范围内潜在的故障模式加以分析并按照严重程度进行分类以确定失效对于系统的最终影响。
FMEA最早是在美国军方开始应用的20世纪40年代后期美国空军正式采用了FMEA。尽管最初是在军事领域建立的方法但FMEA方法现在已广泛应用于各种各样的行业包括半导体加工、餐饮服务、塑料制造、软件及医疗保健行业。FMEA之所以能够在这些差异很大的领域都得到应用根本原因在于FMEA是一套分析和思考的方法而不是某个领域的技能或者工具。
回到软件架构设计领域FMEA并不能指导我们如何做架构设计而是当我们设计出一个架构后再使用FMEA对这个架构进行分析看看架构是否还存在某些可用性的隐患。
FMEA方法
在架构设计领域FMEA的具体分析方法是
给出初始的架构设计图。
假设架构中某个部件发生故障。
分析此故障对系统功能造成的影响。
根据分析结果,判断架构是否需要进行优化。
FMEA分析的方法其实很简单就是一个FMEA分析表常见的FMEA分析表格包含下面部分。
1.功能点
当前的FMEA分析涉及的功能点注意这里的“功能点”指的是从用户角度来看的而不是从系统各个模块功能点划分来看的。例如对于一个用户管理系统使用FMEA分析时 “登录”“注册”才是功能点而用户管理系统中的数据库存储功能、Redis缓存功能不能作为FMEA分析的功能点。
2.故障模式
故障模式指的是系统会出现什么样的故障包括故障点和故障形式。需要特别注意的是这里的故障模式并不需要给出真正的故障原因我们只需要假设出现某种故障现象即可例如MySQL响应时间达到3秒。造成MySQL响应时间达到3秒可能的原因很多磁盘坏道、慢查询、服务器到MySQL的连接网络故障、MySQL bug等我们并不需要在故障模式中一一列出来而是在后面的“故障原因”一节中列出来。因为在实际应用过程中不管哪种原因只要现象是一样的对业务的影响就是一样的。
此外故障模式的描述要尽量精确多使用量化描述避免使用泛化的描述。例如推荐使用“MySQL响应时间达到3秒”而不是“MySQL响应慢”。
3.故障影响
当发生故障模式中描述的故障时,功能点具体会受到什么影响。常见的影响有:功能点偶尔不可用、功能点完全不可用、部分用户功能点不可用、功能点响应缓慢、功能点出错等。
故障影响也需要尽量准确描述。例如推荐使用“20%的用户无法登录”而不是“大部分用户无法登录”。要注意这里的数字不需要完全精确比如21.25%这样的数据其实是没有必要的我们只需要预估影响是20%还是40%。
4.严重程度
严重程度指站在业务的角度故障的影响程度,一般分为“致命/高/中/低/无”五个档次。严重程度按照这个公式进行评估:严重程度 = 功能点重要程度 × 故障影响范围 × 功能点受损程度。同样以用户管理系统为例登录功能比修改用户资料要重要得多80%的用户比20%的用户范围更大,完全无法登录比登录缓慢要更严重。因此我们可以得出如下故障模式的严重程度。
致命超过70%用户无法登录。
超过30%的用户无法登录。
所有用户登录时间超过5秒。
10%的用户登录时间超过5秒。
中:所有用户都无法修改资料。
20%的用户无法修改头像。
对于某个故障的影响到底属于哪个档次,有时会出现一些争议。例如,“所有用户都无法修改资料”,有的人认为是高,有的人可能认为是中,这个没有绝对标准,一般建议相关人员讨论确定即可。也不建议花费太多时间争论,争执不下时架构师裁定即可。
5.故障原因
“故障模式”中只描述了故障的现象,并没有单独列出故障原因。主要原因在于不管什么故障原因,故障现象相同,对功能点的影响就相同。那为何这里还要单独将故障原因列出来呢?主要原因有这几个:
不同的故障原因发生概率不相同
例如导致MySQL查询响应慢的原因可能是MySQL bug也可能是没有索引。很明显“MySQL bug”的概率要远远低于“没有索引”而不同的概率又会影响我们具体如何应对这个故障。
不同的故障原因检测手段不一样
例如磁盘坏道导致MySQL响应慢那我们需要增加机器的磁盘坏道检查这个检查很可能不是当前系统本身去做而是另外运维专门的系统如果是慢查询导致MySQL慢那我们只需要配置MySQL的慢查询日志即可。
不同的故障原因的处理措施不一样
例如如果是MySQL bug我们的应对措施只能是升级MySQL版本如果是没有索引我们的应对措施就是增加索引。
6.故障概率
这里的概率就是指某个具体故障原因发生的概率。例如磁盘坏道的概率、MySQL bug的概率、没有索引的概率。一般分为“高/中/低”三档即可,具体评估的时候需要有以下几点需要重点关注。
硬件
硬件随着使用时间推移故障概率会越来越高。例如新的硬盘坏道几率很低但使用了3年的硬盘坏道几率就会高很多。
开源系统
成熟的开源系统bug率低刚发布的开源系统bug率相比会高一些自己已经有使用经验的开源系统bug率会低刚开始尝试使用的开源系统bug率会高。
自研系统
和开源系统类似,成熟的自研系统故障概率会低,而新开发的系统故障概率会高。
高中低是相对的只是为了确定优先级以决定后续的资源投入没有必要绝对量化因为绝对量化是需要成本的而且很多时候都没法量化。例如XX开源系统是3个月故障一次还是6个月才故障一次是无法评估的。
7.风险程度
风险程度就是综合严重程度和故障概率来一起判断某个故障的最终等级,风险程度 = 严重程度 × 故障概率。因此可能出现某个故障影响非常严重但其概率很低最终来看风险程度就低。“某个机房业务瘫痪”对业务影响是致命的但如果故障原因是“地震”那概率就很低。例如广州的地震概率就很低5级以上地震的20世纪才1次1940年如果故障的原因是“机房空调烧坏”则概率就比地震高很多了可能是2年1次如果故障的原因是“系统所在机架掉电”这个概率比机房空调又要高了可能是1年1次。同样的故障影响不同的故障原因有不同的概率最终得到的风险级别就是不同的。
8.已有措施
针对具体的故障原因,系统现在是否提供了某些措施来应对,包括:检测告警、容错、自恢复等。
检测告警
最简单的措施就是检测故障,然后告警,系统自己不针对故障进行处理,需要人工干预。
容错
检测到故障后系统能够通过备份手段应对。例如MySQL主备机当业务服务器检测到主机无法连接后自动连接备机读取数据。
自恢复
检测到故障后系统能够自己恢复。例如Hadoop检测到某台机器故障后能够将存储在这台机器的副本重新分配到其他机器。当然这里的恢复主要还是指“业务”上的恢复一般不太可能将真正的故障恢复。例如Hadoop不可能将产生了磁盘坏道的磁盘修复成没有坏道的磁盘。
9.规避措施
规避措施指为了降低故障发生概率而做的一些事情,可以是技术手段,也可以是管理手段。例如:
技术手段为了避免新引入的MongoDB丢失数据在MySQL中冗余一份。
管理手段为了降低磁盘坏道的概率强制统一更换服务时间超过2年的磁盘。
10.解决措施
解决措施指为了能够解决问题而做的一些事情,一般都是技术手段。例如:
为了解决密码暴力破解,增加密码重试次数限制。
为了解决拖库导致数据泄露,将数据库中的敏感数据加密保存。
为了解决非法访问,增加白名单控制。
一般来说如果某个故障既可以采取规避措施又可以采取解决措施那么我们会优先选择解决措施毕竟能解决问题当然是最好的。但很多时候有些问题是系统自己无法解决的例如磁盘坏道、开源系统bug这类故障只能采取规避措施系统能够自己解决的故障大部分是和系统本身功能相关的。
11.后续规划
综合前面的分析,就可以看出哪些故障我们目前还缺乏对应的措施,哪些已有措施还不够,针对这些不足的地方,再结合风险程度进行排序,给出后续的改进规划。这些规划既可以是技术手段,也可以是管理手段;可以是规避措施,也可以是解决措施。同时需要考虑资源的投入情况,优先将风险程度高的系统隐患解决。
例如:
地震导致机房业务中断:这个故障模式就无法解决,只能通过备份中心规避,尽量减少影响;而机柜断电导致机房业务中断:可以通过将业务机器分散在不同机柜来规避。
敏感数据泄露:这个故障模式可以通过数据库加密的技术手段来解决。
MongoDB断电丢数据这个故障模式可以通过将数据冗余一份在MySQL中在故障情况下重建数据来规避影响。
FMEA实战
下面我以一个简单的样例来模拟一次FMEA分析。假设我们设计一个最简单的用户管理系统包含登录和注册两个功能其初始架构是
初始架构很简单MySQL负责存储Memcache以下简称MC负责缓存Server负责业务处理。我们来看看这个架构通过FMEA分析后能够有什么样的发现下表是分析的样例注意这个样例并不完整感兴趣的同学可以自行尝试将这个案例补充完整
经过上表的FMEA分析将“后续规划”列的内容汇总一下我们最终得到了下面几条需要改进的措施
MySQL增加备机。
MC从单机扩展为集群。
MySQL双网卡连接。
改进后的架构如下:
小结
今天我为你讲了FMEA高可用分析方法并且给出了一个简单的案例描述如何操作。FMEA是高可用架构设计的一个非常有用的方法能够发现架构中隐藏的高可用问题希望对你有所帮助。
这就是今天的全部内容留一道思考题给你吧请使用FMEA方法分析一下HDFS系统的架构看看HDFS是如何应对各种故障的并且分析一下HDFS是否存在高可用问题。

View File

@@ -0,0 +1,231 @@
因收到Google相关通知网站将会择期关闭。相关通知内容
25 高可用存储架构:双机架构
存储高可用方案的本质都是通过将数据复制到多个存储设备,通过数据冗余的方式来实现高可用,其复杂性主要体现在如何应对复制延迟和中断导致的数据不一致问题。因此,对任何一个高可用存储方案,我们需要从以下几个方面去进行思考和分析:
数据如何复制?
各个节点的职责是什么?
如何应对复制延迟?
如何应对复制中断?
常见的高可用存储架构有主备、主从、主主、集群、分区,每一种又可以根据业务的需求进行一些特殊的定制化功能,由此衍生出更多的变种。由于不同业务的定制功能难以通用化,今天我将针对业界通用的方案,来分析常见的双机高可用架构:主备、主从、主备/主从切换和主主。
主备复制
主备复制是最常见也是最简单的一种存储高可用方案几乎所有的存储系统都提供了主备复制的功能例如MySQL、Redis、MongoDB等。
1.基本实现
下面是标准的主备方案结构图:
其整体架构比较简单,主备架构中的“备机”主要还是起到一个备份作用,并不承担实际的业务读写操作,如果要把备机改为主机,需要人工操作。
2.优缺点分析
主备复制架构的优点就是简单,表现有:
对于客户端来说,不需要感知备机的存在,即使灾难恢复后,原来的备机被人工修改为主机后,对于客户端来说,只是认为主机的地址换了而已,无须知道是原来的备机升级为主机。
对于主机和备机来说,双方只需要进行数据复制即可,无须进行状态判断和主备切换这类复杂的操作。
主备复制架构的缺点主要有:
备机仅仅只为备份,并没有提供读写操作,硬件成本上有浪费。
故障后需要人工干预无法自动恢复。人工处理的效率是很低的可能打电话找到能够操作的人就耗费了10分钟甚至如果是深更半夜出了故障都没人知道。人工在执行恢复操作的过程中也容易出错因为这类操作并不常见可能1年就2、3次实际操作的时候很可能遇到各种意想不到的问题。
综合主备复制架构的优缺点,内部的后台管理系统使用主备复制架构的情况会比较多,例如学生管理系统、员工管理系统、假期管理系统等,因为这类系统的数据变更频率低,即使在某些场景下丢失数据,也可以通过人工的方式补全。
主从复制
主从复制和主备复制只有一字之差,“从”意思是“随从、仆从”,“备”的意思是备份。我们可以理解为仆从是要帮主人干活的,这里的干活就是承担“读”的操作。也就是说,主机负责读写操作,从机只负责读操作,不负责写操作。
1.基本实现
下面是标准的主从复制架构:
与主备复制架构比较类似,主要的差别点在于从机正常情况下也是要提供读的操作。
2.优缺点分析
主从复制与主备复制相比,优点有:
主从复制在主机故障时,读操作相关的业务可以继续运行。
主从复制架构的从机提供读操作,发挥了硬件的性能。
缺点有:
主从复制架构中,客户端需要感知主从关系,并将不同的操作发给不同的机器进行处理,复杂度比主备复制要高。
主从复制架构中,从机提供读业务,如果主从复制延迟比较大,业务会因为数据不一致出现问题。
故障时需要人工干预。
综合主从复制的优缺点一般情况下写少读多的业务使用主从复制的存储架构比较多。例如论坛、BBS、新闻网站这类业务此类业务的读操作数量是写操作数量的10倍甚至100倍以上。
双机切换
1.设计关键
主备复制和主从复制方案存在两个共性的问题:
主机故障后,无法进行写操作。
如果主机无法恢复,需要人工指定新的主机角色。
双机切换就是为了解决这两个问题而产生的,包括主备切换和主从切换两种方案。简单来说,这两个方案就是在原有方案的基础上增加“切换”功能,即系统自动决定主机角色,并完成角色切换。由于主备切换和主从切换在切换的设计上没有差别,我接下来以主备切换为例,一起来看看双机切换架构是如何实现的。
要实现一个完善的切换方案,必须考虑这几个关键的设计点:
主备间状态判断
主要包括两方面:状态传递的渠道,以及状态检测的内容。
状态传递的渠道:是相互间互相连接,还是第三方仲裁?
状态检测的内容:例如机器是否掉电、进程是否存在、响应是否缓慢等。
切换决策
主要包括几方面:切换时机、切换策略、自动程度。
切换时机什么情况下备机应该升级为主机是机器掉电后备机才升级还是主机上的进程不存在就升级还是主机响应时间超过2秒就升级还是3分钟内主机连续重启3次就升级等。
切换策略:原来的主机故障恢复后,要再次切换,确保原来的主机继续做主机,还是原来的主机故障恢复后自动成为新的备机?
自动程度:切换是完全自动的,还是半自动的?例如,系统判断当前需要切换,但需要人工做最终的确认操作(例如,单击一下“切换”按钮)。
数据冲突解决
当原有故障的主机恢复后新旧主机之间可能存在数据冲突。例如用户在旧主机上新增了一条ID为100的数据这个数据还没有复制到旧的备机此时发生了切换旧的备机升级为新的主机用户又在新的主机上新增了一条ID为100的数据当旧的故障主机恢复后这两条ID都为100的数据应该怎么处理
以上设计点并没有放之四海而皆准的答案不同的业务要求不一样所以切换方案比复制方案不只是多了一个切换功能那么简单而是复杂度上升了一个量级。形象点来说如果复制方案的代码是1000行那么切换方案的代码可能就是10000行多出来的那9000行就是用于实现上面我所讲的3个设计点的。
2.常见架构
根据状态传递渠道的不同,常见的主备切换架构有三种形式:互连式、中介式和模拟式。
互连式
故名思议,互连式就是指主备机直接建立状态传递的渠道,架构图请注意与主备复制架构对比。
你可以看到,在主备复制的架构基础上,主机和备机多了一个“状态传递”的通道,这个通道就是用来传递状态信息的。这个通道的具体实现可以有很多方式:
可以是网络连接(例如,各开一个端口),也可以是非网络连接(用串口线连接)。
可以是主机发送状态给备机,也可以是备机到主机来获取状态信息。
可以和数据复制通道共用,也可以独立一条通道。
状态传递通道可以是一条,也可以是多条,还可以是不同类型的通道混合(例如,网络+串口)。
为了充分利用切换方案能够自动决定主机这个优势,客户端这里也会有一些相应的改变,常见的方式有:
为了切换后不影响客户端的访问主机和备机之间共享一个对客户端来说唯一的地址。例如虚拟IP主机需要绑定这个虚拟的IP。
客户端同时记录主备机的地址,哪个能访问就访问哪个;备机虽然能收到客户端的操作请求,但是会直接拒绝,拒绝的原因就是“备机不对外提供服务”。
互连式主备切换主要的缺点在于:
如果状态传递的通道本身有故障(例如,网线被人不小心踢掉了),那么备机也会认为主机故障了从而将自己升级为主机,而此时主机并没有故障,最终就可能出现两个主机。
虽然可以通过增加多个通道来增强状态传递的可靠性,但这样做只是降低了通道故障概率而已,不能从根本上解决这个缺点,而且通道越多,后续的状态决策会更加复杂,因为对备机来说,可能从不同的通道收到了不同甚至矛盾的状态信息。
中介式
中介式指的是在主备两者之外引入第三方中介,主备机之间不直接连接,而都去连接中介,并且通过中介来传递状态信息,其架构图如下:
对比一下互连式切换架构,我们可以看到,主机和备机不再通过互联通道传递状态信息,而是都将状态上报给中介这一角色。单纯从架构上看,中介式似乎比互连式更加复杂了,首先要引入中介,然后要各自上报状态。然而事实上,中介式架构在状态传递和决策上却更加简单了,这是为何呢?
连接管理更简单:主备机无须再建立和管理多种类型的状态传递连接通道,只要连接到中介即可,实际上是降低了主备机的连接管理复杂度。
例如,互连式要求主机开一个监听端口,备机来获取状态信息;或者要求备机开一个监听端口,主机推送状态信息到备机;如果还采用了串口连接,则需要增加串口连接管理和数据读取。采用中介式后,主备机都只需要把状态信息发送给中介,或者从中介获取对方的状态信息。无论是发送还是获取,主备机都是作为中介的客户端去操作,复杂度会降低。
状态决策更简单:主备机的状态决策简单了,无须考虑多种类型的连接通道获取的状态信息如何决策的问题,只需要按照下面简单的算法即可完成状态决策。
无论是主机还是备机,初始状态都是备机,并且只要与中介断开连接,就将自己降级为备机,因此可能出现双备机的情况。
主机与中介断连后,中介能够立刻告知备机,备机将自己升级为主机。
如果是网络中断导致主机与中介断连,主机自己会降级为备机,网络恢复后,旧的主机以新的备机身份向中介上报自己的状态。
如果是掉电重启或者进程重启,旧的主机初始状态为备机,与中介恢复连接后,发现已经有主机了,保持自己备机状态不变。
主备机与中介连接都正常的情况下按照实际的状态决定是否进行切换。例如主机响应时间超过3秒就进行切换主机降级为备机备机升级为主机即可。
虽然中介式架构在状态传递和状态决策上更加简单,但并不意味着这种优点是没有代价的,其关键代价就在于如何实现中介本身的高可用。如果中介自己宕机了,整个系统就进入了双备的状态,写操作相关的业务就不可用了。这就陷入了一个递归的陷阱:为了实现高可用,我们引入中介,但中介本身又要求高可用,于是又要设计中介的高可用方案……如此递归下去就无穷无尽了。
MongoDB的Replica Set采取的就是这种方式其基本架构如下
MongoDB(M)表示主节点MongoDB(S)表示备节点MongoDB(A)表示仲裁节点。主备节点存储数据,仲裁节点不存储数据。客户端同时连接主节点与备节点,不连接仲裁节点。
幸运的是开源方案已经有比较成熟的中介式解决方案例如ZooKeeper和Keepalived。ZooKeeper本身已经实现了高可用集群架构因此已经帮我们解决了中介本身的可靠性问题在工程实践中推荐基于ZooKeeper搭建中介式切换架构。
模拟式
模拟式指主备机之间并不传递任何状态数据,而是备机模拟成一个客户端,向主机发起模拟的读写操作,根据读写操作的响应情况来判断主机的状态。其基本架构如下:
对比一下互连式切换架构,我们可以看到,主备机之间只有数据复制通道,而没有状态传递通道,备机通过模拟的读写操作来探测主机的状态,然后根据读写操作的响应情况来进行状态决策。
模拟式切换与互连式切换相比,优点是实现更加简单,因为省去了状态传递通道的建立和管理工作。
简单既是优点同时也是缺点。因为模拟式读写操作获取的状态信息只有响应信息例如HTTP 404超时、响应时间超过3秒等没有互连式那样多样除了响应信息还可以包含CPU负载、I/O负载、吞吐量、响应时间等基于有限的状态来做状态决策可能出现偏差。
主主复制
主主复制指的是两台机器都是主机,互相将数据复制给对方,客户端可以任意挑选其中一台机器进行读写操作,下面是基本架构图。
相比主备切换架构,主主复制架构具有如下特点:
两台都是主机,不存在切换的概念。
客户端无须区分不同角色的主机,随便将读写操作发送给哪台主机都可以。
从上面的描述来看,主主复制架构从总体上来看要简单很多,无须状态信息传递,也无须状态决策和状态切换。然而事实上主主复制架构也并不简单,而是有其独特的复杂性,具体表现在:如果采取主主复制架构,必须保证数据能够双向复制,而很多数据是不能双向复制的。例如:
用户注册后生成的用户ID如果按照数字增长那就不能双向复制否则就会出现X用户在主机A注册分配的用户ID是100同时Y用户在主机B注册分配的用户ID也是100这就出现了冲突。
库存不能双向复制。例如一件商品库存100件主机A上减了1件变成99主机B上减了2件变成98然后主机A将库存99复制到主机B主机B原有的库存98被覆盖变成了99而实际上此时真正的库存是97。类似的还有余额数据。
因此主主复制架构对数据的设计有严格的要求一般适合于那些临时性、可丢失、可覆盖的数据场景。例如用户登录产生的session数据可以重新登录生成、用户行为的日志数据可以丢失、论坛的草稿数据可以丢失等。
小结
今天我为你讲了高可用存储架构中常见的双机架构,分析了每类架构的优缺点以及适应场景,希望对你有所帮助。
这就是今天的全部内容,留一道思考题给你吧,如果你来设计一个政府信息公开网站的信息存储系统,你会采取哪种架构?谈谈你的分析和理由。

View File

@@ -0,0 +1,183 @@
因收到Google相关通知网站将会择期关闭。相关通知内容
26 高可用存储架构:集群和分区
上一期我讲了高可用存储架构中常见的双机架构,分别为主备复制、主从复制、双机切换和主主复制,并分析了每类架构的优缺点以及适应场景。
今天我们一起来看看另外两种常见的高可用存储架构:数据集群和数据分区。
数据集群
主备、主从、主主架构本质上都有一个隐含的假设主机能够存储所有数据但主机本身的存储和处理能力肯定是有极限的。以PC为例Intel 386时代服务器存储能力只有几百MBIntel 奔腾时代服务器存储能力可以有几十GBIntel 酷睿多核时代的服务器可以有几个TB。单纯从硬件发展的角度来看似乎发展速度还是挺快的但如果和业务发展速度对比那就差得远了。早在2013年Facebook就有2500亿张上传照片当时这些照片的容量就已经达到了250 PB字节250 × 1024TB平均一天上传的图片有3亿5000万张。如此大量的数据单台服务器肯定是无法存储和处理的我们必须使用多台服务器来存储数据这就是数据集群架构。
简单来说集群就是多台机器组合在一起形成一个统一的系统这里的“多台”数量上至少是3台相比而言主备、主从都是2台机器。根据集群中机器承担的不同角色来划分集群可以分为两类数据集中集群、数据分散集群。
1.数据集中集群
数据集中集群与主备、主从这类架构相似我们也可以称数据集中集群为1主多备或者1主多从。无论是1主1从、1主1备还是1主多备、1主多从数据都只能往主机中写而读操作可以参考主备、主从架构进行灵活多变。下图是读写全部到主机的一种架构
虽然架构上是类似的,但由于集群里面的服务器数量更多,导致复杂度整体更高一些,具体体现在:
主机如何将数据复制给备机
主备和主从架构中,只有一条复制通道,而数据集中集群架构中,存在多条复制通道。多条复制通道首先会增大主机复制的压力,某些场景下我们需要考虑如何降低主机复制压力,或者降低主机复制给正常读写带来的压力。
其次,多条复制通道可能会导致多个备机之间数据不一致,某些场景下我们需要对备机之间的数据一致性进行检查和修正。
备机如何检测主机状态
主备和主从架构中,只有一台备机需要进行主机状态判断。在数据集中集群架构中,多台备机都需要对主机状态进行判断,而不同的备机判断的结果可能是不同的,如何处理不同备机对主机状态的不同判断,是一个复杂的问题。
主机故障后,如何决定新的主机
主从架构中,如果主机故障,将备机升级为主机即可;而在数据集中集群架构中,有多台备机都可以升级为主机,但实际上只能允许一台备机升级为主机,那么究竟选择哪一台备机作为新的主机,备机之间如何协调,这也是一个复杂的问题。
目前开源的数据集中集群以ZooKeeper为典型ZooKeeper通过ZAB算法来解决上述提到的几个问题但ZAB算法的复杂度是很高的。
2.数据分散集群
数据分散集群指多个服务器组成一个集群,每台服务器都会负责存储一部分数据;同时,为了提升硬件利用率,每台服务器又会备份一部分数据。
数据分散集群的复杂点在于如何将数据分配到不同的服务器上,算法需要考虑这些设计点:
均衡性
算法需要保证服务器上的数据分区基本是均衡的,不能存在某台服务器上的分区数量是另外一台服务器的几倍的情况。
容错性
当出现部分服务器故障时,算法需要将原来分配给故障服务器的数据分区分配给其他服务器。
可伸缩性
当集群容量不够,扩充新的服务器后,算法能够自动将部分数据分区迁移到新服务器,并保证扩容后所有服务器的均衡性。
数据分散集群和数据集中集群的不同点在于,数据分散集群中的每台服务器都可以处理读写请求,因此不存在数据集中集群中负责写的主机那样的角色。但在数据分散集群中,必须有一个角色来负责执行数据分配算法,这个角色可以是独立的一台服务器,也可以是集群自己选举出的一台服务器。如果是集群服务器选举出来一台机器承担数据分区分配的职责,则这台服务器一般也会叫作主机,但我们需要知道这里的“主机”和数据集中集群中的“主机”,其职责是有差异的。
Hadoop的实现就是独立的服务器负责数据分区的分配这台服务器叫作Namenode。Hadoop的数据分区管理架构如下
下面是Hadoop官方的解释能够说明集中式数据分区管理的基本方式。
HDFS采用master/slave架构。一个HDFS集群由一个Namenode和一定数目的Datanodes组成。
Namenode是一个中心服务器负责管理文件系统的名字空间namespace以及客户端对文件的访问。
集群中的Datanode一般是一个节点一个负责管理它所在节点上的存储。HDFS暴露了文件系统的名字空间用户能够以文件的形式在上面存储数据。从内部看一个文件其实被分成一个或多个数据块这些块存储在一组Datanode上。
Namenode执行文件系统的名字空间操作比如打开、关闭、重命名文件或目录。它也负责确定数据块到具体Datanode节点的映射。Datanode负责处理文件系统客户端的读写请求。在Namenode的统一调度下进行数据块的创建、删除和复制操作。
与Hadoop不同的是Elasticsearch集群通过选举一台服务器来做数据分区的分配叫作master node其数据分区管理架构是
其中master节点的职责如下
The master node is responsible for lightweight cluster-wide actions such as creating or deleting an index, tracking which nodes are part of the cluster, and deciding which shards to allocate to which nodes. It is important for cluster health to have a stable master node.
来源https://www.elastic.co/guide/en/elasticsearch/reference/current/modules-node.html
数据集中集群架构中客户端只能将数据写到主机数据分散集群架构中客户端可以向任意服务器中读写数据。正是因为这个关键的差异决定了两种集群的应用场景不同。一般来说数据集中集群适合数据量不大集群机器数量不多的场景。例如ZooKeeper集群一般推荐5台机器左右数据量是单台服务器就能够支撑而数据分散集群由于其良好的可伸缩性适合业务数据量巨大、集群机器数量庞大的业务场景。例如Hadoop集群、HBase集群大规模的集群可以达到上百台甚至上千台服务器。
数据分区
前面我们讨论的存储高可用架构都是基于硬件故障的场景去考虑和设计的,主要考虑当部分硬件可能损坏的情况下系统应该如何处理,但对于一些影响非常大的灾难或者事故来说,有可能所有的硬件全部故障。例如,新奥尔良水灾、美加大停电、洛杉矶大地震等这些极端灾害或者事故,可能会导致一个城市甚至一个地区的所有基础设施瘫痪,这种情况下基于硬件故障而设计的高可用架构不再适用,我们需要基于地理级别的故障来设计高可用架构,这就是数据分区架构产生的背景。
数据分区指将数据按照一定的规则进行分区,不同分区分布在不同的地理位置上,每个分区存储一部分数据,通过这种方式来规避地理级别的故障所造成的巨大影响。采用了数据分区的架构后,即使某个地区发生严重的自然灾害或者事故,受影响的也只是一部分数据,而不是全部数据都不可用;当故障恢复后,其他地区备份的数据也可以帮助故障地区快速恢复业务。
设计一个良好的数据分区架构,需要从多方面去考虑。
1.数据量
数据量的大小直接决定了分区的规则复杂度。例如使用MySQL来存储数据假设一台MySQL存储能力是500GB那么2TB的数据就至少需要4台MySQL服务器而如果数据是200TB并不是增加到800台的MySQL服务器那么简单。如果按照4台服务器那样去平行管理800台服务器复杂度会发生本质的变化具体表现为
800台服务器里面可能每周都有一两台服务器故障从800台里面定位出2台服务器故障很多情况下并不是一件容易的事情运维复杂度高。
增加新的服务器分区相关的配置甚至规则需要修改而每次修改理论上都有可能影响已有的800台服务器的运行不小心改错配置的情况在实践中太常见了。
如此大量的数据,如果在地理位置上全部集中于某个城市,风险很大,遇到了水灾、大停电这种灾难性的故障时,数据可能全部丢失,因此分区规则需要考虑地理容灾。
因此,数据量越大,分区规则会越复杂,考虑的情况也越多。
2.分区规则
地理位置有近有远,因此可以得到不同的分区规则,包括洲际分区、国家分区、城市分区。具体采取哪种或者哪几种规则,需要综合考虑业务范围、成本等因素。
通常情况下,洲际分区主要用于面向不同大洲提供服务,由于跨洲通讯的网络延迟已经大到不适合提供在线服务了,因此洲际间的数据中心可以不互通或者仅仅作为备份;国家分区主要用于面向不同国家的用户提供服务,不同国家有不同语言、法律、业务等,国家间的分区一般也仅作为备份;城市分区由于都在同一个国家或者地区内,网络延迟较低,业务相似,分区同时对外提供服务,可以满足业务异地多活之类的需求。
3.复制规则
数据分区指将数据分散在多个地区,在某些异常或者灾难情况下,虽然部分数据受影响,但整体数据并没有全部被影响,本身就相当于一个高可用方案了。但仅仅做到这点还不够,因为每个分区本身的数据量虽然只是整体数据的一部分,但还是很大,这部分数据如果损坏或者丢失,损失同样难以接受。因此即使是分区架构,同样需要考虑复制方案。
常见的分区复制规则有三种:集中式、互备式和独立式。
集中式
集中式备份指存在一个总的备份中心,所有的分区都将数据备份到备份中心,其基本架构如下:
集中式备份架构的优缺点是:
设计简单,各分区之间并无直接联系,可以做到互不影响。
扩展容易,如果要增加第四个分区(例如,武汉分区),只需要将武汉分区的数据复制到西安备份中心即可,其他分区不受影响。
成本较高,需要建设一个独立的备份中心。
互备式
互备式备份指每个分区备份另外一个分区的数据,其基本架构如下:
互备式备份架构的优缺点是:
设计比较复杂,各个分区除了要承担业务数据存储,还需要承担备份功能,相互之间互相关联和影响。
扩展麻烦,如果增加一个武汉分区,则需要修改广州分区的复制指向武汉分区,然后将武汉分区的复制指向北京分区。而原有北京分区已经备份了的广州分区的数据怎么处理也是个难题,不管是做数据迁移,还是广州分区历史数据保留在北京分区,新数据备份到武汉分区,无论哪种方式都很麻烦。
成本低,直接利用已有的设备。
独立式
独立式备份指每个分区自己有独立的备份中心,其基本架构如下:
有一个细节需要特别注意,各个分区的备份并不和原来的分区在一个地方。例如,北京分区的备份放到了天津,上海的放到了杭州,广州的放到了汕头,这样做的主要目的是规避同城或者相同地理位置同时发生灾难性故障的极端情况。如果北京分区机房在朝阳区,而备份机房放在通州区,整个北京停电的话,两个机房都无法工作。
独立式备份架构的优缺点是:
设计简单,各分区互不影响。
扩展容易,新增加的分区只需要搭建自己的备份中心即可。
成本高,每个分区需要独立的备份中心,备份中心的场地成本是主要成本,因此独立式比集中式成本要高很多。
小结
今天我为你讲了大数据量存储的两种高可用存储架构:集群架构和分区架构,并介绍了其中的关键设计点,希望对你有所帮助。
这就是今天的全部内容,留一道思考题给你吧,既然数据集群就可以做到不同节点之间复制数据,为何不搭建一个远距离分布的集群来应对地理位置级别的故障呢?

View File

@@ -0,0 +1,152 @@
因收到Google相关通知网站将会择期关闭。相关通知内容
27 如何设计计算高可用架构?
计算高可用的主要设计目标是当出现部分硬件损坏时,计算任务能够继续正常运行。因此计算高可用的本质是通过冗余来规避部分故障的风险,单台服务器是无论如何都达不到这个目标的。所以计算高可用的设计思想很简单:通过增加更多服务器来达到计算高可用。
计算高可用架构的设计复杂度主要体现在任务管理方面,即当任务在某台服务器上执行失败后,如何将任务重新分配到新的服务器进行执行。因此,计算高可用架构设计的关键点有下面两点。
1.哪些服务器可以执行任务
第一种方式和计算高性能中的集群类似,每个服务器都可以执行任务。例如,常见的访问网站的某个页面。
第二种方式和存储高可用中的集群类似只有特定服务器通常叫“主机”可以执行任务。当执行任务的服务器故障后系统需要挑选新的服务器来执行任务。例如ZooKeeper的Leader才能处理写操作请求。
2.任务如何重新执行
第一种策略是对于已经分配的任务即使执行失败也不做任何处理,系统只需要保证新的任务能够分配到其他非故障服务器上执行即可。
第二种策略是设计一个任务管理器来管理需要执行的计算任务,服务器执行完任务后,需要向任务管理器反馈任务执行结果,任务管理器根据任务执行结果来决定是否需要将任务重新分配到另外的服务器上执行。
需要注意的是:“任务分配器”是一个逻辑的概念,并不一定要求系统存在一个独立的任务分配器模块。例如:
Nginx将页面请求发送给Web服务器而CSS/JS等静态文件直接读取本地缓存。这里的Nginx角色是反向代理系统但是承担了任务分配器的职责而不需要Nginx做反向代理后面再来一个任务分配器。
对于一些后台批量运算的任务,可以设计一个独立的任务分配系统来管理这些批处理任务的执行和分配。
ZooKeeper中的Follower节点当接收到写请求时会将请求转发给Leader节点处理当接收到读请求时就自己处理这里的Follower就相当于一个逻辑上的任务分配器。
接下来,我将详细阐述常见的计算高可用架构:主备、主从和集群。
主备
主备架构是计算高可用最简单的架构,和存储高可用的主备复制架构类似,但是要更简单一些,因为计算高可用的主备架构无须数据复制,其基本的架构示意图如下:
主备方案的详细设计:
主机执行所有计算任务。例如,读写数据、执行操作等。
当主机故障(例如,主机宕机)时,任务分配器不会自动将计算任务发送给备机,此时系统处于不可用状态。
如果主机能够恢复(不管是人工恢复还是自动恢复),任务分配器继续将任务发送给主机。
如果主机不能够恢复(例如,机器硬盘损坏,短时间内无法恢复),则需要人工操作,将备机升为主机,然后让任务分配器将任务发送给新的主机(即原来的备机);同时,为了继续保持主备架构,需要人工增加新的机器作为备机。
根据备机状态的不同,主备架构又可以细分为冷备架构和温备架构。
冷备:备机上的程序包和配置文件都准备好,但备机上的业务系统没有启动(注意:备机的服务器是启动的),主机故障后,需要人工手工将备机的业务系统启动,并将任务分配器的任务请求切换发送给备机。
温备:备机上的业务系统已经启动,只是不对外提供服务,主机故障后,人工只需要将任务分配器的任务请求切换发送到备机即可。冷备可以节省一定的能源,但温备能够大大减少手工操作时间,因此一般情况下推荐用温备的方式。
主备架构的优点就是简单主备机之间不需要进行交互状态判断和切换操作由人工执行系统实现很简单。而缺点正好也体现在“人工操作”这点上因为人工操作的时间不可控可能系统已经发生问题了但维护人员还没发现等了1个小时才发现。发现后人工切换的操作效率也比较低可能需要半个小时才完成切换操作而且手工操作过程中容易出错。例如修改配置文件改错了、启动了错误的程序等。
和存储高可用中的主备复制架构类似,计算高可用的主备架构也比较适合与内部管理系统、后台管理系统这类使用人数不多、使用频率不高的业务,不太适合在线的业务。
主从
和存储高可用中的主从复制架构类似,计算高可用的主从架构中的从机也是要执行任务的。任务分配器需要将任务进行分类,确定哪些任务可以发送给主机执行,哪些任务可以发送给备机执行,其基本的架构示意图如下:
主从方案详细设计:
正常情况下主机执行部分计算任务如图中的“计算任务A”备机执行部分计算任务如图中的“计算任务B”
当主机故障(例如,主机宕机)时,任务分配器不会自动将原本发送给主机的任务发送给从机,而是继续发送给主机,不管这些任务执行是否成功。
如果主机能够恢复不管是人工恢复还是自动恢复任务分配器继续按照原有的设计策略分配任务即计算任务A发送给主机计算任务B发送给从机。
如果主机不能够恢复(例如,机器硬盘损坏,短时间内无法恢复),则需要人工操作,将原来的从机升级为主机(一般只是修改配置即可),增加新的机器作为从机,新的从机准备就绪后,任务分配器继续按照原有的设计策略分配任务。
主从架构与主备架构相比,优缺点有:
优点:主从架构的从机也执行任务,发挥了从机的硬件性能。
缺点:主从架构需要将任务分类,任务分配器会复杂一些。
集群
主备架构和主从架构通过冗余一台服务器来提升可用性,且需要人工来切换主备或者主从。这样的架构虽然简单,但存在一个主要的问题:人工操作效率低、容易出错、不能及时处理故障。因此在可用性要求更加严格的场景中,我们需要系统能够自动完成切换操作,这就是高可用集群方案。
高可用计算的集群方案根据集群中服务器节点角色的不同可以分为两类一类是对称集群即集群中每个服务器的角色都是一样的都可以执行所有任务另一类是非对称集群集群中的服务器分为多个不同的角色不同的角色执行不同的任务例如最常见的Master-Slave角色。
需要注意的是计算高可用集群包含2台服务器的集群这点和存储高可用集群不太一样。存储高可用集群把双机架构和集群架构进行了区分而在计算高可用集群架构中2台服务器的集群和多台服务器的集群在设计上没有本质区别因此不需要进行区分。
对称集群
对称集群更通俗的叫法是负载均衡集群,因此接下来我使用“负载均衡集群”这个通俗的说法,架构示意图如下:
负载均衡集群详细设计:
正常情况下,任务分配器采取某种策略(随机、轮询等)将计算任务分配给集群中的不同服务器。
当集群中的某台服务器故障后,任务分配器不再将任务分配给它,而是将任务分配给其他服务器执行。
当故障的服务器恢复后,任务分配器重新将任务分配给它执行。
负载均衡集群的设计关键点在于两点:
任务分配器需要选取分配策略。
任务分配器需要检测服务器状态。
任务分配策略比较简单,轮询和随机基本就够了。状态检测稍微复杂一些,既要检测服务器的状态,例如服务器是否宕机、网络是否正常等;同时还要检测任务的执行状态,例如任务是否卡死、是否执行时间过长等。常用的做法是任务分配器和服务器之间通过心跳来传递信息,包括服务器信息和任务信息,然后根据实际情况来确定状态判断条件。
例如一个在线页面访问系统正常情况下页面平均会在500毫秒内返回那么状态判断条件可以设计为1分钟内响应时间超过1秒包括超时的页面数量占了80%时,就认为服务器有故障。
例如一个后台统计任务系统正常情况下任务会在5分钟内执行完成那么状态判断条件可以设计为单个任务执行时间超过10分钟还没有结束就认为服务器有故障。
通过上面两个案例可以看出,不同业务场景的状态判断条件差异很大,实际设计时要根据业务需求来进行设计和调优。
非对称集群
非对称集群中不同服务器的角色是不同的不同角色的服务器承担不同的职责。以Master-Slave为例部分任务是Master服务器才能执行部分任务是Slave服务器才能执行。非对称集群的基本架构示意图如下
非对称集群架构详细设计:
集群会通过某种方式来区分不同服务器的角色。例如通过ZAB算法选举或者简单地取当前存活服务器中节点ID最小的服务器作为Master服务器。
任务分配器将不同任务发送给不同服务器。例如图中的计算任务A发送给Master服务器计算任务B发送给Slave服务器。
当指定类型的服务器故障时需要重新分配角色。例如Master服务器故障后需要将剩余的Slave服务器中的一个重新指定为Master服务器如果是Slave服务器故障则并不需要重新分配角色只需要将故障服务器从集群剔除即可。
非对称集群相比负载均衡集群,设计复杂度主要体现在两个方面:
任务分配策略更加复杂:需要将任务划分为不同类型并分配给不同角色的集群节点。
角色分配策略实现比较复杂例如可能需要使用ZAB、Raft这类复杂的算法来实现Leader的选举。
我以ZooKeeper为例
任务分配器ZooKeeper中不存在独立的任务分配器节点每个Server都是任务分配器Follower收到请求后会进行判断如果是写请求就转发给Leader如果是读请求就自己处理。
角色指定ZooKeeper通过ZAB算法来选举Leader当Leader故障后所有的Follower节点会暂停读写操作开始进行选举直到新的Leader选举出来后才继续对Client提供服务。
小结
今天我为你讲了几种常见的计算高可用架构,并分析了不同方案的详细设计,希望对你有所帮助。
这就是今天的全部内容,留一道思考题给你吧,计算高可用架构从形式上和存储高可用架构看上去几乎一样,它们的复杂度是一样的么?谈谈你的理解。

View File

@@ -0,0 +1,103 @@
因收到Google相关通知网站将会择期关闭。相关通知内容
28 业务高可用的保障:异地多活架构
无论是高可用计算架构还是高可用存储架构其本质的设计目的都是为了解决部分服务器故障的场景下如何保证系统能够继续提供服务。但在一些极端场景下有可能所有服务器都出现故障。例如典型的有机房断电、机房火灾、地震、水灾……这些极端情况会导致某个系统所有服务器都故障或者业务整体瘫痪而且即使有其他地区的备份把备份业务系统全部恢复到能够正常提供业务花费的时间也比较长可能是半小时也可能是12小时。因为备份系统平时不对外提供服务可能会存在很多隐藏的问题没有发现。如果业务期望达到即使在此类灾难性故障的情况下业务也不受影响或者在几分钟内就能够很快恢复那么就需要设计异地多活架构。
今天我来聊聊异地多活架构,接下来还会再讲异地多活架构的设计技巧和流程。
应用场景
顾名思义,异地多活架构的关键点就是异地、多活,其中异地就是指地理位置上不同的地方,类似于“不要把鸡蛋都放在同一篮子里”;多活就是指不同地理位置上的系统都能够提供业务服务,这里的“活”是活动、活跃的意思。判断一个系统是否符合异地多活,需要满足两个标准:
正常情况下,用户无论访问哪一个地点的业务系统,都能够得到正确的业务服务。
某个地方业务异常的时候,用户访问其他地方正常的业务系统,能够得到正确的业务服务。
与“活”对应的是字是“备”,备是备份,正常情况下对外是不提供服务的,如果需要提供服务,则需要大量的人工干预和操作,花费大量的时间才能让“备”变成“活”。
单纯从异地多活的描述来看,异地多活很强大,能够保证在灾难的情况下业务都不受影响。那是不是意味着不管什么业务,我们都要去实现异地多活架构呢?其实不然,因为实现异地多活架构不是没有代价的,相反其代价很高,具体表现为:
系统复杂度会发生质的变化,需要设计复杂的异地多活架构。
成本会上升,毕竟要多在一个或者多个机房搭建独立的一套业务系统。
因此异地多活虽然功能很强大但也不是每个业务不管三七二十一都要上异地多活。例如常见的新闻网站、企业内部的IT系统、游戏、博客站点等如果无法承受异地多活带来的复杂度和成本是可以不做异地多活的只需要做异地备份即可。因为这类业务系统即使中断对用户的影响并不会很大例如A新闻网站看不了用户换个新闻网站即可。而共享单车、滴滴出行、支付宝、微信这类业务就需要做异地多活了这类业务系统中断后对用户的影响很大。例如支付宝用不了就没法买东西了滴滴用不了用户就打不到车了。
当然,如果业务规模很大,能够做异地多活的情况下还是尽量。首先,这样能够在异常的场景下给用户提供更好的体验;其次,业务规模很大肯定会伴随衍生的收入,例如广告收入,异地多活能够减少异常场景带来的收入损失。同样以新闻网站为例,虽然从业务的角度来看,新闻类网站对用户影响不大,反正用户也可以从其他地方看到基本相同的新闻,甚至用户几个小时不看新闻也没什么问题。但是从网站本身来看,几个小时不可访问肯定会影响用户对网站的口碑;其次几个小时不可访问,网站上的广告收入损失也会很大。
架构模式
根据地理位置上的距离来划分,异地多活架构可以分为同城异区、跨城异地、跨国异地。接下来我详细解释一下每一种架构的细节与优缺点。
1.同城异区
同城异区指的是将业务部署在同一个城市不同区的多个机房。例如,在北京部署两个机房,一个机房在海淀区,一个在通州区,然后将两个机房用专用的高速网络连接在一起。
如果我们考虑一些极端场景(例如,美加大停电、新奥尔良水灾),同城异区似乎没什么作用,那为何我们还要设计同城异区这种架构呢?答案就在于“同城”。
同城的两个机房,距离上一般大约就是几十千米,通过搭建高速的网络,同城异区的两个机房能够实现和同一个机房内几乎一样的网络传输速度。这就意味着虽然是两个不同地理位置上的机房,但逻辑上我们可以将它们看作同一个机房,这样的设计大大降低了复杂度,减少了异地多活的设计和实现复杂度及成本。
那如果采用了同城异区架构,一旦发生新奥尔良水灾这种灾难怎么办呢?很遗憾,答案是无能为力。但我们需要考虑的是,这种极端灾难发生概率是比较低的,可能几年或者十几年才发生一次。其次,除了这类灾难,机房火灾、机房停电、机房空调故障这类问题发生的概率更高,而且破坏力一样很大。而这些故障场景,同城异区架构都可以很好地解决。因此,结合复杂度、成本、故障发生概率来综合考虑,同城异区是应对机房级别故障的最优架构。
2.跨城异地
跨城异地指的是业务部署在不同城市的多个机房,而且距离最好要远一些。例如,将业务部署在北京和广州两个机房,而不是将业务部署在广州和深圳的两个机房。
为何跨城异地要强调距离要远呢?前面我在介绍同城异区的架构时提到同城异区不能解决新奥尔良水灾这种问题,而两个城市离得太近又无法应对如美加大停电这种问题,跨城异地其实就是为了解决这两类问题的,因此需要在距离上比较远,才能有效应对这类极端灾难事件。
跨城异地虽然能够有效应对极端灾难事件但“距离较远”这点并不只是一个距离数字上的变化而是量变引起了质变导致了跨城异地的架构复杂度大大上升。距离增加带来的最主要问题是两个机房的网络传输速度会降低这不是以人的意志为转移的而是物理定律决定的即光速真空传播大约是每秒30万千米在光纤中传输的速度大约是每秒20万千米再加上传输中的各种网络设备的处理实际还远远达不到理论上的速度。
除了距离上的限制中间传输各种不可控的因素也非常多。例如挖掘机把光纤挖断、中美海底电缆被拖船扯断、骨干网故障等这些线路很多是第三方维护针对故障我们根本无能为力也无法预知。例如广州机房到北京机房正常情况下RTT大约是50毫秒左右遇到网络波动之类的情况RTT可能飙升到500毫秒甚至1秒更不用说经常发生的线路丢包问题那延迟可能就是几秒几十秒了。
以上描述的问题,虽然同城异区理论上也会遇到,但由于同城异区距离较短,中间经过的线路和设备较少,问题发生的概率会低很多。而且同城异区距离短,即使是搭建多条互联通道,成本也不会太高,而跨城异区距离太远,搭建或者使用多通道的成本会高不少。
跨城异地距离较远带来的网络传输延迟问题,给异地多活架构设计带来了复杂性,如果要做到真正意义上的多活,业务系统需要考虑部署在不同地点的两个机房,在数据短时间不一致的情况下,还能够正常提供业务。这就引入了一个看似矛盾的地方:数据不一致业务肯定不会正常,但跨城异地肯定会导致数据不一致。
如何解决这个问题呢?重点还是在“数据”上,即根据数据的特性来做不同的架构。如果是强一致性要求的数据,例如银行存款余额、支付宝余额等,这类数据实际上是无法做到跨城异地多活的。我们来看一个假设的例子,假如我们做一个互联网金融的业务,用户余额支持跨城异地多活,我们的系统分别部署在广州和北京,那么如果挖掘机挖断光缆后,会出现如下场景:
用户A余额有10000元钱北京和广州机房都是这个数据。
用户A向用户B转了5000元钱这个操作是在广州机房完成的完成后用户A在广州机房的余额是5000元。
由于广州和北京机房网络被挖掘机挖断广州机房无法将余额变动通知北京机房此时北京机房用户A的余额还是10000元。
用户A到北京机房又发起转账此时他看到自己的余额还有10000元于是向用户C转账10000元转账完成后用户A的余额变为0。
用户A到广州机房一看余额怎么还有5000元于是赶紧又发起转账转账5000元给用户D此时广州机房用户A的余额也变为0了。
最终本来余额10000元的用户A却转了20000元出去给其他用户。
对于以上这种假设场景,虽然普通用户很难这样自如地操作,但如果真的这么做,被黑客发现后,后果不堪设想。正因为如此,支付宝等金融相关的系统,对余额这类数据,一般不会做跨城异地的多活架构,而只能采用同城异区这种架构。
而对数据一致性要求不那么高,或者数据不怎么改变,或者即使数据丢失影响也不大的业务,跨城异地多活就能够派上用场了。例如,用户登录(数据不一致时用户重新登录即可)、新闻类网站(一天内的新闻数据变化较少)、微博类网站(丢失用户发布的微博或者评论影响不大),这些业务采用跨城异地多活,能够很好地应对极端灾难的场景。
3.跨国异地
跨国异地指的是业务部署在不同国家的多个机房。相比跨城异地跨国异地的距离就更远了因此数据同步的延时会更长正常情况下可能就有几秒钟了。这种程度的延迟已经无法满足异地多活标准的第一条“正常情况下用户无论访问哪一个地点的业务系统都能够得到正确的业务服务”。例如假设有一个微博类网站分别在中国的上海和美国的纽约都建了机房用户A在上海机房发表了一篇微博此时如果他的一个关注者B用户访问到美国的机房很可能无法看到用户A刚刚发表的微博。虽然跨城异地也会有此类同步延时问题但正常情况下几十毫秒的延时对用户来说基本无感知的而延时达到几秒钟就感觉比较明显了。
因此,跨国异地的“多活”,和跨城异地的“多活”,实际的含义并不完全一致。跨国异地多活的主要应用场景一般有这几种情况:
为不同地区用户提供服务
例如,亚马逊中国是为中国用户服务的,而亚马逊美国是为美国用户服务的,亚马逊中国的用户如果访问美国亚马逊,是无法用亚马逊中国的账号登录美国亚马逊的。
只读类业务做多活
例如,谷歌的搜索业务,由于用户搜索资料时,这些资料都已经存在于谷歌的搜索引擎上面,无论是访问英国谷歌,还是访问美国谷歌,搜索结果基本相同,并且对用户来说,也不需要搜索到最新的实时资料,跨国异地的几秒钟网络延迟,对搜索结果是没有什么影响的。
小结
今天我为你讲了异地多活架构的应用场景和常见架构模式,希望对你有所帮助。
这就是今天的全部内容留一道思考题给你吧假设我们做了前面提到的高可用存储架构中的数据分区备份又通过自动化运维能够保证1分钟就能将全部系统正常启动那是否意味着没有必要做异地多活了

View File

@@ -0,0 +1,205 @@
因收到Google相关通知网站将会择期关闭。相关通知内容
29 异地多活设计4大技巧
专栏上一期我介绍了三种不同类型的异地多活架构,复习一下每个架构的关键点:
同城异区
关键在于搭建高速网络将两个机房连接起来,达到近似一个本地机房的效果。架构设计上可以将两个机房当作本地机房来设计,无须额外考虑。
跨城异地
关键在于数据不一致的情况下,业务不受影响或者影响很小,这从逻辑的角度上来说其实是矛盾的,架构设计的主要目的就是为了解决这个矛盾。
跨国异地
主要是面向不同地区用户提供业务,或者提供只读业务,对架构设计要求不高。
基于这个分析跨城异地多活是架构设计复杂度最高的一种接下来我将介绍跨城异地多活架构设计的一些技巧和步骤今天我们先来看4大技巧掌握这些技巧可以说是完成好设计步骤的前提。
技巧1保证核心业务的异地多活
“异地多活”是为了保证业务的高可用,但很多架构师在考虑这个“业务”时,会不自觉地陷入一个思维误区:我要保证所有业务都能“异地多活”!
假设我们需要做一个“用户子系统”这个子系统负责“注册”“登录”“用户信息”三个业务。为了支持海量用户我们设计了一个“用户分区”的架构即正常情况下用户属于某个主分区每个分区都有其他数据的备份用户用邮箱或者手机号注册路由层拿到邮箱或者手机号后通过Hash计算属于哪个中心然后请求对应的业务中心。基本的架构如下
这样一个系统如果3个业务要同时实现异地多活会发现这些难以解决的问题
注册问题
A中心注册了用户数据还未同步到B中心此时A中心宕机为了支持注册业务多活可以挑选B中心让用户去重新注册。看起来很容易就支持多活了但仔细思考一下会发现这样做会有问题一个手机号只能注册一个账号A中心的数据没有同步过来B中心无法判断这个手机号是否重复如果B中心让用户注册后来A中心恢复了发现数据有冲突怎么解决实际上是无法解决的因为同一个手机号注册的账号不能以后一次注册为准而如果B中心不支持本来属于A中心的业务进行注册注册业务的多活又成了空谈。
如果我们修改业务规则,允许一个手机号注册多个账号不就可以了吗?
这样做是不可行的,类似一个手机号只能注册一个账号这种规则,是核心业务规则,修改核心业务规则的代价非常大,几乎所有的业务都要重新设计,为了架构设计去改变业务规则(而且是这么核心的业务规则)是得不偿失的。
用户信息问题
用户信息的修改和注册有类似的问题即A、B两个中心在异常的情况下都修改了用户信息如何处理冲突
由于用户信息并没有账号那么关键一种简单的处理方式是按照时间合并即最后修改的生效。业务逻辑上没问题但实际操作也有一个很关键的“坑”怎么保证多个中心所有机器时间绝对一致在异地多中心的网络下这个是无法保证的即使有时间同步也无法完全保证只要两个中心的时间误差超过1秒数据就可能出现混乱即先修改的反而生效。
还有一种方式是生成全局唯一递增ID这个方案的成本很高因为这个全局唯一递增ID的系统本身又要考虑异地多活同样涉及数据一致性和冲突的问题。
综合上面的简单分析可以发现,如果“注册”“登录”“用户信息”全部都要支持异地多活,实际上是挺难的,有的问题甚至是无解的。那这种情况下我们应该如何考虑“异地多活”的架构设计呢?答案其实很简单:优先实现核心业务的异地多活架构!
对于这个模拟案例来说“登录”才是最核心的业务“注册”和“用户信息”虽然也是主要业务但并不一定要实现异地多活主要原因在于业务影响不同。对于一个日活1000万的业务来说每天注册用户可能是几万修改用户信息的可能还不到1万但登录用户是1000万很明显我们应该保证登录的异地多活。
对于新用户来说,注册不了的影响并不明显,因为他还没有真正开始使用业务。用户信息修改也类似,暂时修改不了用户信息,对于其业务不会有很大影响。而如果有几百万用户登录不了,就相当于几百万用户无法使用业务,对业务的影响就非常大了:公司的客服热线很快就被打爆,微博、微信上到处都在传业务宕机,论坛里面到处是抱怨的用户,那就是互联网大事件了!
而登录实现“异地多活”恰恰是最简单的因为每个中心都有所有用户的账号和密码信息用户在哪个中心都可以登录。用户在A中心登录A中心宕机后用户到B中心重新登录即可。
如果某个用户在A中心修改了密码此时数据还没有同步到B中心用户到B中心登录是无法登录的这个怎么处理这个问题其实就涉及另外一个设计技巧了我卖个关子稍后再谈。
技巧2保证核心数据最终一致性
异地多活本质上是通过异地的数据冗余,来保证在极端异常的情况下业务也能够正常提供给用户,因此数据同步是异地多活架构设计的核心。但大部分架构师在考虑数据同步方案时,会不知不觉地陷入完美主义误区:我要所有数据都实时同步!
数据冗余是要将数据从A地同步到B地从业务的角度来看是越快越好最好和本地机房一样的速度最好。但让人头疼的问题正在这里异地多活理论上就不可能很快因为这是物理定律决定的我在上一期已有说明
因此异地多活架构面临一个无法彻底解决的矛盾:业务上要求数据快速同步,物理上正好做不到数据快速同步,因此所有数据都实时同步,实际上是一个无法达到的目标。
既然是无法彻底解决的矛盾,那就只能想办法尽量减少影响。有几种方法可以参考:
尽量减少异地多活机房的距离,搭建高速网络
这和我上一期讲到的同城异区架构类似,但搭建跨城异地的高速网络成本远远超过同城异区的高速网络,成本巨大,一般只有巨头公司才能承担。
尽量减少数据同步,只同步核心业务相关的数据
简单来说就是不重要的数据不同步,同步后没用的数据不同步,只同步核心业务相关的数据。
以前面的“用户子系统”为例用户登录所产生的token或者session信息数据量很大但其实并不需要同步到其他业务中心因为这些数据丢失后重新登录就可以再次获取了。
这时你可能会想到:这些数据丢失后要求用户重新登录,影响用户体验!
确实如此,毕竟需要用户重新输入账户和密码信息,或者至少要弹出登录界面让用户点击一次,但相比为了同步所有数据带来的代价,这个影响完全可以接受。为什么这么说呢,还是卖个关子我会在后面分析。
保证最终一致性,不保证实时一致性
最终一致性就是[专栏第23期]在介绍CAP理论时提到的BASE理论即业务不依赖数据同步的实时性只要数据最终能一致即可。例如A机房注册了一个用户业务上不要求能够在50毫秒内就同步到所有机房正常情况下要求5分钟同步到所有机房即可异常情况下甚至可以允许1小时或者1天后能够一致。
最终一致性在具体实现时还需要根据不同的数据特征进行差异化的处理以满足业务需要。例如对“账号”信息来说如果在A机房新注册的用户5分钟内正好跑到B机房了此时B机房还没有这个用户的信息为了保证业务的正确B机房就需要根据路由规则到A机房请求数据。
而对“用户信息”来说5分钟后同步也没有问题也不需要采取其他措施来弥补但还是会影响用户体验即用户看到了旧的用户信息这个问题怎么解决呢好像又是一个解决不了的问题和前面我留下的两个问题一起在最后我来给出答案。
技巧3采用多种手段同步数据
数据同步是异地多活架构设计的核心幸运的是基本上存储系统本身都会有同步的功能。例如MySQL的主备复制、Redis的Cluster功能、Elasticsearch的集群功能。这些系统本身的同步功能已经比较强大能够直接拿来就用但这也无形中将我们引入了一个思维误区只使用存储系统的同步功能
既然说存储系统本身就有同步功能,而且同步功能还很强大,为何说只使用存储系统是一个思维误区呢?因为虽然绝大部分场景下,存储系统本身的同步功能基本上也够用了,但在某些比较极端的情况下,存储系统本身的同步功能可能难以满足业务需求。
以MySQL为例MySQL 5.1版本的复制是单线程的复制在网络抖动或者大量数据同步时经常发生延迟较长的问题短则延迟十几秒长则可能达到十几分钟。而且即使我们通过监控的手段知道了MySQL同步时延较长也难以采取什么措施只能干等。
Redis又是另外一个问题Redis 3.0之前没有Cluster功能只有主从复制功能而为了设计上的简单Redis 2.8之前的版本,主从复制有一个比较大的隐患:从机宕机或者和主机断开连接都需要重新连接主机,重新连接主机都会触发全量的主从复制。这时主机会生成内存快照,主机依然可以对外提供服务,但是作为读的从机,就无法提供对外服务了,如果数据量大,恢复的时间会相当长。
综合上面的案例可以看出,存储系统本身自带的同步功能,在某些场景下是无法满足业务需要的。尤其是异地多机房这种部署,各种各样的异常情况都可能出现,当我们只考虑存储系统本身的同步功能时,就会发现无法做到真正的异地多活。
解决的方案就是拓开思路,避免只使用存储系统的同步功能,可以将多种手段配合存储系统的同步来使用,甚至可以不采用存储系统的同步方案,改用自己的同步方案。
还是以前面的“用户子系统”为例,我们可以采用如下几种方式同步数据:
消息队列方式
对于账号数据,由于账号只会创建,不会修改和删除(假设我们不提供删除功能),我们可以将账号数据通过消息队列同步到其他业务中心。
二次读取方式
某些情况下可能出现消息队列同步也延迟了用户在A中心注册然后访问B中心的业务此时B中心本地拿不到用户的账号数据。为了解决这个问题B中心在读取本地数据失败时可以根据路由规则再去A中心访问一次这就是所谓的二次读取第一次读取本地本地失败后第二次读取对端这样就能够解决异常情况下同步延迟的问题。
存储系统同步方式
对于密码数据由于用户改密码频率较低而且用户不可能在1秒内连续改多次密码所以通过数据库的同步机制将数据复制到其他业务中心即可用户信息数据和密码类似。
回源读取方式
对于登录的session数据由于数据量很大我们可以不同步数据但当用户在A中心登录后然后又在B中心登录B中心拿到用户上传的session id后根据路由判断session属于A中心直接去A中心请求session数据即可反之亦然A中心也可以到B中心去获取session数据。
重新生成数据方式
对于“回源读取”场景如果异常情况下A中心宕机了B中心请求session数据失败此时就只能登录失败让用户重新在B中心登录生成新的session数据。
注意:以上方案仅仅是示意,实际的设计方案要比这个复杂一些,还有很多细节要考虑。
综合上述的各种措施,最后“用户子系统”同步方式整体如下:
技巧4只保证绝大部分用户的异地多活
前面我在给出每个思维误区对应的解决方案时留下了几个小尾巴某些场景下我们无法保证100%的业务可用性,总是会有一定的损失。例如,密码不同步导致无法登录、用户信息不同步导致用户看到旧的信息等,这个问题怎么解决呢?
其实这个问题涉及异地多活架构设计中一个典型的思维误区我要保证业务100%可用但极端情况下就是会丢一部分数据就是会有一部分数据不能同步有没有什么巧妙能做到100%可用呢?
很遗憾答案是没有异地多活也无法保证100%的业务可用这是由物理规律决定的光速和网络的传播速度、硬盘的读写速度、极端异常情况的不可控等都是无法100%解决的。所以针对这个思维误区我的答案是“忍”也就是说我们要忍受这一小部分用户或者业务上的损失否则本来想为了保证最后的0.01%的用户的可用性做一个完美方案结果却发现99.99%的用户都保证不了了。
对于某些实时强一致性的业务实际上受影响的用户会更多甚至可能达到1/3的用户。以银行转账这个业务为例假设小明在北京XX银行开了账号如果小明要转账一定要北京的银行业务中心才可用否则就不允许小明自己转账。如果不这样的话假设在北京和上海两个业务中心实现了实时转账的异地多活某些异常情况下就可能出现小明只有1万元存款他在北京转给了张三1万元然后又到上海转给了李四1万元两次转账都成功了。这种漏洞如果被人利用后果不堪设想。
当然针对银行转账这个业务虽然无法做到“实时转账”的异地多活但可以通过特殊的业务手段让转账业务也能实现异地多活。例如转账业务除了“实时转账”外还提供“转账申请”业务即小明在上海业务中心提交转账请求但上海的业务中心并不立即转账而是记录这个转账请求然后后台异步发起真正的转账操作如果此时北京业务中心不可用转账请求就可以继续等待重试假设等待2个小时后北京业务中心恢复了此时上海业务中心去请求转账发现余额不够这个转账请求就失败了。小明再登录上来就会看到转账申请失败原因是“余额不足”。
不过需要注意的是“转账申请”的这种方式虽然有助于实现异地多活,但其实还是牺牲了用户体验的,对于小明来说,本来一次操作的事情,需要分为两次:一次提交转账申请,另外一次是要确认是否转账成功。
虽然我们无法做到100%可用性,但并不意味着我们什么都不能做,为了让用户心里更好受一些,我们可以采取一些措施进行安抚或者补偿,例如:
挂公告
说明现在有问题和基本的问题原因,如果不明确原因或者不方便说出原因,可以发布“技术哥哥正在紧急处理”这类比较轻松和有趣的公告。
事后对用户进行补偿
例如,送一些业务上可用的代金券、小礼包等,减少用户的抱怨。
补充体验
对于为了做异地多活而带来的体验损失,可以想一些方法减少或者规避。以“转账申请”为例,为了让用户不用确认转账申请是否成功,我们可以在转账成功或者失败后直接给用户发个短信,告诉他转账结果,这样用户就不用时不时地登录系统来确认转账是否成功了。
核心思想
异地多活设计的理念可以总结为一句话:采用多种手段,保证绝大部分用户的核心业务异地多活!
小结
今天我为你讲了异地多活的设计技巧这些技巧是结合CAP、BASE等理论以及我在具体业务实践的经验和思考总结出来的希望对你有所帮助。
这就是今天的全部内容留一道思考题给你吧异地多活的4大技巧需要结合业务进行分析取舍这样没法通用如果底层存储采用OceanBase这种分布式强一致性的数据存储系统是否就可以做到和业务无关的异地多活

View File

@@ -0,0 +1,189 @@
因收到Google相关通知网站将会择期关闭。相关通知内容
30 异地多活设计4步走
上一期基于异地多活架构设计复杂度最高的“跨城异地”我结合自己的经验总结了异地多活设计的4个技巧及其核心思想我认为掌握这些技巧是进入具体设计步骤的前提。
今天在掌握这4大技巧的基础上我来讲讲跨城异地多活架构设计的4个步骤。
第1步业务分级
按照一定的标准将业务进行分级,挑选出核心的业务,只为核心业务设计异地多活,降低方案整体复杂度和实现成本。
常见的分级标准有下面几种:
访问量大的业务
以用户管理系统为例,业务包括登录、注册、用户信息管理,其中登录的访问量肯定是最大的。
核心业务
以QQ为例QQ的主场景是聊天QQ空间虽然也是重要业务但和聊天相比重要性就会低一些如果要从聊天和QQ空间两个业务里面挑选一个做异地多活那明显聊天要更重要当然此类公司如腾讯应该是两个都实现了异地多活的
产生大量收入的业务
同样以QQ为例聊天可能很难为腾讯带来收益因为聊天没法插入广告而QQ空间反而可能带来更多收益因为QQ空间可以插入很多广告因此如果从收入的角度来看QQ空间做异地多活的优先级反而高于QQ聊天了。
以我们一直在举例的用户管理系统为例,“登录”业务符合“访问量大的业务”和“核心业务”这两条标准,因此我们将登录业务作为核心业务。
第2步数据分类
挑选出核心业务后,需要对核心业务相关的数据进一步分析,目的在于识别所有的数据及数据特征,这些数据特征会影响后面的方案设计。
常见的数据特征分析维度有:
数据量
这里的数据量包括总的数据量和新增、修改、删除的量。对异地多活架构来说,新增、修改、删除的数据就是可能要同步的数据,数据量越大,同步延迟的几率越高,同步方案需要考虑相应的解决方案。
唯一性
唯一性指数据是否要求多个异地机房产生的同类数据必须保证唯一。例如用户ID如果两个机房的两个不同用户注册后生成了一样的用户ID这样业务上就出错了。
数据的唯一性影响业务的多活设计,如果数据不需要唯一,那就说明两个地方都产生同类数据是可能的;如果数据要求必须唯一,要么只能一个中心点产生数据,要么需要设计一个数据唯一生成的算法。
实时性
实时性指如果在A机房修改了数据要求多长时间必须同步到B机房实时性要求越高对同步的要求越高方案越复杂。
可丢失性
可丢失性指数据是否可以丢失。例如写入A机房的数据还没有同步到B机房此时A机房机器宕机会导致数据丢失那这部分丢失的数据是否对业务会产生重大影响。
例如登录过程中产生的session数据就是可丢失的因为用户只要重新登录就可以生成新的session而用户ID数据是不可丢失的丢失后用户就会失去所有和用户ID相关的数据例如用户的好友、用户的钱等。
可恢复性
可恢复性指数据丢失后,是否可以通过某种手段进行恢复,如果数据可以恢复,至少说明对业务的影响不会那么大,这样可以相应地降低异地多活架构设计的复杂度。
例如,用户的微博丢失后,用户重新发一篇一模一样的微博,这个就是可恢复的;或者用户密码丢失,用户可以通过找回密码来重新设置一个新密码,这也算是可以恢复的;而用户账号如果丢失,用户无法登录系统,系统也无法通过其他途径来恢复这个账号,这就是不可恢复的数据。
我们同样以用户管理系统的登录业务为例,简单分析如下表所示。
第3步数据同步
确定数据的特点后,我们可以根据不同的数据设计不同的同步方案。常见的数据同步方案有:
存储系统同步
这是最常用也是最简单的同步方式。例如使用MySQL的数据主从数据同步、主主数据同步。
这类数据同步的优点是使用简单因为几乎主流的存储系统都会有自己的同步方案缺点是这类同步方案都是通用的无法针对业务数据特点做定制化的控制。例如无论需要同步的数据量有多大MySQL都只有一个同步通道。因为要保证事务性一旦数据量比较大或者网络有延迟则同步延迟就会比较严重。
消息队列同步
采用独立消息队列进行数据同步常见的消息队列有Kafka、ActiveMQ、RocketMQ等。
消息队列同步适合无事务性或者无时序性要求的数据。例如用户账号两个用户先后注册了账号A和B如果同步时先把B同步到异地机房再同步A到异地机房业务上是没有问题的。而如果是用户密码用户先改了密码为m然后改了密码为n同步时必须先保证同步m到异地机房再同步n到异地机房如果反过来同步后用户的密码就不对了。因此对于新注册的用户账号我们可以采用消息队列同步了而对于用户密码就不能采用消息队列同步了。
重复生成
数据不同步到异地机房每个机房都可以生成数据这个方案适合于可以重复生成的数据。例如登录产生的cookie、session数据、缓存数据等。
我们同样以用户管理系统的登录业务为例,针对不同的数据特点设计不同的同步方案,如下表所示。
第4步异常处理
无论数据同步方案如何设计,一旦出现极端异常的情况,总是会有部分数据出现异常的。例如,同步延迟、数据丢失、数据不一致等。异常处理就是假设在出现这些问题时,系统将采取什么措施来应对。异常处理主要有以下几个目的:
问题发生时,避免少量数据异常导致整体业务不可用。
问题恢复后,将异常的数据进行修正。
对用户进行安抚,弥补用户损失。
常见的异常处理措施有这几类:
1.多通道同步
多通道同步的含义是采取多种方式来进行数据同步,其中某条通道故障的情况下,系统可以通过其他方式来进行同步,这种方式可以应对同步通道处故障的情况。
以用户管理系统中的用户账号数据为例我们的设计方案一开始挑选了消息队列的方式进行同步考虑异常情况下消息队列同步通道可能中断也可能延迟很严重为了保证新注册账号能够快速同步到异地机房我们再增加一种MySQL同步这种方式作为备份。这样针对用户账号数据同步系统就有两种同步方式MySQL主从同步和消息队列同步。除非两个通道同时故障否则用户账号数据在其中一个通道异常的情况下能够通过另外一个通道继续同步到异地机房如下图所示。
多通道同步设计的方案关键点有:
一般情况下,采取两通道即可,采取更多通道理论上能够降低风险,但付出的成本也会增加很多。
数据库同步通道和消息队列同步通道不能采用相同的网络连接,否则一旦网络故障,两个通道都同时故障;可以一个走公网连接,一个走内网连接。
需要数据是可以重复覆盖的,即无论哪个通道先到哪个通道后到,最终结果是一样的。例如,新建账号数据就符合这个标准,而密码数据则不符合这个标准。
2.同步和访问结合
这里的访问指异地机房通过系统的接口来进行数据访问。例如业务部署在异地两个机房A和BB机房的业务系统通过接口来访问A机房的系统获取账号信息如下图所示。
同步和访问结合方案的设计关键点有:
接口访问通道和数据库同步通道不能采用相同的网络连接,不能让数据库同步和接口访问都走同一条网络通道,可以采用接口访问走公网连接,数据库同步走内网连接这种方式。
数据有路由规则可以根据数据来推断应该访问哪个机房的接口来读取数据。例如有3个机房A、B、CB机房拿到一个不属于B机房的数据后需要根据路由规则判断是访问A机房接口还是访问C机房接口。
由于有同步通道,优先读取本地数据,本地数据无法读取到再通过接口去访问,这样可以大大降低跨机房的异地接口访问数量,适合于实时性要求非常高的数据。
3.日志记录
日志记录主要用于用户故障恢复后对数据进行恢复,其主要方式是每个关键操作前后都记录相关一条日志,然后将日志保存在一个独立的地方,当故障恢复后,拿出日志跟数据进行对比,对数据进行修复。
为了应对不同级别的故障,日志保存的要求也不一样,常见的日志保存方式有:
服务器上保存日志,数据库中保存数据,这种方式可以应对单台数据库服务器故障或者宕机的情况。
本地独立系统保存日志,这种方式可以应对某业务服务器和数据库同时宕机的情况。例如,服务器和数据库部署在同一个机架,或者同一个电源线路上,就会出现服务器和数据库同时宕机的情况。
日志异地保存,这种方式可以应对机房宕机的情况。
上面不同的日志保存方式,应对的故障越严重,方案本身的复杂度和成本就会越高,实际选择时需要综合考虑成本和收益情况。
4.用户补偿
无论采用什么样的异常处理措施都只能最大限度地降低受到影响的范围和程度无法完全做到没有任何影响。例如双同步通道有可能同时出现故障、日志记录方案本身日志也可能丢失。因此无论多么完美的方案故障的场景下总是可能有一小部分用户业务上出问题系统无法弥补这部分用户的损失。但我们可以采用人工的方式对用户进行补偿弥补用户损失培养用户的忠诚度。简单来说系统的方案是为了保证99.99%的用户在故障的场景下业务不受影响人工的补偿是为了弥补0.01%的用户的损失。
常见的补偿措施有送用户代金券、礼包、礼品、红包等有时为了赢得用户口碑付出的成本可能还会比较大但综合最终的收益来看还是很值得的。例如暴雪《炉石传说》2017年回档故障暴雪给每个用户大约价值人民币200元的补偿结果玩家都求暴雪再来一次回档形象地说明了玩家对暴雪补偿的充分认可。
只要在2017年1月18日18点之前登录过国服《炉石传说》的玩家均可获得与25卡牌包等值的补偿具体如下
1000游戏金币
15个卡牌包经典卡牌包x5、上古之神的低语卡牌包x5、龙争虎斗加基森卡牌包x5。
小结
今天我为你讲了异地多活设计的具体步骤,分别是业务分级、数据分类、数据同步和异常处理,希望对你有所帮助。
这就是今天的全部内容留一道思考题给你吧业务分级讨论的时候产品说A也很重要因为影响用户使用B也很重要因为影响公司收入C也很重要因为会导致客户投诉……这种情况下我们该如何处理业务分级

View File

@@ -0,0 +1,235 @@
因收到Google相关通知网站将会择期关闭。相关通知内容
31 如何应对接口级的故障?
你好,我是华仔。
前几讲我介绍了异地多活方案。它主要用来应对系统级的故障,例如机器宕机、机房故障和网络故障等问题。这些系统级的故障虽然影响很大,但发生概率较小。在实际业务运行过程中,还有另外一种故障影响可能没有那么大,但发生的概率较高,这就是今天我要跟你聊的接口级的故障。
接口级故障的典型表现就是,系统并没有宕机、网络也没有中断,但业务却出现问题了,例如业务响应缓慢、大量访问超时和大量访问出现异常(给用户弹出提示“无法连接数据库”)。
这类问题的主要原因在于系统压力太大、负载太高,导致无法快速处理业务请求,由此引发更多的后续问题。最常见的情况就是,数据库慢查询将数据库的服务器资源耗尽,导致读写超时,业务读写数据库时要么无法连接数据库、要么超时,最终用户看到的现象就是访问很慢,一会儿访问抛出异常,一会儿访问又是正常结果。
如果进一步探究,导致接口级故障的原因可以分为两大类:
内部原因包括程序bug导致死循环某个接口导致数据库慢查询程序逻辑不完善导致耗尽内存等。
外部原因:包括黑客攻击,促销或者抢购引入了超出平时几倍甚至几十倍的用户,第三方系统大量请求,第三方系统响应缓慢等。
解决接口级故障的核心思想和异地多活基本类似,都是优先保证核心业务和优先保证绝大部分用户。常见的应对方法有四种,降级、熔断、限流和排队,下面我会一一讲解。
1. 降级
降级指系统将某些业务或者接口的功能降低,可以是只提供部分功能,也可以是完全停掉所有功能。
例如论坛可以降级为只能看帖子不能发帖子也可以降级为只能看帖子和评论不能发评论而App的日志上传接口可以完全停掉一段时间这段时间内App都不能上传日志。
降级的核心思想就是丢车保帅,优先保证核心业务。
例如对于论坛来说90%的流量是看帖子那我们就优先保证看帖的功能对于一个App来说日志上传接口只是一个辅助的功能故障时完全可以停掉。
常见的实现降级的方式有两种:
1.1 系统后门降级
简单来说就是系统预留了后门用于降级操作。例如系统提供一个降级URL当访问这个URL时就相当于执行降级指令具体的降级指令通过URL的参数传入即可。这种方案有一定的安全隐患所以也会在URL中加入密码这类安全措施。
系统后门降级的方式实现成本低,但主要缺点是如果服务器数量多,需要一台一台去操作,效率比较低,这在故障处理争分夺秒的场景下是比较浪费时间的。
1.2 独立降级系统
为了解决系统后门降级方式的缺点,我们可以将降级操作独立到一个单独的系统中,实现复杂的权限管理、批量操作等功能。
其基本架构如下:
2. 熔断
熔断是指按照规则停掉外部接口的访问,防止某些外部接口故障导致自己的系统处理能力急剧下降或者出故障。
熔断和降级是两个比较容易混淆的概念,因为单纯从名字上看,好像都有禁止某个功能的意思。但它们的内涵是不同的,因为降级的目的是应对系统自身的故障,而熔断的目的是应对依赖的外部系统故障的情况。
假设一个这样的场景A服务的X功能依赖B服务的某个接口当B服务的接口响应很慢的时候A服务的X功能响应肯定也会被拖慢进一步导致A服务的线程都被卡在X功能处理上于是A服务的其他功能都会被卡住或者响应非常慢。
这时就需要熔断机制了A服务不再请求B服务的这个接口A服务内部只要发现是请求B服务的这个接口就立即返回错误从而避免A服务整个被拖慢甚至拖死。
实现熔断机制有两个关键点:
一是需要有一个统一的API调用层由API调用层来进行采样或者统计。如果接口调用散落在代码各处就没法进行统一处理了。
二是阈值的设计例如1分钟内30%的请求响应时间超过1秒就熔断这个策略中的“1分钟”“30%”“1秒”都对最终的熔断效果有影响。实践中一般都是先根据分析确定阈值然后上线观察效果再进行调优。
3. 限流
降级是从系统功能优先级的角度考虑如何应对故障,而限流则是从用户访问压力的角度来考虑如何应对故障。限流指只允许系统能够承受的访问量进来,超出系统访问能力的请求将被丢弃。
虽然“丢弃”这个词听起来让人不太舒服,但保证一部分请求能够正常响应,总比全部请求都不能响应要好得多。
限流一般都是系统内实现的,常见的限流方式可以分为两类:基于请求限流和基于资源限流。
3.1 基于请求限流
基于请求限流指从外部访问的请求角度考虑限流,常见的方式有两种。
第一种是限制总量也就是限制某个指标的累积上限常见的是限制当前系统服务的用户总量例如某个直播间限制总用户数上限为100万超过100万后新的用户无法进入某个抢购活动商品数量只有100个限制参与抢购的用户上限为1万个1万以后的用户直接拒绝。
第二种是限制时间量也就是限制一段时间内某个指标的上限例如1分钟内只允许10000个用户访问每秒请求峰值最高为10万。
无论是限制总量还是限制时间量共同的特点都是实现简单但在实践中面临的主要问题是比较难以找到合适的阈值。例如系统设定了1分钟10000个用户但实际上6000个用户的时候系统就扛不住了或者达到1分钟10000用户后其实系统压力还不大但此时已经开始丢弃用户访问了。
即使找到了合适的阈值基于请求限流还面临硬件相关的问题。例如一台32核的机器和64核的机器处理能力差别很大阈值是不同的可能有的技术人员以为简单根据硬件指标进行数学运算就可以得出来实际上这样是不可行的64核的机器比32核的机器业务处理性能并不是2倍的关系可能是1.5倍甚至可能是1.1倍。
为了找到合理的阈值,通常情况下可以采用性能压测来确定阈值,但性能压测也存在覆盖场景有限的问题,可能出现某个性能压测没有覆盖的功能导致系统压力很大;另外一种方式是逐步优化:先设定一个阈值然后上线观察运行情况,发现不合理就调整阈值。
基于上述的分析,根据阈值来限制访问量的方式更多的适应于业务功能比较简单的系统,例如负载均衡系统、网关系统、抢购系统等。
3.2 基于资源限流
基于请求限流是从系统外部考虑的,而基于资源限流是从系统内部考虑的,也就是找到系统内部影响性能的关键资源,对其使用上限进行限制。常见的内部资源包括连接数、文件句柄、线程数和请求队列等。
例如采用Netty来实现服务器每个进来的请求都先放入一个队列业务线程再从队列读取请求进行处理队列长度最大值为10000队列满了就拒绝后面的请求也可以根据CPU的负载或者占用率进行限流当CPU的占用率超过80%的时候就开始拒绝新的请求。
基于资源限流相比基于请求限流能够更加有效地反映当前系统的压力,但实际设计时也面临两个主要的难点:如何确定关键资源,以及如何确定关键资源的阈值。
通常情况下,这也是一个逐步调优的过程:设计的时候先根据推断选择某个关键资源和阈值,然后测试验证,再上线观察,如果发现不合理,再进行优化。
限流算法
为了更好地实现前面描述的各种限流方式,通常情况下我们会基于限流算法来设计方案。常见的限流算法有两大类四小类,它们的实现原理和优缺点各不相同,在实际设计的时候需要根据业务场景来选择。
1时间窗
第一大类是时间窗算法,它会限制一定时间窗口内的请求量或者资源消耗量,根据实现方式又可以细分为“固定时间窗”和“滑动时间窗”。
固定时间窗
固定时间窗算法的实现原理是,统计固定时间周期内的请求量或者资源消耗量,超过限额就会启动限流,如下图所示:
它的优点是实现简单缺点是存在临界点问题。例如上图中的红蓝两点只间隔了短短10秒期间的请求数却已经达到200超过了算法规定的限额1分钟内处理100。但是因为这些请求分别来自两个统计窗口从单个窗口来看还没有超出限额所以并不会启动限流结果可能导致系统因为压力过大而挂掉。
滑动时间窗
为了解决临界点问题,滑动时间窗算法应运而生,它的实现原理是,两个统计周期部分重叠,从而避免短时间内的两个统计点分属不同的时间窗的情况,如下图所示:
总体上来看,滑动时间窗的限流效果要比固定时间窗更好,但是实现也会稍微复杂一些。
2桶算法
第二大类是桶算法,用一个虚拟的“桶”来临时存储一些东西。根据桶里面放的东西,又可以细分为“漏桶”和“令牌桶”。
漏桶
漏桶算法的实现原理是,将请求放入“桶”(消息队列等),业务处理单元(线程、进程和应用等)从桶里拿请求处理,桶满则丢弃新的请求,如下图所示:
我们可以看到漏桶算法的三个关键实现点:
流入速率不固定可能瞬间流入非常多的请求例如0点签到、整点秒杀。
匀速(极速)流出:这是理解漏桶算法的关键,也就是说即使大量请求进入了漏桶,但是从漏桶流出的速度是匀速的,速度的最大值就是系统的极限处理速度(对应图中的“极速”)。这样就保证了系统在收到海量请求的时候不被压垮,这是第一层的保护措施。需要注意的是:如果漏桶没有堆积,那么流出速度就等于流入速度,这个时候流出速度就不是匀速的。
桶满则丢弃请求这是第二层保护措施也就是说漏桶不是无限容量而是有限容量例如漏桶最多存储100万个请求桶满了则直接丢弃后面的请求。
漏桶算法的技术本质是总量控制,桶大小是设计关键,具体的优缺点如下:
突发大量流量时丢弃的请求较少,因为漏桶本身有缓存请求的作用。
桶大小动态调整比较困难(例如 Java BlockingQueue需要不断的尝试才能找到符合业务需求的最佳桶大小。
无法精确控制流出速度,也就是业务的处理速度。
漏桶算法主要适用于瞬时高并发流量的场景例如刚才提到的0点签到、整点秒杀等。在短短几分钟内涌入大量请求时为了更好的业务效果和用户体验即使处理慢一些也要做到尽量不丢弃用户请求。
令牌桶算法
令牌桶算法和漏桶算法的不同之处在于,桶中放入的不是请求,而是“令牌”,这个令牌就是业务处理前需要拿到的“许可证”。也就是说,当系统收到一个请求时,先要到令牌桶里面拿“令牌”,拿到令牌才能进一步处理,拿不到就要丢弃请求。
它的实现原理是如下图所示:
我们可以看到令牌桶算法的三个关键设计点:
有一个处理单元往桶里面放令牌,放的速率是可以控制的。
桶里面可以累积一定数量的令牌,当突发流量过来的时候,因为桶里面有累积的令牌,此时的业务处理速度会超过令牌放入的速度。
如果令牌不足,即使系统有能力处理,也会丢弃请求。
令牌桶算法的技术本质是速率控制,令牌产生的速率是设计关键,具体的优缺点如下:
可以动态调整处理速率,实现更加灵活。
突发大量流量的时候可能丢弃很多请求,因为令牌桶不能累积太多令牌。
实现相对复杂。
令牌桶算法主要适用于两种典型的场景一种是需要控制访问第三方服务的速度防止把下游压垮例如支付宝需要控制访问银行接口的速率另一种是需要控制自己的处理速度防止过载例如压测结果显示系统最大处理TPS是100那么就可以用令牌桶来限制最大的处理速度。
刚才介绍漏桶算法的时候我提到漏桶算法可以应对瞬时高并发流量,现在介绍令牌桶算法的时候,我又说令牌桶允许突发流量。
你可能会问,这两种说法好像差不多啊,它们到底有什么区别,到底谁更适合做秒杀呢?
其实令牌桶的“允许突发”实际上只是“允许一定程度的突发”比如系统处理能力是每秒100 TPS突发到120 TPS是可以的但如果突发到1000 TPS的话系统大概率就被压垮了。所以处理秒杀时高并发流量还是得用漏桶算法。
令牌桶的算法原本是用于网络设备控制传输速度的,而且它控制的目的是保证一段时间内的平均传输速度。之所以说令牌桶适合突发流量,是指在网络传输的时候,可以允许某段时间内(一般就几秒)超过平均传输速率,这在网络环境下常见的情况就是“网络抖动”。
但这个短时间的突发流量并不会导致雪崩效应,网络设备也能够处理得过来。对应到令牌桶应用到业务处理的场景,就要求即使有突发流量来了,系统自己或者下游系统要真的能够处理的过来,否则令牌桶允许突发流量进来,结果系统或者下游处理不了,那还是会被压垮。
因此令牌桶在实际设计的时候桶大小不能像漏桶那样设计很大需要根据系统的处理能力来进行仔细的估算。例如漏桶算法的桶容量可以设计为100万但是一个每秒30 TPS的令牌桶桶的容量可能只能设计成40左右。海外有的银行给移动钱包提供的接口TPS上限是30压测到了40就真的挂了。
4. 排队
排队实际上是限流的一个变种限流是直接拒绝用户排队是让用户等待一段时间全世界最有名的排队当属12306网站排队了。
排队虽然没有直接拒绝用户,但用户等了很长时间后进入系统,体验并不一定比限流好。
由于排队需要临时缓存大量的业务请求单个系统内部无法缓存这么多数据一般情况下排队需要用独立的系统去实现例如使用Kafka这类消息队列来缓存用户请求。
下图是1号店的“双11”秒杀排队系统架构
备注图片参考刘霄晖《1 号店 11.11:秒杀排队系统设计理念》)
它的基本实现摘录如下:
【排队模块】
负责接收用户的抢购请求,将请求以先入先出的方式保存下来。每一个参加秒杀活动的商品保存一个队列,队列的大小可以根据参与秒杀的商品数量(或加点余量)自行定义。
【调度模块】
负责排队模块到服务模块的动态调度,不断检查服务模块,一旦处理能力有空闲,就从排队队列头上把用户访问请求调入服务模块,并负责向服务模块分发请求。这里调度模块扮演一个中介的角色,但不只是传递请求而已,它还担负着调节系统处理能力的重任。我们可以根据服务模块的实际处理能力,动态调节向排队系统拉取请求的速度。
【服务模块】
负责调用真正业务来处理服务,并返回处理结果,调用排队模块的接口回写业务处理结果。
小结
今天我为你讲了接口级故障的四种应对方法,分别是降级、熔断、限流和排队,希望对你有所帮助。
这就是今天的全部内容,留一道思考题给你吧,如果你来设计一个整点限量秒杀系统,包括登录、抢购、支付(依赖支付宝)等功能,你会如何设计接口级的故障应对手段?

View File

@@ -0,0 +1,140 @@
因收到Google相关通知网站将会择期关闭。相关通知内容
32 可扩展架构的基本思想和模式
软件系统与硬件和建筑系统最大的差异在于软件是可扩展的一个硬件生产出来后就不会再进行改变、一个建筑完工后也不会再改变其整体结构。例如一颗CPU生产出来后装到一台PC机上不会再返回工厂进行加工以增加新的功能金字塔矗立千年历经风吹雨打但其现在的结构和当时建成完工时的结构并无两样。相比之下软件系统就完全相反如果一个软件系统开发出来后再也没有任何更新和调整反而说明了这套软件系统没有发展、没有生命力。真正有生命力的软件系统都是在不断迭代和发展的典型的如Windows操作系统从Windows 3.0到Windows 95到Windows XP直到现在的Windows 10一直在跟着技术的发展而不断地发展。
今天我们进入架构可扩展模式的学习这部分内容包括分层架构、SOA架构、微服务和微内核等先来聊聊架构的可扩展模式。
软件系统的这种天生和内在的可扩展的特性,既是魅力所在,又是难点所在。魅力体现在我们可以通过修改和扩展,不断地让软件系统具备更多的功能和特性,满足新的需求或者顺应技术发展的趋势。而难点体现在如何以最小的代价去扩展系统,因为很多情况下牵一发动全身,扩展时可能出现到处都要改,到处都要推倒重来的情况。这样做的风险不言而喻:改动的地方越多,投入也越大,出错的可能性也越大。因此,如何避免扩展时改动范围太大,是软件架构可扩展性设计的主要思考点。
可扩展的基本思想
幸运的是,可扩展性架构的设计方法很多,但万变不离其宗,所有的可扩展性架构设计,背后的基本思想都可以总结为一个字:拆!
拆,就是将原本大一统的系统拆分成多个规模小的部分,扩展时只修改其中一部分即可,无须整个系统到处都改,通过这种方式来减少改动范围,降低改动风险。
说起来好像挺简单,毕竟“拆”我们见得太多了。一般情况下,我们要拆一个东西时,都是简单粗暴的。例如,用推土机拆房子、用剪刀拆快递包装、用手撕开包装袋等,反正拆完了这些东西就扔了。但面对软件系统,拆就没那么简单了,因为我们并不是要摧毁一个软件系统,而是要通过拆让软件系统变得更加优美(具备更好的可扩展性)。形象地说,软件系统中的“拆”是建设性的,因此难度要高得多。
按照不同的思路来拆分软件系统,就会得到不同的架构。常见的拆分思路有如下三种。
面向流程拆分:将整个业务流程拆分为几个阶段,每个阶段作为一部分。
面向服务拆分:将系统提供的服务拆分,每个服务作为一部分。
面向功能拆分:将系统提供的功能拆分,每个功能作为一部分。
理解这三种思路的关键就在于如何理解“流程”“服务”“功能”三者的联系和区别。从范围上来看,从大到小依次为:流程>服务>功能,单纯从概念解释可能难以理解,但实际上看几个案例就很清楚了。
我以TCP/IP协议栈为例来说明“流程”“服务”“功能”的区别和联系。TCP/IP协议栈和模型图如下图所示。
流程
对应TCP/IP四层模型因为TCP/IP网络通信流程是应用层 → 传输层 → 网络层 → 物理+数据链路层,不管最上层的应用层是什么,这个流程都不会变。
服务
对应应用层的HTTP、FTP、SMTP等服务HTTP提供Web服务FTP提供文件服务SMTP提供邮件服务以此类推。
功能
每个服务都会提供相应的功能。例如HTTP服务提供GET、POST功能FTP提供上传下载功能SMTP提供邮件发送和收取功能。
我再以一个简单的学生信息管理系统为例(几乎每个技术人员读书时都做过这样一个系统),拆分方式是:
1.面向流程拆分
展示层 → 业务层 → 数据层 → 存储层,各层含义是:
展示层:负责用户页面设计,不同业务有不同的页面。例如,登录页面、注册页面、信息管理页面、安全设置页面等。
业务层:负责具体业务逻辑的处理。例如,登录、注册、信息管理、修改密码等业务。
数据层:负责完成数据访问。例如,增删改查数据库中的数据、记录事件到日志文件等。
存储层负责数据的存储。例如关系型数据库MySQL、缓存系统Memcache等。
最终的架构如下:
2.面向服务拆分
将系统拆分为注册、登录、信息管理、安全设置等服务,最终架构示意图如下:
3.面向功能拆分
每个服务都可以拆分为更多细粒度的功能,例如:
注册服务:提供多种方式进行注册,包括手机号注册、身份证注册、学生邮箱注册三个功能。
登录服务:包括手机号登录、身份证登录、邮箱登录三个功能。
信息管理服务:包括基本信息管理、课程信息管理、成绩信息管理等功能。
安全设置服务:包括修改密码、安全手机、找回密码等功能。
最终架构图如下:
通过学生信息管理系统的案例可以发现,不同的拆分方式,架构图差异很大。但好像无论哪种方式,最终都是可以实现的。既然如此,我们何必费尽心机去选择呢,随便挑选一个不就可以了?
当然不能随便挑,否则架构设计就没有意义了,架构师也就要丢掉饭碗了。原因在于:不同的拆分方式,本质上决定了系统的扩展方式。
可扩展方式
当我们谈可扩展性时,很多同学都会有一个疑惑:就算是不拆分系统,只要在设计和写代码时做好了,同样不会出现到处改的问题啊?例如,在面向服务拆分的案例中,增加“学号注册”,就算是不拆分为服务,也可以控制修改的范围,那为何我们要大费周章地去拆分系统呢?
在一个理想的环境你的团队都是高手每个程序员都很厉害对业务都很熟悉新来的同事很快就知晓所有的细节……那确实不拆分也没有问题。但现实却是团队有菜鸟程序员到底是改A处实现功能还是改B处实现功能完全取决于他觉得哪里容易改有的程序员比较粗心有的程序员某天精神状态不太好新来的同事不知道历史上某行代码为何那么“恶心”而轻易地将其改漂亮了一些……所有的这些问题都可能出现这时候你就会发现合理的拆分能够强制保证即使程序员出错出错的范围也不会太广影响也不会太大。
下面是不同拆分方式应对扩展时的优势。
1.面向流程拆分
扩展时大部分情况只需要修改某一层少部分情况可能修改关联的两层不会出现所有层都同时要修改。例如学生信息管理系统如果我们将存储层从MySQL扩展为同时支持MySQL和Oracle那么只需要扩展存储层和数据层即可展示层和业务层无须变动。
2.面向服务拆分
对某个服务扩展,或者要增加新的服务时,只需要扩展相关服务即可,无须修改所有的服务。同样以学生管理系统为例,如果我们需要在注册服务中增加一种“学号注册”功能,则只需要修改“注册服务”和“登录服务”即可,“信息管理服务”和“安全设置”服务无须修改。
3.面向功能拆分
对某个功能扩展,或者要增加新的功能时,只需要扩展相关功能即可,无须修改所有的服务。同样以学生管理系统为例,如果我们增加“学号注册”功能,则只需要在系统中增加一个新的功能模块,同时修改“登录功能”模块即可,其他功能都不受影响。
不同的拆分方式,将得到不同的系统架构,典型的可扩展系统架构有:
面向流程拆分:分层架构。
面向服务拆分SOA、微服务。
面向功能拆分:微内核架构。
当然,这几个系统架构并不是非此即彼的,而是可以在系统架构设计中进行组合使用的。以学生管理系统为例,我们最终可以这样设计架构:
整体系统采用面向服务拆分中的“微服务”架构,拆分为“注册服务”“登录服务”“信息管理服务”“安全服务”,每个服务是一个独立运行的子系统。
其中的“注册服务”子系统本身又是采用面向流程拆分的分层架构。
“登录服务”子系统采用的是面向功能拆分的“微内核”架构。
专栏后面的内容我将详细阐述每种可扩展架构。
小结
今天我为你讲了可扩展架构的一些基本思想和方式,希望对你有所帮助。
这就是今天的全部内容,留一道思考题给你吧,规则引擎是常用的一种支持可扩展的方式,按照今天的分析,它属于哪一类?

View File

@@ -0,0 +1,185 @@
因收到Google相关通知网站将会择期关闭。相关通知内容
33 传统的可扩展架构模式分层架构和SOA
相比于高性能、高可用架构模式在最近几十年的迅猛发展来说,可扩展架构模式的发展可以说是步履蹒跚,最近几年火热的微服务模式算是可扩展模式发展历史中为数不多的亮点,但这也导致了现在谈可扩展的时候必谈微服务,甚至微服务架构都成了架构设计的银弹,高性能也用微服务、高可用也用微服务,很多时候这样的架构设计看起来高大上,实际上是大炮打蚊子,违背了架构设计的“合适原则”和“简单原则”。
为了帮助你在实践中更好的进行可扩展架构设计我将分别介绍几种可扩展架构模式指出每种架构模式的关键点和优缺点。今天我来介绍传统的可扩展模式包括分层架构和SOA后面还会介绍微服务架构。
分层架构
分层架构是很常见的架构模式它也叫N层架构通常情况下N至少是2层。例如C/S架构、B/S架构。常见的是3层架构例如MVC、MVP架构、4层架构5层架构的比较少见一般是比较复杂的系统才会达到或者超过5层比如操作系统内核架构。
按照分层架构进行设计时,根据不同的划分维度和对象,可以得到多种不同的分层架构。
C/S架构、B/S架构
划分的对象是整个业务系统划分的维度是用户交互即将和用户交互的部分独立为一层支撑用户交互的后台作为另外一层。例如下面是C/S架构结构图。
MVC架构、MVP架构
划分的对象是单个业务子系统划分的维度是职责将不同的职责划分到独立层但各层的依赖关系比较灵活。例如MVC架构中各层之间是两两交互的
逻辑分层架构
划分的对象可以是单个业务子系统也可以是整个业务系统划分的维度也是职责。虽然都是基于职责划分但逻辑分层架构和MVC架构、MVP架构的不同点在于逻辑分层架构中的层是自顶向下依赖的。典型的有操作系统内核架构、TCP/IP架构。例如下面是Android操作系统架构图。
典型的J2EE系统架构也是逻辑分层架构架构图如下
针对整个业务系统进行逻辑分层的架构图如下:
无论采取何种分层维度分层架构设计最核心的一点就是需要保证各层之间的差异足够清晰边界足够明显让人看到架构图后就能看懂整个架构这也是分层不能分太多层的原因。否则如果两个层的差异不明显就会出现程序员小明认为某个功能应该放在A层而程序员老王却认为同样的功能应该放在B层这样会导致分层混乱。如果这样的架构进入实际开发落地则A层和B层就会乱成一锅粥也就失去了分层的意义。
分层架构之所以能够较好地支撑系统扩展本质在于隔离关注点separation of concerns即每个层中的组件只会处理本层的逻辑。比如说展示层只需要处理展示逻辑业务层中只需要处理业务逻辑这样我们在扩展某层时其他层是不受影响的通过这种方式可以支撑系统在某层上快速扩展。例如Linux内核如果要增加一个新的文件系统则只需要修改文件存储层即可其他内核层无须变动。
当然并不是简单地分层就一定能够实现隔离关注点从而支撑快速扩展分层时要保证层与层之间的依赖是稳定的才能真正支撑快速扩展。例如Linux内核为了支撑不同的文件系统格式抽象了VFS文件系统接口架构图如下
如果没有VFS只是简单地将ext2、ext3、reiser等文件系统划为“文件系统层”那么这个分层是达不到支撑可扩展的目的的。因为增加一个新的文件系统后所有基于文件系统的功能都要适配新的文件系统接口而有了VFS后只需要VFS适配新的文件系统接口其他基于文件系统的功能是依赖VFS的不会受到影响。
对于操作系统这类复杂的系统接口本身也可以成为独立的一层。例如我们把VFS独立为一层是完全可以的。而对于一个简单的业务系统接口可能就是Java语言上的几个interface定义这种情况下如果独立为一层看起来可能就比较重了。例如经典的J2EE分层架构中Presentation Layer和Business Layer之间如果硬要拆分一个独立的接口层则显得有点多余了。
分层结构的另外一个特点就是层层传递也就是说一旦分层确定整个业务流程是按照层进行依次传递的不能在层之间进行跳跃。最简单的C/S结构用户必须先使用C层然后C层再传递到S层用户是不能直接访问S层的。传统的J2EE 4层架构收到请求后必须按照下面的方式传递请求
分层结构的这种约束好处在于强制将分层依赖限定为两两依赖降低了整体系统复杂度。例如Business Layer被Presentation Layer依赖自己只依赖Persistence Layer。但分层结构的代价就是冗余也就是说不管这个业务有多么简单每层都必须要参与处理甚至可能每层都写了一个简单的包装函数。我以用户管理系统最简单的一个功能“查看头像”为例。查看头像功能的实现很简单只是显示一张图片而已但按照分层分册架构来实现每层都要写一个简单的函数。比如
Presentation Layer
package layer;
/**
* Created by Liyh on 2017/9/18.
*/
public class AvatarView {
public void displayAvatar(int userId){
String url = AvatarBizz.getAvatarUrl(userId);
//此处省略渲染代码
return;
}
}
Business Layer
package layer;
/**
* Created by Liyh on 2017/9/18.
*/
public class AvatarBizz {
public static String getAvatarUrl(int userId){
return AvatarDao.getAvatarUrl(userId);
}
}
Persistence Layer
package layer;
/**
* Created by Liyh on 2017/9/18.
*/
public class AvatarDao {
public static String getAvatarUrl(int userId) {
//此处省略具体实现代码正常情况下可以从MySQL数据库中通过userId查询头像URL即可
return "http://avatar.csdn.net/B/8/3/1_yah99_wolf.jpg";
}
}
可以看出Business Layer的AvatarBizz类的getAvatarUrl方法和Persistence Layer的AvatarDao类的getAvatarUrl方法名称和参数都一模一样。
既然如此我们是否应该自由选择是否绕过分层的约束呢例如“查看头像”的示例中直接让AvatarView类访问AvatarDao类不就可以减少AvatarBizz的冗余实现了吗
答案是不建议这样做分层架构的优势就体现在通过分层强制约束两两依赖一旦自由选择绕过分层时间一长架构就会变得混乱。例如Presentation Layer直接访问Persistence LayerBusiness Layer直接访问Database Layer这样做就失去了分层架构的意义也导致后续扩展时无法控制受影响范围牵一发动全身无法支持快速扩展。除此以外虽然分层架构的实现在某些场景下看起来有些啰嗦和冗余但复杂度却很低。例如样例中AvatarBizz的getAvatarUrl方法实现起来很简单不会增加太多工作量。
分层架构另外一个典型的缺点就是性能因为每一次业务请求都需要穿越所有的架构分层有一些事情是多余的多少都会有一些性能的浪费。当然这里所谓的性能缺点只是理论上的分析实际上分层带来的性能损失如果放到20世纪80年代可能很明显但到了现在硬件和网络的性能有了质的飞越其实分层模式理论上的这点性能损失在实际应用中绝大部分场景下都可以忽略不计。
SOA
SOA的全称是Service Oriented Architecture中文翻译为“面向服务的架构”诞生于上世纪90年代1996年Gartner的两位分析师Roy W. Schulte和Yefim V. Natis发表了第一个SOA的报告。
2005年Gartner预言到了2008年SOA将成为80%的开发项目的基础。历史证明这个预言并不十分靠谱SOA虽然在很多企业成功推广但没有达到占有绝对优势的地步。SOA更多是在传统企业例如制造业、金融业等落地和推广在互联网行业并没有大规模地实践和推广。互联网行业推行SOA最早的应该是亚马逊得益于杰弗·贝索斯的远见卓识亚马逊内部的系统都以服务的方式构造间接地促使了后来的亚马逊云计算技术的出现。
SOA出现 的背景是企业内部的IT系统重复建设且效率低下主要体现在
企业各部门有独立的IT系统比如人力资源系统、财务系统、销售系统这些系统可能都涉及人员管理各IT系统都需要重复开发人员管理的功能。例如某个员工离职后需要分别到上述三个系统中删除员工的权限。
各个独立的IT系统可能采购于不同的供应商实现技术不同企业自己也不太可能基于这些系统进行重构。
随着业务的发展复杂度越来越高更多的流程和业务需要多个IT系统合作完成。由于各个独立的IT系统没有标准的实现方式例如人力资源系统用Java开发对外提供RPC而财务系统用C#开发对外提供SOAP协议每次开发新的流程和业务都需要协调大量的IT系统同时定制开发效率很低。
为了应对传统IT系统存在的问题SOA提出了3个关键概念。
服务
所有业务功能都是一项服务,服务就意味着要对外提供开放的能力,当其他系统需要使用这项功能时,无须定制化开发。
服务可大可小,可简单也可复杂。例如,人力资源管理可以是一项服务,包括人员基本信息管理、请假管理、组织结构管理等功能;而人员基本信息管理也可以作为一项独立的服务,组织结构管理也可以作为一项独立的服务。到底是划分为粗粒度的服务,还是划分为细粒度的服务,需要根据企业的实际情况进行判断。
ESB
ESB的全称是Enterprise Service Bus中文翻译为“企业服务总线”。从名字就可以看出ESB参考了计算机总线的概念。计算机中的总线将各个不同的设备连接在一起ESB将企业中各个不同的服务连接在一起。因为各个独立的服务是异构的如果没有统一的标准则各个异构系统对外提供的接口是各式各样的。SOA使用ESB来屏蔽异构系统对外提供各种不同的接口方式以此来达到服务间高效的互联互通。
松耦合
松耦合的目的是减少各个服务间的依赖和互相影响。因为采用SOA架构后各个服务是相互独立运行的甚至都不清楚某个服务到底有多少对其他服务的依赖。如果做不到松耦合某个服务一升级依赖它的其他服务全部故障这样肯定是无法满足业务需求的。
但实际上真正做到松耦合并没有那么容易,要做到完全后向兼容,是一项复杂的任务。
典型的SOA架构样例如下
SOA架构是比较高层级的架构设计理念一般情况下我们可以说某个企业采用了SOA的架构来构建IT系统但不会说某个独立的系统采用了SOA架构。例如某企业采用SOA架构将系统分为“人力资源管理服务”“考勤服务”“财务服务”但人力资源管理服务本身通常不会再按照SOA的架构拆分更多服务也不会再使用独立的一套ESB因为这些系统本身可能就是采购的ESB本身也是采购的如果人力资源系统本身重构为多个子服务再部署独立的ESB系统成本很高也没有什么收益。
SOA解决了传统IT系统重复建设和扩展效率低的问题但其本身也引入了更多的复杂性。SOA最广为人诟病的就是ESBESB需要实现与各种系统间的协议转换、数据转换、透明的动态路由等功能。例如下图中ESB将JSON转换为Java摘自《Microservices vs. Service-Oriented Architecture》
下图中ESB将REST协议转换为RMI和AMQP两个不同的协议
ESB虽然功能强大但现实中的协议有很多种如JMS、WS、HTTP、RPC等数据格式也有很多种如XML、JSON、二进制、HTML等。ESB要完成这么多协议和数据格式的互相转换工作量和复杂度都很大而且这种转换是需要耗费大量计算性能的当ESB承载的消息太多时ESB本身会成为整个系统的性能瓶颈。
当然SOA的ESB设计也是无奈之举。回想一下SOA的提出背景就可以发现企业在应用SOA时各种异构的IT系统都已经存在很多年了完全重写或者按照统一标准进行改造的成本是非常大的只能通过ESB方式去适配已经存在的各种异构系统。
小结
今天我为你讲了传统的可扩展架构模式包括分层架构和SOA架构希望对你有所帮助。
这就是今天的全部内容留一道思考题给你吧为什么互联网企业很少采用SOA架构

View File

@@ -0,0 +1,189 @@
因收到Google相关通知网站将会择期关闭。相关通知内容
34 深入理解微服务架构:银弹 or 焦油坑?
微服务是近几年非常火热的架构设计理念大部分人认为是Martin Fowler提出了微服务概念但事实上微服务概念的历史要早得多也不是Martin Fowler创造出来的Martin只是将微服务进行了系统的阐述原文链接https://martinfowler.com/articles/microservices.html。不过不能否认Martin在推动微服务起到的作用微服务能火Martin功不可没。
微服务的定义相信你早已耳熟能详参考维基百科我就来简单梳理一下微服务的历史吧https://en.wikipedia.org/wiki/Microservices#History
2005年Dr. Peter Rodgers在Web Services Edge大会上提出了“Micro-Web-Services”的概念。
2011年一个软件架构工作组使用了“microservice”一词来描述一种架构模式。
2012年同样是这个架构工作组正式确定用“microservice”来代表这种架构。
2012年ThoughtWorks的James Lewis针对微服务概念在QCon San Francisco 2012发表了演讲。
2014年James Lewis和Martin Fowler合写了关于微服务的一篇学术性的文章详细阐述了微服务。
由于微服务的理念中也包含了“服务”的概念而SOA中也有“服务”的概念我们自然而然地会提出疑问微服务与SOA有什么关系有什么区别为何有了SOA还要提微服务这几个问题是理解微服务的关键否则如果只是跟风拿来就用既不会用也用不好用了不但没有效果反而还可能有副作用。
今天我们就来深入理解微服务,到底是银弹还是焦油坑。
微服务与SOA的关系
对于了解过SOA的人来说第一次看到微服务这个概念肯定会有所疑惑为何有了SOA还要提微服务呢等到简单看完微服务的介绍后可能很多人更困惑了这不就是SOA吗
关于SOA和微服务的关系和区别大概分为下面几个典型的观点。
微服务是SOA的实现方式
如下图所示这种观点认为SOA是一种架构理念而微服务是SOA理念的一种具体实现方法。例如“微服务就是使用HTTP RESTful协议来实现ESB的SOA”“使用SOA来构建单个系统就是微服务”和“微服务就是更细粒度的SOA”。
微服务是去掉ESB后的SOA
如下图所示这种观点认为传统SOA架构最广为人诟病的就是庞大、复杂、低效的ESB因此将ESB去掉改为轻量级的HTTP实现就是微服务。
微服务是一种和SOA相似但本质上不同的架构理念
如下图所示这种观点认为微服务和SOA只是有点类似但本质上是不同的架构设计理念。相似点在于下图中交叉的地方就是两者都关注“服务”都是通过服务的拆分来解决可扩展性问题。本质上不同的地方在于几个核心理念的差异是否有ESB、服务的粒度、架构设计的目标等。
以上观点看似都有一定的道理但都有点差别到底哪个才是准确的呢单纯从概念上是难以分辨的我来对比一下SOA和微服务的一些具体做法再来看看到底哪一种观点更加符合实际情况。
服务粒度
整体上来说SOA的服务粒度要粗一些而微服务的服务粒度要细一些。例如对一个大型企业来说“员工管理系统”就是一个SOA架构中的服务而如果采用微服务架构则“员工管理系统”会被拆分为更多的服务比如“员工信息管理”“员工考勤管理”“员工假期管理”和“员工福利管理”等更多服务。
服务通信
SOA采用了ESB作为服务间通信的关键组件负责服务定义、服务路由、消息转换、消息传递总体上是重量级的实现。微服务推荐使用统一的协议和格式例如RESTful协议、RPC协议无须ESB这样的重量级实现。Martin Fowler将微服务架构的服务通讯理念称为“Smart endpoints and dumb pipes”简单翻译为“聪明的终端愚蠢的管道”。之所以用“愚蠢”二字其实就是与ESB对比的因为ESB太强大了既知道每个服务的协议类型例如是RMI还是HTTP又知道每个服务的数据类型例如是XML还是JSON还知道每个数据的格式例如是2017-01-01还是01/01/2017而微服务的“dumb pipes”仅仅做消息传递对消息格式和内容一无所知。
服务交付
SOA对服务的交付并没有特殊要求因为SOA更多考虑的是兼容已有的系统微服务的架构理念要求“快速交付”相应地要求采取自动化测试、持续集成、自动化部署等敏捷开发相关的最佳实践。如果没有这些基础能力支撑微服务规模一旦变大例如超过20个微服务整体就难以达到快速交付的要求这也是很多企业在实行微服务时踩过的一个明显的坑就是系统拆分为微服务后部署的成本呈指数上升。
应用场景
SOA更加适合于庞大、复杂、异构的企业级系统这也是SOA诞生的背景。这类系统的典型特征就是很多系统已经发展多年采用不同的企业级技术有的是内部开发的有的是外部购买的无法完全推倒重来或者进行大规模的优化和重构。因为成本和影响太大只能采用兼容的方式进行处理而承担兼容任务的就是ESB。
微服务更加适合于快速、轻量级、基于Web的互联网系统这类系统业务变化快需要快速尝试、快速交付同时基本都是基于Web虽然开发技术可能差异很大例如Java、C++、.NET等但对外接口基本都是提供HTTP RESTful风格的接口无须考虑在接口层进行类似SOA的ESB那样的处理。
综合上述分析我将SOA和微服务对比如下
因此我们可以看到SOA和微服务本质上是两种不同的架构设计理念只是在“服务”这个点上有交集而已因此两者的关系应该是上面第三种观点。
其实Martin Fowler在他的微服务文章中已经做了很好的提炼
In short, the microservice architectural style is an approach to developing a single application as a suite of small services, each running in its own process and communicating with lightweight mechanisms, often an HTTP resource API. These services are built around business capabilities and independently deployable by fully automated deployment machinery.
https://martinfowler.com/articles/microservices.html
上述英文的三个关键词分别是small、lightweight、automated基本上浓缩了微服务的精华也是微服务与SOA的本质区别所在。
通过前面的详细分析和比较似乎微服务本质上就是一种比SOA要优秀很多的架构模式那是否意味着我们都应该把架构重构为微服务呢
其实不然SOA和微服务是两种不同理念的架构模式并不存在孰优孰劣只是应用场景不同而已。我们介绍SOA时候提到其产生历史背景是因为企业的IT服务系统庞大而又复杂改造成本很高但业务上又要求其互通因此才会提出SOA这种解决方案。如果我们将微服务的架构模式生搬硬套到企业级IT服务系统中这些IT服务系统的改造成本可能远远超出实施SOA的成本。
微服务的陷阱
单纯从上面的对比来看似乎微服务大大优于SOA这也导致了很多团队在实践时不加思考地采用微服务——既不考虑团队的规模也不考虑业务的发展也没有考虑基础技术的支撑只是觉得微服务很牛就赶紧来实施以为实施了微服务后就什么问题都解决了而一旦真正实施后才发现掉到微服务的坑里面去了。
我们看一下微服务具体有哪些坑:
服务划分过细,服务间关系复杂
服务划分过细,单个服务的复杂度确实下降了,但整个系统的复杂度却上升了,因为微服务将系统内的复杂度转移为系统间的复杂度了。
从理论的角度来计算n个服务的复杂度是n×(n-1)/2整体系统的复杂度是随着微服务数量的增加呈指数级增加的。下图形象了说明了整体复杂度
粗粒度划分服务时系统被划分为3个服务虽然单个服务较大但服务间的关系很简单细粒度划分服务时虽然单个服务小了一些但服务间的关系却复杂了很多。
服务数量太多,团队效率急剧下降
微服务的“微”字本身就是一个陷阱很多团队看到“微”字后就想到必须将服务拆分得很细有的团队人员规模是5 ~ 6个人然而却拆分出30多个微服务平均每个人要维护5个以上的微服务。
这样做给工作效率带来了明显的影响一个简单的需求开发就需要涉及多个微服务光是微服务之间的接口就有6 ~ 7个无论是设计、开发、测试、部署都需要工程师不停地在不同的服务间切换。
开发工程师要设计多个接口,打开多个工程,调试时要部署多个程序,提测时打多个包。
测试工程师要部署多个环境,准备多个微服务的数据,测试多个接口。
运维工程师每次上线都要操作多个微服务,并且微服务之间可能还有依赖关系。
调用链太长,性能下降
由于微服务之间都是通过HTTP或者RPC调用的每次调用必须经过网络。一般线上的业务接口之间的调用平均响应时间大约为50毫秒如果用户的一起请求需要经过6次微服务调用则性能消耗就是300毫秒这在很多高性能业务场景下是难以满足需求的。为了支撑业务请求可能需要大幅增加硬件这就导致了硬件成本的大幅上升。
调用链太长,问题定位困难
系统拆分为微服务后,一次用户请求需要多个微服务协同处理,任意微服务的故障都将导致整个业务失败。然而由于微服务数量较多,且故障存在扩散现象,快速定位到底是哪个微服务故障是一件复杂的事情。下面是一个典型样例。
Service C的数据库出现慢查询导致Service C给Service B的响应错误Service B 给Service A的响应错误Service A给用户的响应错误。我们在实际定位时是不会有样例图中这么清晰的最开始是用户报错这时我们首先会去查Service A。导致Service A故障的原因有很多我们可能要花半个小时甚至1个小时才能发现是Service B返回错误导致的。于是我们又去查Service B这相当于重复Service A故障定位的步骤……如此循环下去最后可能花费了几个小时才能定位到是Service C的数据库慢查询导致了错误。
如果多个微服务同时发生不同类型的故障,则定位故障更加复杂,如下图所示。
Service C的数据库发生慢查询故障同时Service C到Service D的网络出现故障此时到底是哪个原因导致了Service C返回Error给Service B需要大量的信息和人力去排查。
没有自动化支撑,无法快速交付
如果没有相应的自动化系统进行支撑,都是靠人工去操作,那么微服务不但达不到快速交付的目的,甚至还不如一个大而全的系统效率高。例如:
没有自动化测试支撑,每次测试时需要测试大量接口。
没有自动化部署支撑每次部署6 ~ 7个服务几十台机器运维人员敲shell命令逐台部署手都要敲麻。
没有自动化监控,每次故障定位都需要人工查几十台机器几百个微服务的各种状态和各种日志文件。
没有服务治理,微服务数量多了后管理混乱
信奉微服务理念的设计人员总是强调微服务的lightweight特性并举出ESB的反例来证明微服务的优越之处。但具体实践后就会发现随着微服务种类和数量越来越多如果没有服务治理系统进行支撑微服务提倡的lightweight就会变成问题。主要问题有
服务路由假设某个微服务有60个节点部署在20台机器上那么其他依赖的微服务如何知道这个部署情况呢
服务故障隔离假设上述例子中的60个节点有5个节点发生故障了依赖的微服务如何处理这种情况呢
服务注册和发现同样是上述的例子现在我们决定从60个节点扩容到80个节点或者将60个节点缩减为40个节点新增或者减少的节点如何让依赖的服务知道呢
如果以上场景都依赖人工去管理整个系统将陷入一片混乱最终的解决方案必须依赖自动化的服务管理系统这时就会发现微服务所推崇的“lightweight”最终也发展成和ESB几乎一样的复杂程度。
小结
今天我为你讲了微服务与SOA的关系以及微服务实践中的常见陷阱希望对你有所帮助。
这就是今天的全部内容,留一道思考题给你吧,你们的业务有采用微服务么?谈谈具体实践过程中有什么经验和教训。

View File

@@ -0,0 +1,113 @@
因收到Google相关通知网站将会择期关闭。相关通知内容
35 微服务架构最佳实践 - 方法篇
专栏上一期,我谈了实施微服务需要避免踩的陷阱,简单提炼为:
微服务拆分过细过分强调“small”。
微服务基础设施不健全忽略了“automated”。
微服务并不轻量级规模大了后“lightweight”不再适应。
针对这些问题,今天我们看看微服务最佳实践应该如何去做。我会分两期介绍这部分内容,今天是微服务架构最佳实践的方法篇,下一期是基础设施篇。
服务粒度
针对微服务拆分过细导致的问题我建议基于团队规模进行拆分类似贝索斯在定义团队规模时提出的“两个披萨”理论每个团队的人数不能多到两张披萨都不够吃的地步分享一个我认为微服务拆分粒度的“三个火枪手”原则即一个微服务三个人负责开发。当我们在实施微服务架构时根据团队规模来划分微服务数量如果业务规继续发展团队规模扩大我们再将已有的微服务进行拆分。例如团队最初有6个人那么可以划分为2个微服务随着业务的发展业务功能越来越多逻辑越来越复杂团队扩展到12个人那么我们可以将已有的2个微服务进行拆分变成4个微服务。
为什么是3个人不是4个也不是2个呢
首先从系统规模来讲3个人负责开发一个系统系统的复杂度刚好达到每个人都能全面理解整个系统又能够进行分工的粒度如果是2个人开发一个系统系统的复杂度不够开发人员可能觉得无法体现自己的技术实力如果是4个甚至更多人开发一个系统系统复杂度又会无法让开发人员对系统的细节都了解很深。
其次从团队管理来说3个人可以形成一个稳定的备份即使1个人休假或者调配到其他系统剩余2个人还可以支撑如果是2个人抽调1个后剩余的1个人压力很大如果是1个人这就是单点了团队没有备份某些情况下是很危险的假如这个人休假了系统出问题了怎么办
最后从技术提升的角度来讲3个人的技术小组既能够形成有效的讨论又能够快速达成一致意见如果是2个人可能会出现互相坚持自己的意见或者2个人经验都不足导致设计缺陷如果是1个人由于没有人跟他进行技术讨论很可能陷入思维盲区导致重大问题如果是4个人或者更多可能有的参与的人员并没有认真参与只是完成任务而已。
“三个火枪手”的原则主要应用于微服务设计和开发阶段如果微服务经过一段时间发展后已经比较稳定处于维护期了无须太多的开发那么平均1个人维护1个微服务甚至几个微服务都可以。当然考虑到人员备份问题每个微服务最好都安排2个人维护每个人都可以维护多个微服务。
拆分方法
基于“三个火枪手”的理论,我们可以计算出拆分后合适的服务数量,但具体怎么拆也是有技巧的,并不是快刀斩乱麻随便拆分成指定数量的微服务就可以了,也不是只能按照业务来进行拆分,而是可以根据目的的不同灵活地选取不同的拆分方式。接下来我一一介绍常见的拆分方式。
1.基于业务逻辑拆分
这是最常见的一种拆分方式,将系统中的业务模块按照职责范围识别出来,每个单独的业务模块拆分为一个独立的服务。
基于业务逻辑拆分虽然看起来很直观但在实践过程中最常见的一个问题就是团队成员对于“职责范围”的理解差异很大经常会出现争论难以达成一致意见。例如假设我们做一个电商系统第一种方式是将服务划分为“商品”“交易”“用户”3个服务第二种方式是划分为“商品”“订单”“支付”“发货”“买家”“卖家”6个服务哪种方式更合理是不是划分越细越正确
导致这种困惑的主要根因在于从业务的角度来拆分的话,规模粗和规模细都没有问题,因为拆分基础都是业务逻辑,要判断拆分粒度,不能从业务逻辑角度,而要根据前面介绍的“三个火枪手”的原则,计算一下大概的服务数量范围,然后再确定合适的“职责范围”,否则就可能出现划分过粗或者过细的情况,而且大部分情况下会出现过细的情况。
例如如果团队规模是10个人支撑业务按照“三个火枪手”规则计算大约需要划分为4个服务那么“登录、注册、用户信息管理”都可以划到“用户服务”职责范围内如果团队规模是100人支撑业务服务数量可以达到40个那么“用户登录“就是一个服务了如果团队规模达到1000人支撑业务那“用户连接管理”可能就是一个独立的服务了。
2.基于可扩展拆分
将系统中的业务模块按照稳定性排序,将已经成熟和改动不大的服务拆分为稳定服务,将经常变化和迭代的服务拆分为变动服务。稳定的服务粒度可以粗一些,即使逻辑上没有强关联的服务,也可以放在同一个子系统中,例如将“日志服务”和“升级服务”放在同一个子系统中;不稳定的服务粒度可以细一些,但也不要太细,始终记住要控制服务的总数量。
这样拆分主要是为了提升项目快速迭代的效率,避免在开发的时候,不小心影响了已有的成熟功能导致线上问题。
3.基于可靠性拆分
将系统中的业务模块按照优先级排序,将可靠性要求高的核心服务和可靠性要求低的非核心服务拆分开来,然后重点保证核心服务的高可用。具体拆分的时候,核心服务可以是一个也可以是多个,只要最终的服务数量满足“三个火枪手”的原则就可以。
这样拆分带来下面几个好处:
避免非核心服务故障影响核心服务
例如,日志上报一般都属于非核心服务,但是在某些场景下可能有大量的日志上报,如果系统没有拆分,那么日志上报可能导致核心服务故障;拆分后即使日志上报有问题,也不会影响核心服务。
核心服务高可用方案可以更简单
核心服务的功能逻辑更加简单,存储的数据可能更少,用到的组件也会更少,设计高可用方案大部分情况下要比不拆分简单很多。
能够降低高可用成本
将核心服务拆分出来后,核心服务占用的机器、带宽等资源比不拆分要少很多。因此,只针对核心服务做高可用方案,机器、带宽等成本比不拆分要节省较多。
4.基于性能拆分
基于性能拆分和基于可靠性拆分类似将性能要求高或者性能压力大的模块拆分出来避免性能压力大的服务影响其他服务。常见的拆分方式和具体的性能瓶颈有关可以拆分Web服务、数据库、缓存等。例如电商的抢购性能压力最大的是入口的排队功能可以将排队功能独立为一个服务。
以上几种拆分方式不是多选一而是可以根据实际情况自由排列组合例如可以基于可靠性拆分出服务A基于性能拆分出服务B基于可扩展拆分出C/D/F三个服务加上原有的服务X最后总共拆分出6个服务A/B/C/D/F/X
基础设施
大部分人主要关注的是微服务的“small”和“lightweight”特性但实际上真正决定微服务成败的恰恰是那个被大部分人都忽略的“automated”。为何这样说呢因为服务粒度即使划分不合理实际落地后如果团队遇到麻烦自然会想到拆服务或者合服务如果“automated”相关的基础设施不健全那微服务就是焦油坑让研发、测试、运维陷入我上一期讲的各种微服务陷阱中。
微服务基础设施如下图所示:
看到上面这张图相信很多人都会倒吸一口凉气说好的微服务的“轻量级”呢都这么多基础设施还好意思说自己是“轻量级”感觉比ESB还要复杂啊
确实如此微服务并不是很多人认为的那样又简单又轻量级。要做好微服务这些基础设施都是必不可少的否则微服务就会变成一个焦油坑让业务和团队在里面不断挣扎且无法自拔。因此也可以说微服务并没有减少复杂度而只是将复杂度从ESB转移到了基础设施。你可以看到“服务发现”“服务路由”等其实都是ESB的功能只是在微服务中剥离出来成了独立的基础系统。
虽然建设完善的微服务基础设施是一项庞大的工程但也不用太过灰心认为自己团队小或者公司规模不大就不能实施微服务了。第一个原因是已经有开源的微服务基础设施全家桶了例如大名鼎鼎的Spring Cloud项目涵盖了服务发现、服务路由、网关、配置中心等功能第二个原因是如果微服务的数量并不是很多的话并不是每个基础设施都是必须的。通常情况下我建议按照下面优先级来搭建基础设施
1.服务发现、服务路由、服务容错:这是最基本的微服务基础设施。
2.接口框架、API网关主要是为了提升开发效率接口框架是提升内部服务的开发效率API网关是为了提升与外部服务对接的效率。
3.自动化部署、自动化测试、配置中心:主要是为了提升测试和运维效率。
4.服务监控、服务跟踪、服务安全:主要是为了进一步提升运维效率。
以上3和4两类基础设施其重要性会随着微服务节点数量增加而越来越重要但在微服务节点数量较少的时候可以通过人工的方式支撑虽然效率不高但也基本能够顶住。
小结
今天我为你讲了微服务架构实践中的三个关键点:如何把握拆分粒度、按照什么维度进行拆分、需要什么基础设施支撑,希望对你有所帮助。
这就是今天的全部内容,留一道思考题给你吧,参考文章中提到的方法,思考一下你所在的业务微服务架构是否还有可以改进和提升的空间?

View File

@@ -0,0 +1,126 @@
因收到Google相关通知网站将会择期关闭。相关通知内容
36 微服务架构最佳实践 - 基础设施篇
每项微服务基础设施都是一个平台、一个系统、一个解决方案如果要自己实现其过程和做业务系统类似都需要经过需求分析、架构设计、开发、测试、部署上线等步骤专栏里我来简单介绍一下每个基础设施的主要作用更多详细设计你可以参考Spring Cloud的相关资料https://projects.spring.io/spring-cloud/)。
下面进入今天的内容,微服务架构最佳实践的基础设施篇。
自动化测试
微服务将原本大一统的系统拆分为多个独立运行的“微”服务,微服务之间的接口数量大大增加,并且微服务提倡快速交付,版本周期短,版本更新频繁。如果每次更新都靠人工回归整个系统,则工作量大,效率低下,达不到“快速交付”的目的,因此必须通过自动化测试系统来完成绝大部分测试回归的工作。
自动化测试涵盖的范围包括代码级的单元测试、单个系统级的集成测试、系统间的接口测试,理想情况是每类测试都自动化。如果因为团队规模和人力的原因无法全面覆盖,至少要做到接口测试自动化。
自动化部署
相比大一统的系统微服务需要部署的节点增加了几倍甚至十几倍微服务部署的频率也会大幅提升例如我们的业务系统70%的工作日都有部署操作),综合计算下来,微服务部署的次数是大一统系统部署次数的几十倍。这么大量的部署操作,如果继续采用人工手工处理,需要投入大量的人力,且容易出错,因此需要自动化部署的系统来完成部署操作。
自动化部署系统包括版本管理、资源管理(例如,机器管理、虚拟机管理)、部署操作、回退操作等功能。
配置中心
微服务的节点数量非常多,通过人工登录每台机器手工修改,效率低,容易出错。特别是在部署或者排障时,需要快速增删改查配置,人工操作的方式显然是不行的。除此以外,有的运行期配置需要动态修改并且所有节点即时生效,人工操作是无法做到的。综合上面的分析,微服务需要一个统一的配置中心来管理所有微服务节点的配置。
配置中心包括配置版本管理例如同样的微服务有10个节点是给移动用户服务的有20个节点给联通用户服务的配置项都一样配置值不一样、增删改查配置、节点管理、配置同步、配置推送等功能。
接口框架
微服务提倡轻量级的通信方式一般采用HTTP/REST或者RPC方式统一接口协议。但在实践过程中光统一接口协议还不够还需要统一接口传递的数据格式。例如我们需要指定接口协议为HTTP/REST但这还不够还需要指定HTTP/REST的数据格式采用JSON并且JSON的数据都遵循如下规范。
如果我们只是简单指定了HTTP/REST协议而不指定JSON和JSON的数据规范那么就会出现这样混乱的情况有的微服务采用XML有的采用JSON有的采用键值对即使同样都是JSONJSON数据格式也不一样。这样每个微服务都要适配几套甚至几十套接口协议相当于把曾经由ESB做的事情转交给微服务自己做了这样做的效率显然是无法接受的因此需要统一接口框架。
接口框架不是一个可运行的系统一般以库或者包的形式提供给所有微服务调用。例如针对上面的JSON样例可以由某个基础技术团队提供多种不同语言的解析包Java包、Python包、C库等
API网关
系统拆分为微服务后,内部的微服务之间是互联互通的,相互之间的访问都是点对点的。如果外部系统想调用系统的某个功能,也采取点对点的方式,则外部系统会非常“头大”。因为在外部系统看来,它不需要也没办法理解这么多微服务的职责分工和边界,它只会关注它需要的能力,而不会关注这个能力应该由哪个微服务提供。
除此以外,外部系统访问系统还涉及安全和权限相关的限制,如果外部系统直接访问某个微服务,则意味着每个微服务都要自己实现安全和权限的功能,这样做不但工作量大,而且都是重复工作。
综合上面的分析微服务需要一个统一的API网关负责外部系统的访问操作。
API网关是外部系统访问的接口所有的外部系统接⼊系统都需要通过API网关主要包括接入鉴权是否允许接入、权限控制可以访问哪些功能、传输加密、请求路由、流量控制等功能。
服务发现
微服务种类和数量很多,如果这些信息全部通过手工配置的方式写入各个微服务节点,首先配置工作量很大,配置文件可能要配几百上千行,几十个节点加起来后配置项就是几万几十万行了,人工维护这么大数量的配置项是一项灾难;其次是微服务节点经常变化,可能是由于扩容导致节点增加,也可能是故障处理时隔离掉一部分节点,还可能是采用灰度升级,先将一部分节点升级到新版本,然后让新老版本同时运行。不管哪种情况,我们都希望节点的变化能够及时同步到所有其他依赖的微服务。如果采用手工配置,是不可能做到实时更改生效的。因此,需要一套服务发现的系统来支撑微服务的自动注册和发现。
服务发现主要有两种实现方式:自理式和代理式。
1.自理式
自理式结构如下:
自理式结构就是指每个微服务自己完成服务发现。例如图中SERVICE INSTANCE A访问SERVICE REGISTRY获取服务注册信息然后直接访问SERVICE INSTANCE B。
自理式服务发现实现比较简单,因为这部分的功能一般通过统一的程序库或者程序包提供给各个微服务调用,而不会每个微服务都自己来重复实现一遍;并且由于每个微服务都承担了服务发现的功能,访问压力分散到了各个微服务节点,性能和可用性上不存在明显的压力和风险。
2.代理式
代理式结构如下:
代理式结构就是指微服务之间有一个负载均衡系统图中的LOAD BALANCER节点由负载均衡系统来完成微服务之间的服务发现。
代理式的方式看起来更加清晰微服务本身的实现也简单了很多但实际上这个方案风险较大。第一个风险是可用性风险一旦LOAD BALANCER系统故障就会影响所有微服务之间的调用第二个风险是性能风险所有的微服务之间的调用流量都要经过LOAD BALANCER系统性能压力会随着微服务数量和流量增加而不断增加最后成为性能瓶颈。因此LOAD BALANCER系统需要设计成集群的模式但LOAD BALANCER集群的实现本身又增加了复杂性。
不管是自理式还是代理式服务发现的核心功能就是服务注册表注册表记录了所有的服务节点的配置和状态每个微服务启动后都需要将自己的信息注册到服务注册表然后由微服务或者LOAD BALANCER系统到服务注册表查询可用服务。
服务路由
有了服务发现后,微服务之间能够方便地获取相关配置信息,但具体进行某次调用请求时,我们还需要从所有符合条件的可用微服务节点中挑选出一个具体的节点发起请求,这就是服务路由需要完成的功能。
服务路由和服务发现紧密相关服务路由一般不会设计成一个独立运行的系统通常情况下是和服务发现放在一起实现的。对于自理式服务发现服务路由是微服务内部实现的对于代理式服务发现服务路由是由LOAD BALANCER系统实现的。无论放在哪里实现服务路由核心的功能就是路由算法。常见的路由算法有随机路由、轮询路由、最小压力路由、最小连接数路由等。
服务容错
系统拆分为微服务后,单个微服务故障的概率变小,故障影响范围也减少,但是微服务的节点数量大大增加。从整体上来看,系统中某个微服务出故障的概率会大大增加。[专栏第34期]我在分析微服务陷阱时提到微服务具有故障扩散的特点,如果不及时处理故障,故障扩散开来就会导致看起来系统中很多服务节点都故障了,因此需要微服务能够自动应对这种出错场景,及时进行处理。否则,如果节点一故障就需要人工处理,投入人力大,处理速度慢;而一旦处理速度慢,则故障就很快扩散,所以我们需要服务容错的能力。
常见的服务容错包括请求重试、流控和服务隔离。通常情况下,服务容错会集成在服务发现和服务路由系统中。
服务监控
系统拆分为微服务后,节点数量大大增加,导致需要监控的机器、网络、进程、接口调用数等监控对象的数量大大增加;同时,一旦发生故障,我们需要快速根据各类信息来定位故障。这两个目标如果靠人力去完成是不现实的。举个简单例子:我们收到用户投诉说业务有问题,如果此时采取人工的方式去搜集、分析信息,可能把几十个节点的日志打开一遍就需要十几分钟了,因此需要服务监控系统来完成微服务节点的监控。
服务监控的主要作用有:
实时搜集信息并进行分析,避免故障后再来分析,减少了处理时间。
服务监控可以在实时分析的基础上进行预警,在问题萌芽的阶段发觉并预警,降低了问题影响的范围和时间。
通常情况下服务监控需要搜集并分析大量的数据因此建议做成独立的系统而不要集成到服务发现、API网关等系统中。
服务跟踪
服务监控可以做到微服务节点级的监控和信息收集,但如果我们需要跟踪某一个请求在微服务中的完整路径,服务监控是难以实现的。因为如果每个服务的完整请求链信息都实时发送给服务监控系统,数据量会大到无法处理。
服务监控和服务跟踪的区别可以简单概括为宏观和微观的区别。例如A服务通过HTTP协议请求B服务10次B通过HTTP返回JSON对象服务监控会记录请求次数、响应时间平均值、响应时间最高值、错误码分布这些信息而服务跟踪会记录其中某次请求的发起时间、响应时间、响应错误码、请求参数、返回的JSON对象等信息。
目前无论是分布式跟踪还是微服务的服务跟踪绝大部分请求跟踪的实现技术都基于Google的Dapper论文《Dapper, a Large-Scale Distributed Systems Tracing Infrastructure》。
服务安全
系统拆分为微服务后,数据分散在各个微服务节点上。从系统连接的角度来说,任意微服务都可以访问所有其他微服务节点;但从业务的角度来说,部分敏感数据或者操作,只能部分微服务可以访问,而不是所有的微服务都可以访问,因此需要设计服务安全机制来保证业务和数据的安全性。
服务安全主要分为三部分:接入安全、数据安全、传输安全。通常情况下,服务安全可以集成到配置中心系统中进行实现,即配置中心配置微服务的接入安全策略和数据安全策略,微服务节点从配置中心获取这些配置信息,然后在处理具体的微服务调用请求时根据安全策略进行处理。由于这些策略是通用的,一般会把策略封装成通用的库提供给各个微服务调用。基本架构如下:
小结
今天我为你讲了微服务架构相关的基础设施的概要介绍和关键设计点,希望对你有所帮助。
这就是今天的全部内容这一期的思考题很特别给你一个由10位Java高级软件工程师组成的开发团队采用自研的方式完成所有的微服务基础设施开发你预测需要多长时间理由是什么呢

View File

@@ -0,0 +1,208 @@
因收到Google相关通知网站将会择期关闭。相关通知内容
37 微内核架构详解
微内核架构Microkernel Architecture也被称为插件化架构Plug-in Architecture是一种面向功能进行拆分的可扩展性架构通常用于实现基于产品原文为product-based指存在多个版本、需要下载安装才能使用与web-based相对应的应用。例如Eclipse这类IDE软件、UNIX这类操作系统、淘宝App这类客户端软件等也有一些企业将自己的业务系统设计成微内核的架构例如保险公司的保险核算逻辑系统不同的保险品种可以将逻辑封装成插件。
今天我将为你详细介绍常见的微内核架构及其实现。
基本架构
微内核架构包含两类组件核心系统core system和插件模块plug-in modules。核心系统负责和具体业务功能无关的通用功能例如模块加载、模块间通信等插件模块负责实现具体的业务逻辑例如专栏前面经常提到的“学生信息管理”系统中的“手机号注册”功能。
微内核的基本架构示意图如下:
上面这张图中核心系统Core System功能比较稳定不会因为业务功能扩展而不断修改插件模块可以根据业务功能的需要不断地扩展。微内核的架构本质就是将变化部分封装在插件里面从而达到快速灵活扩展的目的而又不影响整体系统的稳定。
设计关键点
微内核的核心系统设计的关键技术有:插件管理、插件连接和插件通信。
1.插件管理
核心系统需要知道当前有哪些插件可用,如何加载这些插件,什么时候加载插件。常见的实现方法是插件注册表机制。
核心系统提供插件注册表(可以是配置文件,也可以是代码,还可以是数据库),插件注册表含有每个插件模块的信息,包括它的名字、位置、加载时机(启动就加载,还是按需加载)等。
2.插件连接
插件连接指插件如何连接到核心系统。通常来说,核心系统必须制定插件和核心系统的连接规范,然后插件按照规范实现,核心系统按照规范加载即可。
常见的连接机制有OSGiEclipse使用、消息模式、依赖注入Spring使用甚至使用分布式的协议都是可以的比如RPC或者HTTP Web的方式。
3.插件通信
插件通信指插件间的通信。虽然设计的时候插件间是完全解耦的但实际业务运行过程中必然会出现某个业务流程需要多个插件协作这就要求两个插件间进行通信。由于插件之间没有直接联系通信必须通过核心系统因此核心系统需要提供插件通信机制。这种情况和计算机类似计算机的CPU、硬盘、内存、网卡是独立设计的配件但计算机运行过程中CPU和内存、内存和硬盘肯定是有通信的计算机通过主板上的总线提供了这些组件之间的通信功能。微内核的核心系统也必须提供类似的通信机制各个插件之间才能进行正常的通信。
OSGi架构简析
OSGi的全称是Open Services Gateway initiative本身其实是指OSGi Alliance。这个联盟是Sun Microsystems、IBM、爱立信等公司于1999年3月成立的开放的标准化组织最初名为Connected Alliance。它是一个非盈利的国际组织旨在建立一个开放的服务规范为通过网络向设备提供服务建立开放的标准这个标准就是OSGi specification。现在我们谈到OSGi如果没有特别说明一般都是指OSGi的规范。
OSGi联盟的初始目标是构建一个在广域网和局域网或设备上展开业务的基础平台所以OSGi的最早设计也是针对嵌入式应用的诸如机顶盒、服务网关、手机、汽车等都是其应用的主要环境。然而无心插柳柳成荫由于OSGi具备动态化、热插拔、高可复用性、高效性、扩展方便等优点它被应用到了PC上的应用开发。尤其是Eclipse这个流行软件采用OSGi标准后OSGi更是成为了首选的插件化标准。现在我们谈论OSGi已经和嵌入式应用关联不大了更多是将OSGi当作一个微内核的架构模式。
Eclipse从3.0版本开始抛弃了原来自己实现的插件化框架改用了OSGi框架。需要注意的是OSGi是一个插件化的标准而不是一个可运行的框架Eclipse采用的OSGi框架称为Equinox类似的实现还有Apache的Felix、Spring的Spring DM。
OSGi框架的逻辑架构图如下
1.模块层Module层
模块层实现插件管理功能。OSGi中插件被称为Bundle每个Bundle是一个Java的JAR文件每个Bundle里面都包含一个元数据文件MANIFEST.MF这个文件包含了Bundle的基本信息。例如Bundle的名称、描述、开发商、classpath以及需要导入的包和输出的包等OSGi核心系统会将这些信息加载到系统中用于后续使用。
一个简单的MANIFEST.MF样例如下
// MANIFEST.MF
Bundle-ManifestVersion: 2
Bundle-Name:UserRegister
Bundle-SymbolicName: com.test.userregister
Bundle-Version: 1.0
Bundle-Activator: com.test.UserRegisterActivator
Import-Package: org.log4j;version="2.0",
.....
Export-Package: com.test.userregister;version="1.0",
2.生命周期层Lifecycle层
生命周期层实现插件连接功能提供了执行时模块管理、模块对底层OSGi框架的访问。生命周期层精确地定义了Bundle生命周期的操作安装、更新、启动、停止、卸载Bundle必须按照规范实现各个操作。例如
public class UserRegisterActivator implements BundleActivator {
public void start(BundleContext context) {
UserRegister.instance = new UserRegister ();
}
public void stop(BundleContext context) {
UserRegister.instance = null;
}
}
3.服务层Service层
服务层实现插件通信的功能。OSGi提供了一个服务注册的功能用于各个插件将自己能提供的服务注册到OSGi核心的服务注册中心如果某个服务想用其他服务则直接在服务注册中心搜索可用服务中心就可以了。
例如:
// 注册服务
public class UserRegisterActivator implements BundleActivator {
//在start()中用BundleContext.registerService()注册服务
public void start(BundleContext context) {
context.registerService(UserRegister.class.getName(), new UserRegisterImpl(), null);
}
//无须在stop()中注销服务因为Bundle停止时会自动注销该Bundle中已注册的服务
public void stop(BundleContext context) {}
}
// 检索服务
public class Client implements BundleActivator {
public void start(BundleContext context) {
// 1. 从服务注册表中检索间接的“服务引用”
ServiceReference ref = context.getServiceReference(UserRegister.class.getName());
// 2. 使用“服务引用”去访问服务对象的实例
((UserRegister) context.getService(ref)).register();
}
public void stop(BundleContext context) {}
}
注意:这里的服务注册不是插件管理功能中的插件注册,实际上是插件间通信的机制。
规则引擎架构简析
规则引擎从结构上来看也属于微内核架构的一种具体实现,其中执行引擎可以看作是微内核,执行引擎解析配置好的业务流,执行其中的条件和规则,通过这种方式来支持业务的灵活多变。
规则引擎在计费、保险、促销等业务领域应用较多。例如电商促销,常见的促销规则有:
满100送50
3件立减50
3件8折
第3件免费
跨店满200减100
新用户立减50
……
以上仅仅列出来常见的几种,实际上完整列下来可能有几十上百种,再加上排列组合,促销方案可能有几百上千种,这样的业务如果完全靠代码来实现,开发效率远远跟不上业务的变化速度,而规则引擎却能够很灵活的应对这种需求,主要原因在于:
可扩展
通过引入规则引擎,业务逻辑实现与业务系统分离,可以在不改动业务系统的情况下扩展新的业务功能。
易理解
规则通过自然语言描述,业务人员易于理解和操作,而不像代码那样只有程序员才能理解和开发。
高效率
规则引擎系统一般提供可视化的规则定制、审批、查询及管理,方便业务人员快速配置新的业务。
规则引擎的基本架构如下:
我来简单介绍一下:
开发人员将业务功能分解提炼为多个规则,将规则保存在规则库中。
业务人员根据业务需要,通过将规则排列组合,配置成业务流程,保存在业务库中。
规则引擎执行业务流程实现业务功能。
对照微内核架构的设计关键点,我们来看看规则引擎是具体是如何实现的。
插件管理
规则引擎中的规则就是微内核架构的插件,引擎就是微内核架构的内核。规则可以被引擎加载和执行。规则引擎架构中,规则一般保存在规则库中,通常使用数据库来存储。
插件连接
类似于程序员开发的时候需要采用Java、C++等语言,规则引擎也规定了规则开发的语言,业务人员需要基于规则语言来编写规则文件,然后由规则引擎加载执行规则文件来完成业务功能,因此,规则引擎的插件连接实现机制其实就是规则语言。
插件通信
规则引擎的规则之间进行通信的方式就是数据流和事件流,由于单个规则并不需要依赖其他规则,因此规则之间没有主动的通信,规则只需要输出数据或者事件,由引擎将数据或者事件传递到下一个规则。
目前最常用的规则引擎是开源的JBoss Drools采用Java语言编写基于Rete算法参考https://en.wikipedia.org/wiki/Rete_algorithm。Drools具有下面这些优点
非常活跃的社区支持,以及广泛的应用。
快速的执行速度。
与Java Rule Engine APIJSR-94兼容。
提供了基于Web的BRMS——GuvnorGuvnor提供了规则管理的知识库通过它可以实现规则的版本控制以及规则的在线修改与编译使得开发人员和系统管理人员可以在线管理业务规则。
虽然Drools号称简单易用但实际上其规则语言还是和编程语言比较类似在实际应用的时候普通业务人员面对这样的规则语言学习成本和理解成本还是比较高的例如下面这个样例https://blog.csdn.net/ouyangshixiong/article/details/46315273
因此通常情况下需要基于Drools进行封装将规则配置做成可视化的操作例如下面电商反欺诈的一个示例https://cloud.tencent.com/developer/article/1031839
小结
今天我为你讲了微内核架构设计的关键点以及常见的两种微内核具体实现OSGi和规则引擎希望对你有所帮助。
这就是今天的全部内容留一道思考题给你吧结合今天所学内容尝试分析一下手淘Atlas容器化框架是如何实现微内核架构的设计关键点的分享一下你的理解。

View File

@@ -0,0 +1,166 @@
因收到Google相关通知网站将会择期关闭。相关通知内容
38 架构师应该如何判断技术演进的方向?
互联网的出现不但改变了普通人的生活方式同时也促进了技术圈的快速发展和开放。在开源和分享两股力量的推动下最近10多年的技术发展可以说是目不暇接你方唱罢我登场大的方面有大数据、云计算、人工智能等细分的领域有NoSQL、Node.js、Docker容器化等。各个大公司也乐于将自己的技术分享出来以此来提升自己的技术影响力打造圈内技术口碑从而形成强大的人才吸引力典型的有Google的大数据论文、淘宝的全链路压测、微信的红包高并发技术等。
对于技术人员来说,技术的快速发展当然是一件大好事,毕竟这意味着技术百宝箱中又多了更多的可选工具,同时也可以通过学习业界先进的技术来提升自己的技术实力。但对于架构师来说,除了这些好处,却也多了“甜蜜的烦恼”:面对层出不穷的新技术,我们应该采取什么样的策略?
架构师可能经常会面临下面这些诱惑或者挑战:
现在Docker虚拟化技术很流行我们要不要引进引入Docker后可以每年节省几十万元的硬件成本呢
竞争对手用了阿里的云计算技术,听说因为上了云,业务增长了好几倍呢,我们是否也应该尽快上云啊?
我们的技术和业界顶尖公司(例如,淘宝、微信)差距很大,应该投入人力和时间追上去,不然招聘的时候没有技术影响力!
公司的技术发展现在已经比较成熟了程序员都觉得在公司学不到东西我们可以尝试引入Golang来给大家一个学习新技术的机会。
类似的问题还有很多,本质上都可以归纳总结为一个问题:架构师应该如何判断技术演进的方向?
关于这个问题的答案,基本上可以分为几个典型的派别:
1.潮流派
潮流派的典型特征就是对于新技术特别热衷,紧跟技术潮流,当有新的技术出现时,迫切想将新的技术应用到自己的产品中。
例如:
NoSQL很火咱们要大规模地切换为NoSQL。
大数据好牛呀将我们的MySQL切换为Hadoop吧。
Node.js使得JavaScript统一前后端这样非常有助于开展工作。
2.保守派
保守派的典型特征和潮流派正好相反,对于新技术抱有很强的戒备心,稳定压倒一切,已经掌握了某种技术,就一直用这种技术打天下。就像有句俗语说的,“如果你手里有一把锤子,那么所有的问题都变成了钉子”,保守派就是拿着一把锤子解决所有的问题。
例如:
MySQL咱们用了这么久了很熟悉了业务用MySQL数据分析也用MySQL报表还用MySQL吧。
Java语言我们都很熟业务用Java工具用Java平台也用Java。
3.跟风派
跟风派与潮流派不同,这里的跟风派不是指跟着技术潮流,而是指跟着竞争对手的步子走。
简单来说,判断技术的发展就看竞争对手,竞争对手用了咱们就用,竞争对手没用咱们就等等看。
例如:
这项技术腾讯用了吗?腾讯用了我们就用。
阿里用了Hadoop他们都在用肯定是好东西咱们也要尽快用起来以提高咱们的竞争力。
Google都用了Docker咱们也用吧。
不同派别的不同做法本质上是价值观的不同:潮流派的价值观是新技术肯定能带来很大收益;稳定派的价值观是稳定压倒一切;跟风派的价值观是别人用了我就用。这些价值观本身都有一定的道理,但如果不考虑实际情况生搬硬套,就会出现“橘生淮南则为橘,生于淮北则为枳”的情况。
下面我们来看一下不同的派别可能存在的问题。
1.潮流派
首先,新技术需要时间成熟,如果刚出来就用,此时新技术还不怎么成熟,实际应用中很可能遇到各种“坑”,自己成了实验小白鼠。
其次,新技术需要学习,需要花费一定的时间去掌握,这个也是较大的成本;如果等到掌握了技术后又发现不适用,则是一种较大的人力浪费。
2.保守派
保守派的主要问题是不能享受新技术带来的收益,因为新技术很多都是为了解决以前技术存在的固有缺陷。就像汽车取代马车一样,不是量变而是质变,带来的收益不是线性变化的,而是爆发式变化的。如果无视技术的发展,形象一点说就是有了拖拉机,你还偏偏要用牛车。
3.跟风派
可能很多人都会认为,跟风派与“潮流派”和“保守派”相比,是最有效的策略,既不会承担“潮流派”的风险,也不会遭受“保守派”的损失,花费的资源也少,简直就是一举多得。
看起来很美妙,但跟风派最大的问题在于如果没有风可跟的时候怎么办。如果你是领头羊怎么办,其他人都准备跟你的风呢?另外一种情况就是竞争对手的这些信息并不那么容易获取,即使获取到了一些信息,大部分也是不全面的,一不小心可能就变成邯郸学步了。
即使有风可跟,其实也存在问题。有时候适用于竞争对手的技术,并不一定适用于自己,盲目模仿可能带来相反的效果。
既然潮流派、保守派、跟风派都存在这样或者那样的问题,那架构师究竟如何判断技术演进的方向呢?
技术演进的动力
这个问题之所以让人困惑,关键的原因还是在于不管是潮流派、保守派,还是跟风派,都是站在技术本身的角度来考虑问题的,正所谓“不识庐山真面,只缘身在此山中”。因此,要想看到“庐山真面目”,只有跳出技术的范畴,从一个更广更高的角度来考虑这个问题,这个角度就是企业的业务发展。
无论是代表新兴技术的互联网企业还是代表传统技术的制造业无论是通信行业还是金融行业的发展归根到底就是业务的发展。而影响一个企业业务的发展主要有3个因素市场、技术、管理这三者构成支撑业务发展的铁三角任何一个因素的不足都可能导致企业的业务停滞不前。
在这个铁三角中,业务处于三角形的中心,毫不夸张地说,市场、技术、管理都是为了支撑企业业务的发展。在专栏里,我主要探讨“技术”和“业务”之间的关系和互相如何影响。
我们可以简单地将企业的业务分为两类:一类是产品类,一类是服务类。
产品类360的杀毒软件、苹果的iPhone、UC的浏览器等都属于这个范畴这些产品本质上和传统的制造业产品类似都是具备了某种“功能”单个用户通过购买或者免费使用这些产品来完成自己相关的某些任务用户对这些产品是独占的。
服务类百度的搜索、淘宝的购物、新浪的微博、腾讯的IM等都属于这个范畴大量用户使用这些服务来完成需要与其他人交互的任务单个用户“使用”但不“独占”某个服务。事实上服务的用户越多服务的价值就越大。服务类的业务符合互联网的特征和本质“互联”+“网”。
对于产品类业务,答案看起来很明显:技术创新推动业务发展!
例如:
苹果开发智能手机,将诺基亚推下王座,自己成为全球手机行业的新王者。
2G时代UC浏览器独创的云端架构很好地解决了上网慢的问题智能机时代UC浏览器又自主研发全新的U3内核兼顾高速、安全、智能及可扩展性这些技术创新是UC浏览器成为了全球最大的第三方手机浏览器最强有力的推动力。
为何对于产品类的业务技术创新能够推动业务发展呢答案在于用户选择一个产品的根本驱动力在于产品的功能是否能够更好地帮助自己完成任务。用户会自然而然地选择那些功能更加强大、性能更加先进、体验更加顺畅、外观更加漂亮的产品而功能、性能、体验、外观等都需要强大的技术支撑。例如iPhone手机的多点触摸操作、UC浏览器的U3内核等。
对于“服务”类的业务,答案和产品类业务正好相反:业务发展推动技术的发展!
为什么会出现截然相反的差别呢?主要原因是用户选择服务的根本驱动力与选择产品不同。用户选择一个产品的根本驱动力是其“功能”,而用户选择一个服务的根本驱动力不是功能,而是“规模”。
例如选择UC浏览器还是选择QQ浏览器更多的人是根据个人喜好和体验来决定的而选择微信还是Whatsapp就不是根据它们之间的功能差异来选择的而是根据其规模来选择的就像我更喜欢Whatsapp的简洁但我的朋友和周边的人都用微信那我也不得不用微信。
当“规模”成为业务的决定因素后,服务模式的创新就成为了业务发展的核心驱动力,而产品只是为了完成服务而提供给用户使用的一个载体。以淘宝为例,淘宝提供的“网络购物”是一种新的服务,这种业务与传统的到实体店购物是完全不同的,而为了完成这种业务,需要“淘宝网”“支付宝”“一淘”和“菜鸟物流”等多个产品。随便一个软件公司,如果只是模仿开发出类似的产品,只要愿意投入,半年时间就可以将这些产品全部开发出来。但是这样做并没有意义,因为用户选择的是淘宝的整套网络购物服务,并且这个服务已经具备了一定的规模,其他公司不具备这种同等规模服务的能力。即使开发出完全一样的产品,用户也不会因为产品功能更加强大而选择新的类似产品。
以微信为例同样可以得出类似结论。假如我们进行技术创新开发一个耗电量只有微信的1/10用户体验比微信好10倍的产品你觉得现在的微信用户都会抛弃微信而转投我们的这个产品吗我相信绝大部分人都不会因为微信不是一个互联网产品而是一个互联网服务你一个人换到其他类微信类产品是没有意义的。
因此,服务类的业务发展路径是这样的:提出一种创新的服务模式→吸引了一批用户→业务开始发展→吸引了更多用户→服务模式不断完善和创新→吸引越来越多的用户,如此循环往复。在这个发展路径中,技术并没有成为业务发展的驱动力,反过来由于用户规模的不断扩展,业务的不断创新和改进,对技术会提出越来越高的要求,因此是业务驱动了技术发展。
其实回到产品类业务如果我们将观察的时间拉长来看即使是产品类业务在技术创新开创了一个新的业务后后续的业务发展也会反向推动技术的发展。例如第一代iPhone缺少对3G的支持且只能通过Web发布应用程序第二代iPhone才开始支持3G并且内置GPSUC浏览器随着功能越来越强大原有的技术无法满足业务发展的需求浏览器的架构需要进行更新先后经过UC浏览器7.0版本、8.0版本、9.0版本等几个技术差异很大的版本。
综合这些分析,除非是开创新的技术能够推动或者创造一种新的业务,其他情况下,都是业务的发展推动了技术的发展。
技术演进的模式
明确了技术发展主要的驱动力是业务发展后,我们来看看业务发展究竟是如何驱动技术发展的。
业务模式千差万别有互联网的业务淘宝、微信等有金融的业务中国平安、招商银行等有传统企业的业务各色ERP对应的业务但无论什么模式的业务如果业务的发展需要技术同步发展进行支撑无一例外是因为业务“复杂度”的上升导致原有的技术无法支撑。
按照专栏前面所介绍的复杂度分类,复杂度要么来源于功能不断叠加,要么来源于规模扩大,从而对性能和可用性有了更高的要求。既然如此,判断到底是什么复杂度发生了变化就显得至关重要了。是任何时候都要同时考虑功能复杂度和规模复杂度吗?还是有时候考虑功能复杂度,有时候考虑规模复杂度?还是随机挑一个复杂度的问题解决就可以了?
所以,对于架构师来说,判断业务当前和接下来一段时间的主要复杂度是什么就非常关键。判断不准确就会导致投入大量的人力和时间做了对业务没有作用的事情,判断准确就能够做到技术推动业务更加快速发展。那架构师具体应该按照什么标准来判断呢?
答案就是基于业务发展阶段进行判断,这也是为什么架构师必须具备业务理解能力的原因。不同的行业业务发展路径、轨迹、模式不一样,架构师必须能够基于行业发展和企业自身情况做出准确判断。
假设你是一个银行IT系统的架构师
90年代主要的业务复杂度可能就是银行业务范围逐渐扩大功能越来越复杂导致内部系统数量越来越多单个系统功能越来越复杂。
2004年以后主要的复杂度就是银行业务从柜台转向网上银行网上银行的稳定性、安全性、易用性是主要的复杂度这些复杂度主要由银行IT系统自己解决。
2009年以后主要的复杂度又变化为移动支付复杂度尤其是“双11”这种海量支付请求的情况下高性能、稳定性、安全性是主要的复杂度而这些复杂度需要银行和移动支付服务商支付宝、微信等一起解决。
而如果你是淘宝这种互联网业务的架构师,业务发展又会是另外一种模式:
2003年业务刚刚创立主要的复杂度体现为如何才能快速开发各种需求淘宝团队采取的是买了一个PHP写的系统来改。
2004年上线后业务发展迅速用户请求数量大大增加主要的复杂度体现为如何才能保证系统的性能淘宝的团队采取的是用Oracle取代MySQL。
用户数量再次增加主要的复杂度还是性能和稳定性淘宝的团队采取的是Java替换PHP。
2005年用户数量继续增加主要的复杂度体现为单一的Oracle库已经无法满足性能要求于是进行了分库分表、读写分离、缓存等优化。
2008年淘宝的商品数量在1亿以上PV2.5亿以上,主要的复杂度又变成了系统内部耦合,交易和商品耦合在一起,支付的时候又和支付宝强耦合,整个系统逻辑复杂,功能之间跳来跳去,用户体验也不好。淘宝的团队采取的是系统解耦,将交易中心、类目管理、用户中心从原来大一统的系统里面拆分出来。
小结
今天我为你讲了架构师该如何判断技术演进的方向,希望对你有所帮助。
这就是今天的全部内容,留一道思考题给你吧,如果业界已经有了一个明显的参照对象(例如电商企业可以参考淘宝),那架构师是否还需要按照步骤逐步演进,还是直接将架构一步到位设计好?

View File

@@ -0,0 +1,155 @@
因收到Google相关通知网站将会择期关闭。相关通知内容
39 互联网技术演进的模式
由于各行业的业务发展轨迹并不完全相同,无法给出一个统一的模板让所有的架构师拿来就套用,因此我以互联网的业务发展为案例,谈谈互联网技术演进的模式,其他行业可以参考分析方法对自己的行业进行分析。
互联网业务千差万别,但由于它们具有“规模决定一切”的相同点,其发展路径也基本上是一致的。互联网业务发展一般分为几个时期:初创期、发展期、竞争期、成熟期。
不同时期的差别主要体现在两个方面:复杂性、用户规模。
业务复杂性
互联网业务发展第一个主要方向就是“业务越来越复杂”,我们来看看不同时期业务的复杂性的表现。
1.初创期
互联网业务刚开始一般都是一个创新的业务点,这个业务点的重点不在于“完善”,而在于“创新”,只有创新才能吸引用户;而且因为其“新”的特点,其实一开始是不可能很完善的。只有随着越来越多的用户的使用,通过快速迭代试错、用户的反馈等手段,不断地在实践中去完善,才能继续创新。
初创期的业务对技术就一个要求:“快”,但这个时候却又是创业团队最弱小的时期,可能就几个技术人员,所以这个时候十八般武艺都需要用上:能买就买,有开源的就用开源的。
我还以淘宝和QQ为例。
第一版的淘宝https://blog.csdn.net/linlin_juejue/article/details/5959171
第一版的QQhttp://www.yixieshi.com/20770.html
可以看到最开始的淘宝和QQ与现在相比几乎看不出是同一个业务了。
2.发展期
当业务推出后经过市场验证如果是可行的,则吸引的用户就会越来越多,此时原来不完善的业务就进入了一个快速发展的时期。业务快速发展时期的主要目的是将原来不完善的业务逐渐完善,因此会有越来越多的新功能不断地加入到系统中。对于绝大部分技术团队来说,这个阶段技术的核心工作是快速地实现各种需求,只有这样才能满足业务发展的需要。
如何做到“快”,一般会经历下面几个阶段。
堆功能期
业务进入快速发展期的初期,此时团队规模也不大,业务需求又很紧,最快实现业务需求的方式是继续在原有的系统里面不断地增加新的功能,重构、优化、架构等方面的工作即使想做,也会受制于人力和业务发展的压力而放在一边。
优化期
“堆功能”的方式在刚开始的时候好用,因为系统还比较简单,但随着功能越来越多,系统开始变得越来越复杂,后面继续堆功能会感到越来越吃力,速度越来越慢。一种典型的场景是做一个需求要改好多地方,一不小心就改出了问题。直到有一天,技术团队或者产品人员再也受不了这种慢速的方式,终于下定决定要解决这个问题了。
如何解决这个问题,一般会分为两派:一派是优化派,一派是架构派。
优化派的核心思想是将现有的系统优化。例如采用重构、分层、优化某个MySQL查询语句将机械硬盘换成SSD将数据库从MySQL换成Oracle增加Memcache缓存等。优化派的优势是对系统改动较小优化可以比较快速地实施缺点就是可能过不了多久系统又撑不住了。
架构派的核心思想是调整系统架构,主要是将原来的大系统拆分为多个互相配合的小系统。例如,将购物系统拆分为登录认证子系统、订单系统、查询系统、分析系统等。架构派的优势是一次调整可以支撑比较长期的业务发展,缺点是动作较大、耗时较长,对业务的发展影响也比较大。
相信在很多公司都遇到这种情况,大部分情况下都是“优化派”会赢,主要的原因还是因为此时“优化”是最快的方式。至于说“优化派”支撑不了多久这个问题,其实也不用考虑太多,因为业务能否发展到那个阶段还是个未知数,保证当下的竞争力是最主要的问题。
架构期
经过优化期后如果业务能够继续发展慢慢就会发现优化也顶不住了毕竟再怎么优化系统的能力总是有极限的。Oracle再强大也不可能一台Oracle顶住1亿的交易量小型机再好也不可能一台机器支持100万在线人数。此时已经没有别的选择只能进行架构调整在优化期被压制的架构派开始扬眉吐气了甚至会骄傲地说“看看吧早就说要进行架构调整你们偏要优化现在还是顶不住了吧哼……”。
架构期可以用的手段很多,但归根结底可以总结为一个字“拆”,什么地方都可以拆。
拆功能:例如,将购物系统拆分为登录认证子系统、订单系统、查询系统、分析系统等。
拆数据库MySQL一台变两台2台变4台增加DBProxy、分库分表等。
拆服务器服务器一台变两台2台变4台增加负载均衡的系统如Nginx、HAProxy等。
3.竞争期
当业务继续发展已经形成一定规模后一定会有竞争对手开始加入行业来竞争毕竟谁都想分一块蛋糕甚至有可能一不小心还会成为下一个BAT。当竞争对手加入后大家互相学习和模仿业务更加完善也不断有新的业务创新出来而且由于竞争的压力对技术的要求是更上一层楼了。
新业务的创新给技术带来的典型压力就是新的系统会更多,同时,原有的系统也会拆得越来越多。两者合力的一个典型后果就是系统数量在原来的基础上又增加了很多。架构拆分后带来的美好时光又开始慢慢消逝,技术工作又开始进入了“慢”的状态,这又是怎么回事呢?
原来系统数量越来越多,到了一个临界点后就产生了质变,即系统数量的量变带来了技术工作的质变。主要体现在下面几个方面:
重复造轮子
系统越来越多,各系统相似的工作越来越多。例如,每个系统都有存储,都要用缓存,都要用数据库。新建一个系统,这些工作又要都做一遍,即使其他系统已经做过了一遍,这样怎么能快得起来?
系统交互一团乱麻
系统越来越多各系统的交互关系变成了网状。系统间的交互数量和系统的数量成平方比的关系。例如4个系统的交互路径是6个10个系统的交互路径是45个。每实现一个业务需求都需要几个甚至十几个系统一起改然后互相调用来调用去联调成了研发人员的灾难、联测成了测试人员的灾难、部署成了运维的灾难。
针对这个时期业务变化带来的问题,技术工作主要的解决手段有:
平台化
目的在于解决“重复造轮子”的问题。
存储平台化淘宝的TFS、京东JFS。
数据库平台化百度的DBProxy、淘宝TDDL。
缓存平台化Twitter的Twemproxy豆瓣的BeansDB、腾讯TTC。
服务化
目的在于解决“系统交互”的问题,常见的做法是通过消息队列来完成系统间的异步通知,通过服务框架来完成系统间的同步调用。
消息队列淘宝的Notify、MetaQ开源的Kafka、ActiveMQ等。
服务框架Facebook的thrift、当当网的Dubbox、淘宝的HSF等。
4.成熟期
当企业熬过竞争期,成为了行业的领头羊,或者整个行业整体上已经处于比较成熟的阶段,市场地位已经比较牢固后,业务创新的机会已经不大,竞争压力也没有那么激烈,此时求快求新已经没有很大空间,业务上开始转向为“求精”:我们的响应时间是否比竞争对手快?我们的用户体验是否比竞争对手好?我们的成本是否比竞争对手低……
此时技术上其实也基本进入了成熟期该拆的也拆了该平台化的也平台化了技术上能做的大动作其实也不多了更多的是进行优化。但有时候也会为了满足某个优化系统做很大的改变。例如为了将用户响应时间从200ms降低到50ms可能就需要从很多方面进行优化CDN、数据库、网络等。这个时候的技术优化没有固定的套路只能按照竞争的要求找出自己的弱项然后逐项优化。在逐项优化时可以采取之前各个时期采用的手段。
用户规模
互联网业务的发展第二个主要方向就是“用户量越来越大”。互联网业务的发展会经历“初创期、发展期、竞争期、成熟期”几个阶段,不同阶段典型的差别就是用户量的差别,用户量随着业务的发展而越来越大。
用户量增大对技术的影响主要体现在两个方面:性能要求越来越高、可用性要求越来越高。
1.性能
用户量增大给技术带来的第一个挑战就是性能要求越来越高。以互联网企业最常用的MySQL为例再简单的查询再高的硬件配置单台MySQL机器支撑的TPS和QPS最高也就是万级低的可能是几千高的也不过几万。当用户量增长后必然要考虑使用多台MySQL从一台MySQL到多台MySQL不是简单的数量的增加而是本质上的改变即原来集中式的存储变为了分布式的存储。
稍微有经验的工程师都会知道分布式将会带来复杂度的大幅度上升。以MySQL为例分布式MySQL要考虑分库分表、读写分离、复制、同步等很多问题。
2.可用性
用户量增大对技术带来的第二个挑战就是可用性要求越来越高。当你有1万个用户的时候宕机1小时可能也没有很大的影响但当你有了100万用户的时候宕机10分钟投诉电话估计就被打爆了这些用户再到朋友圈抱怨一下你的系统有多烂很可能你就不会再有机会发展下一个100万用户了。
除了口碑的影响可用性对收入的影响也会随着用户量增大而增大。1万用户宕机1小时你可能才损失了几千元100万用户宕机10分钟损失可能就是几十万元了。
量变到质变
通过前面的分析,我们可以看到互联网业务驱动技术发展的两大主要因素是复杂性和用户规模,而这两个因素的本质其实都是“量变带来质变”。
究竟用户规模发展到什么阶段才会由量变带来质变,虽然不同的业务有所差别,但基本上可以按照下面这个模型去衡量。
应对业务质变带来的技术压力,不同时期有不同的处理方式,但不管什么样的方式,其核心目标都是为了满足业务“快”的要求,当发现你的业务快不起来的时候,其实就是技术的水平已经跟不上业务发展的需要了,技术变革和发展的时候就到了。更好的做法是在问题还没有真正暴露出来就能够根据趋势预测下一个转折点,提前做好技术上的准备,这对技术人员的要求是非常高的。
小结
今天我为你讲了互联网技术演进的基本模式,希望对你有所帮助。
这就是今天的全部内容,留一道思考题给你吧,参考今天文章的方法,简单分析一下你所在行业,看看是否存在典型的技术演进模式?

View File

@@ -0,0 +1,90 @@
因收到Google相关通知网站将会择期关闭。相关通知内容
40 互联网架构模板:存储层技术
很多人对于BAT的技术有一种莫名的崇拜感觉得只有天才才能做出这样的系统但经过前面对架构的本质、架构的设计原则、架构的设计模式、架构演进等多方位的探讨和阐述你可以看到其实并没有什么神秘的力量和魔力融合在技术里面而是业务的不断发展推动了技术的发展这样一步一个脚印持续几年甚至十几年的发展才能达到当前技术复杂度和先进性。
抛开BAT各自差异很大的业务站在技术的角度来看其实BAT的技术架构基本是一样的。再将视角放大你会发现整个互联网行业的技术发展最后都是殊途同归。
如果你正处于一个创业公司或者正在为成为另一个BAT拼搏那么深入理解这种技术模式或者叫技术结构、技术架构对于自己和公司的发展都大有裨益。
互联网的标准技术架构如下图所示,这张图基本上涵盖了互联网技术公司的大部分技术点,不同的公司只是在具体的技术实现上稍有差异,但不会跳出这个框架的范畴。
从本期开始,我将逐层介绍每个技术点的产生背景、应用场景、关键技术,有的技术点可能已经在前面的架构模式部分有所涉及,因此就不再详细展开技术细节了,而是将关键技术点分门别类,进而形成一张架构大图,让架构师对一个公司的整体技术架构有一个完整的全貌认知。
今天我们首先来聊聊互联网架构模板的“存储层”技术。
SQL
SQL即我们通常所说的关系数据。前几年NoSQL火了一阵子很多人都理解为NoSQL是完全抛弃关系数据全部采用非关系型数据。但经过几年的试验后大家发现关系数据不可能完全被抛弃NoSQL不是No SQL而是Not Only SQL即NoSQL是SQL的补充。
所以互联网行业也必须依赖关系数据考虑到Oracle太贵还需要专人维护一般情况下互联网行业都是用MySQL、PostgreSQL这类开源数据库。这类数据库的特点是开源免费拿来就用但缺点是性能相比商业数据库要差一些。随着互联网业务的发展性能要求越来越高必然要面对一个问题将数据拆分到多个数据库实例才能满足业务的性能需求其实Oracle也一样只是时间早晚的问题
数据库拆分满足了性能的要求,但带来了复杂度的问题:数据如何拆分、数据如何组合?这个复杂度的问题解决起来并不容易,如果每个业务都去实现一遍,重复造轮子将导致投入浪费、效率降低,业务开发想快都快不起来。
所以互联网公司流行的做法是业务发展到一定阶段后就会将这部分功能独立成中间件例如百度的DBProxy、淘宝的TDDL。不过这部分的技术要求很高将分库分表做到自动化和平台化不是一件容易的事情所以一般是规模很大的公司才会自己做。中小公司建议使用开源方案例如MySQL官方推荐的MySQL Router、360开源的数据库中间件Atlas。
假如公司业务继续发展规模继续扩大SQL服务器越来越多如果每个业务都基于统一的数据库中间件独立部署自己的SQL集群就会导致新的复杂度问题具体表现在
数据库资源使用率不高,比较浪费。
各SQL集群分开维护投入的维护成本越来越高。
因此实力雄厚的大公司此时一般都会在SQL集群上构建SQL存储平台以对业务透明的形式提供资源分配、数据备份、迁移、容灾、读写分离、分库分表等一系列服务例如淘宝的UMPUnified MySQL Platform系统。
NoSQL
首先NoSQL在数据结构上与传统的SQL的不同例如典型的Memcache的key-value结构、Redis的复杂数据结构、MongoDB的文档数据结构其次NoSQL无一例外地都会将性能作为自己的一大卖点。NoSQL的这两个特点很好地弥补了关系数据库的不足因此在互联网行业NoSQL的应用基本上是基础要求。
由于NoSQL方案一般自己本身就提供集群的功能例如Memcache的一致性Hash集群、Redis 3.0的集群因此NoSQL在刚开始应用时很方便不像SQL分库分表那么复杂。一般公司也不会在开始时就考虑将NoSQL包装成存储平台但如果公司发展很快例如Memcache的节点有上千甚至几千时NoSQL存储平台就很有意义了。首先是存储平台通过集中管理能够大大提升运维效率其次是存储平台可以大大提升资源利用效率2000台机器如果利用率能提升10%就可以减少200台机器一年几十万元就节省出来了。
所以NoSQL发展到一定规模后通常都会在NoSQL集群的基础之上再实现统一存储平台统一存储平台主要实现这几个功能
资源动态按需动态分配例如同一台Memcache服务器可以根据内存利用率分配给多个业务使用。
资源自动化管理例如新业务只需要申请多少Memcache缓存空间就可以了无需关注具体是哪些Memcache服务器在为自己提供服务。
故障自动化处理例如某台Memcache服务器挂掉后有另外一台备份Memcache服务器能立刻接管缓存请求不会导致丢失很多缓存数据。
当然要发展到这个阶段一般也是大公司才会这么做简单来说就是如果只有几十台NoSQL服务器做存储平台收益不大但如果有几千台NoSQL服务器NoSQL存储平台就能够产生很大的收益。
小文件存储
除了关系型的业务数据互联网行业还有很多用于展示的数据。例如淘宝的商品图片、商品描述Facebook的用户图片新浪微博的一条微博内容等。这些数据具有三个典型特征一是数据小一般在1MB以下二是数量巨大Facebook在2013年每天上传的照片就达到了3.5亿张三是访问量巨大Facebook每天的访问量超过10亿。
由于互联网行业基本上每个业务都会有大量的小数据,如果每个业务都自己去考虑如何设计海量存储和海量访问,效率自然会低,重复造轮子也会投入浪费,所以自然而然就要将小文件存储做成统一的和业务无关的平台。
和SQL和NoSQL不同的是小文件存储不一定需要公司或者业务规模很大基本上认为业务在起步阶段就可以考虑做小文件统一存储。得益于开源运动的发展和最近几年大数据的火爆在开源方案的基础上封装一个小文件存储平台并不是太难的事情。例如HBase、Hadoop、Hypertable、FastDFS等都可以作为小文件存储的底层平台只需要将这些开源方案再包装一下基本上就可以用了。
典型的小文件存储有淘宝的TFS、京东JFS、Facebook的Haystack。
下图是淘宝TFS的架构
大文件存储
互联网行业的大文件主要分为两类一类是业务上的大数据例如Youtube的视频、电影网站的电影另一类是海量的日志数据例如各种访问日志、操作日志、用户轨迹日志等。和小文件的特点正好相反大文件的数量没有小文件那么多但每个文件都很大几百MB、几个GB都是常见的几十GB、几TB也是有可能的因此在存储上和小文件有较大差别不能直接将小文件存储系统拿来存储大文件。
说到大文件特别要提到Google和YahooGoogle的3篇大数据论文Bigtable/Map- Reduce/GFS开启了一个大数据的时代而Yahoo开源的Hadoop系列HDFS、HBase等基本上垄断了开源界的大数据处理。当然江山代有才人出长江后浪推前浪Hadoop后又有更多优秀的开源方案被贡献出来现在随便走到大街上拉住一个程序员如果他不知道大数据那基本上可以确定是“火星程序员”。
对照Google的论文构建一套完整的大数据处理方案的难度和成本实在太高而且开源方案现在也很成熟了所以大数据存储和处理这块反而是最简单的因为你没有太多选择只能用这几个流行的开源方案例如Hadoop、HBase、Storm、Hive等。实力雄厚一些的大公司会基于这些开源方案结合自己的业务特点封装成大数据平台例如淘宝的云梯系统、腾讯的TDW系统。
下面是Hadoop的生态圈
小结
今天我为你讲了互联网架构模板中的存储层技术,可以看到当公司规模发展到一定阶段后,基本上都是基于某个开源方案搭建统一的存储平台,希望对你有所帮助。
这就是今天的全部内容,留一道思考题给你吧,既然存储技术发展到最后都是存储平台,为何没有出现存储平台的开源方案,但云计算却都提供了存储平台方案?

View File

@@ -0,0 +1,169 @@
因收到Google相关通知网站将会择期关闭。相关通知内容
41 互联网架构模板:开发层和服务层技术
上一期,我介绍了互联网架构模板中的存储层技术。关于这部分内容,我将逐层介绍每个技术点的产生背景、应用场景和关键技术,希望让你可以对整体的技术架构有一个全貌认知。
今天我们来聊聊互联网架构模板的“开发层”和“服务层”技术。
开发层技术
1.开发框架
在专栏第38、39期中我们深入分析了互联网业务发展的一个特点复杂度越来越高。复杂度增加的典型现象就是系统越来越多不同的系统由不同的小组开发。如果每个小组用不同的开发框架和技术则会带来很多问题典型的问题有
技术人员之间没有共同的技术语言,交流合作少。
每类技术都需要投入大量的人力和资源并熟练精通。
不同团队之间人员无法快速流动,人力资源不能高效的利用。
所以互联网公司都会指定一个大的技术方向然后使用统一的开发框架。例如Java相关的开发框架SSH、SpringMVC、PlayRuby的Ruby on RailsPHP的ThinkPHPPython的Django等。使用统一的开发框架能够解决上面提到的各种问题大大提升组织和团队的开发效率。
对于框架的选择,有一个总的原则:优选成熟的框架,避免盲目追逐新技术!
为什么呢?
首先,成熟的框架资料文档齐备,各种坑基本上都有人踩过了,遇到问题很容易通过搜索来解决。
其次,成熟的框架受众更广,招聘时更加容易招到合适的人才。
第三,成熟的框架更加稳定,不会出现大的变动,适合长期发展。
2.Web服务器
开发框架只是负责完成业务功能的开发,真正能够运行起来给用户提供服务,还需要服务器配合。
独立开发一个成熟的Web服务器成本非常高况且业界又有那么多成熟的开源Web服务器所以互联网行业基本上都是“拿来主义”挑选一个流行的开源服务器即可。大一点的公司可能会在开源服务器的基础上结合自己的业务特点做二次开发例如淘宝的Tengine但一般公司基本上只需要将开源服务器摸透优化一下参数调整一下配置就差不多了。
选择一个服务器主要和开发语言相关例如Java的有Tomcat、JBoss、Resin等PHP/Python的用Nginx当然最保险的就是用Apache了什么语言都支持。
你可能会担心Apache的性能之类的问题其实不用过早担心这个等到业务真的发展到Apache撑不住的时候再考虑切换也不迟那时候你有的是钱有的是人有的是时间。
3.容器
容器是最近几年才开始火起来的其中以Docker为代表在BAT级别的公司已经有较多的应用。例如腾讯万台规模的Docker应用实践http://www.infoq.com/cn/articles/tencent-millions-scale-docker-application-practice、新浪微博红包的大规模Docker集群http://www.infoq.com/cn/articles/large-scale-docker-cluster-practise-experience-share等。
传统的虚拟化技术是虚拟机解决了跨平台的问题但由于虚拟机太庞大启动又慢运行时太占资源在互联网行业并没有大规模应用而Docker的容器技术虽然没有跨平台但启动快几乎不占资源推出后立刻就火起来了预计Docker类的容器技术将是技术发展的主流方向。
千万不要以为Docker只是一个虚拟化或者容器技术它将在很大程度上改变目前的技术形势
运维方式会发生革命性的变化Docker启动快几乎不占资源随时启动和停止基于Docker打造自动化运维、智能化运维将成为主流方式。
设计模式会发生本质上的变化:启动一个新的容器实例代价如此低,将鼓励设计思路朝“微服务”的方向发展。
例如,一个传统的网站包括登录注册、页面访问、搜索等功能,没有用容器的情况下,除非有特别大的访问量,否则这些功能开始时都是集成在一个系统里面的;有了容器技术后,一开始就可以将这些功能按照服务的方式设计,避免后续访问量增大时又要重构系统。
服务层技术
互联网业务的不断发展带来了复杂度的不断提升业务系统也越来越多系统间相互依赖程度加深。比如说为了完成A业务系统可能需要B、C、D、E等十几个其他系统进行合作。从数学的角度进行评估可以发现系统间的依赖是呈指数级增长的3个系统相互关联的路径为3条6个系统相互关联的路径为15条。
服务层的主要目标其实就是为了降低系统间相互关联的复杂度。
1.配置中心
故名思议,配置中心就是集中管理各个系统的配置。
当系统数量不多的时候,一般是各系统自己管理自己的配置,但系统数量多了以后,这样的处理方式会有问题:
某个功能上线时,需要多个系统配合一起上线,分散配置时,配置检查、沟通协调需要耗费较多时间。
处理线上问题时,需要多个系统配合查询相关信息,分散配置时,操作效率很低,沟通协调也需要耗费较多时间。
各系统自己管理配置时,一般是通过文本编辑的方式修改的,没有自动的校验机制,容易配置错误,而且很难发现。
例如我曾经遇到将IP地址的数字0误敲成了键盘的字母O肉眼非常难发现但程序检查其实就很容易。
实现配置中心主要就是为了解决上面这些问题,将配置中心做成通用的系统的好处有:
集中配置多个系统,操作效率高。
所有配置都在一个集中的地方,检查方便,协作效率高。
配置中心可以实现程序化的规则检查避免常见的错误。比如说检查最小值、最大值、是否IP地址、是否URL地址都可以用正则表达式完成。
配置中心相当于备份了系统的配置,当某些情况下需要搭建新的环境时,能够快速搭建环境和恢复业务。
整机磁盘坏掉、机器主板坏掉……遇到这些不可恢复的故障时基本上只能重新搭建新的环境。程序包肯定是已经有的加上配置中心的配置能够很快搭建新的运行环境恢复业务。否则几十个配置文件重新一个个去Vim中修改耗时很长还很容易出错。
下面是配置中心简单的设计,其中通过“系统标识 + host + port”来标识唯一一个系统运行实例是常见的设计方法。
2.服务中心
当系统数量不多的时候,系统间的调用一般都是直接通过配置文件记录在各系统内部的,但当系统数量多了以后,这种方式就存在问题了。
比如说总共有10个系统依赖A系统的X接口A系统实现了一个新接口Y能够更好地提供原有X接口的功能如果要让已有的10个系统都切换到Y接口则这10个系统的几十上百台机器的配置都要修改然后重启可想而知这个效率是很低的。
除此以外如果A系统总共有20台机器现在其中5台出故障了其他系统如果是通过域名访问A系统则域名缓存失效前还是可能访问到这5台故障机器的如果其他系统通过IP访问A系统那么A系统每次增加或者删除机器其他所有10个系统的几十上百台机器都要同步修改这样的协调工作量也是非常大的。
服务中心就是为了解决上面提到的跨系统依赖的“配置”和“调度”问题。
服务中心的实现一般来说有两种方式:服务名字系统和服务总线系统。
服务名字系统Service Name System
看到这个翻译相信你会立刻联想到DNS即Domain Name System。没错两者的性质是基本类似的。
DNS的作用将域名解析为IP地址主要原因是我们记不住太多的数字IP域名就容易记住。服务名字系统是为了将Service名称解析为“host + port + 接口名称”但是和DNS一样真正发起请求的还是请求方。
基本的设计如下:
服务总线系统Service Bus System
看到这个翻译,相信你可能立刻联想到计算机的总线。没错,两者的本质也是基本类似的。
相比服务名字系统,服务总线系统更进一步了:由总线系统完成调用,服务请求方都不需要直接和服务提供方交互了。
基本的设计如下:
“服务名字系统”和“服务总线系统”简单对比如下表所示:
3.消息队列
互联网业务的一个特点是“快”这就要求很多业务处理采用异步的方式。例如大V发布一条微博后系统需要发消息给关注的用户我们不可能等到所有消息都发送给关注用户后再告诉大V说微博发布成功了只能先让大V发布微博然后再发消息给关注用户。
传统的异步通知方式是由消息生产者直接调用消息消费者提供的接口进行通知的,但当业务变得庞大,子系统数量增多时,这样做会导致系统间交互非常复杂和难以管理,因为系统间互相依赖和调用,整个系统的结构就像一张蜘蛛网,如下图所示:
消息队列就是为了实现这种跨系统异步通知的中间件系统。消息队列既可以“一对一”通知,也可以“一对多”广播。以微博为例,可以清晰地看到异步通知的实现和作用,如下图所示。
对比前面的蜘蛛网架构,可以清晰地看出引入消息队列系统后的效果:
整体结构从网状结构变为线性结构,结构清晰;
消息生产和消息消费解耦,实现简单;
增加新的消息消费者,消息生产者完全不需要任何改动,扩展方便;
消息队列系统可以做高可用、高性能,避免各业务子系统各自独立做一套,减轻工作量;
业务子系统只需要聚焦业务即可,实现简单。
消息队列系统基本功能的实现比较简单但要做到高性能、高可用、消息时序性、消息事务性则比较难。业界已经有很多成熟的开源实现方案如果要求不高基本上拿来用即可例如RocketMQ、Kafka、ActiveMQ等。但如果业务对消息的可靠性、时序、事务性要求较高时则要深入研究这些开源方案否则很容易踩坑。
开源的用起来方便,但要改就很麻烦了。由于其相对比较简单,很多公司也会花费人力和时间重复造一个轮子,这样也有好处,因为可以根据自己的业务特点做快速的适配开发。
小结
今天我为你讲了互联网架构模板中的开发层和服务层技术,希望对你有所帮助。
这就是今天的全部内容,留一道思考题给你吧,使用统一的开发框架和开发语言可以让团队开发效率更高,但这样做会带来什么问题?如何解决?

View File

@@ -0,0 +1,104 @@
因收到Google相关通知网站将会择期关闭。相关通知内容
42 互联网架构模板:网络层技术
除了复杂度,互联网业务发展的另外两个关键特点是“高性能”和“高可用”。通常情况下,我们在设计高可用和高性能系统的时候,主要关注点在系统本身的复杂度,然后通过各种手段来实现高可用和高性能的要求,例如我前面介绍的计算高性能架构模式、存储高可用架构模式等。但是当我们站在一个公司的的角度来思考架构的时候,单个系统的高可用和高性能并不等于整体业务的高可用和高性能,互联网业务的高性能和高可用需要从更高的角度去设计,这个高点就是“网络”,所以我将相关措施统一划归为“网络层”。注意这里的网络层和通常理解的如何搭建一个局域网这种概念不一样,这里强调的是站在网络层的角度整体设计架构,而不是某个具体网络的搭建。
接下来我将介绍互联网架构模板的“网络层”技术的几个关键架构设计点,部分内容专栏前面已经有深入阐述,今天作为概要的总结把它们归纳一下。
负载均衡
顾名思议负载均衡就是将请求均衡地分配到多个系统上。使用负载均衡的原因也很简单每个系统的处理能力是有限的为了应对大容量的访问必须使用多个系统。例如一台32核64GB内存的机器性能测试数据显示每秒处理Hello World的HTTP请求不超过2万实际业务机器处理HTTP请求每秒可能才几百QPS而互联网业务并发超过1万是比较常见的遇到双十一、过年发红包这些极端场景每秒可以达到几十万的请求。
1.DNS
DNS是最简单也是最常见的负载均衡方式一般用来实现地理级别的均衡。例如北方的用户访问北京的机房南方的用户访问广州的机房。一般不会使用DNS来做机器级别的负载均衡因为太耗费IP资源了。例如百度搜索可能要10000台以上机器不可能将这么多机器全部配置公网IP然后用DNS来做负载均衡。有兴趣的读者可以在Linux用“dig baidu.com”命令看看实际上用了几个IP地址。
DNS负载均衡的优点是通用全球通用、成本低申请域名注册DNS即可但缺点也比较明显主要体现在
DNS缓存的时间比较长即使将某台业务机器从DNS服务器上删除由于缓存的原因还是有很多用户会继续访问已经被删除的机器。
DNS不够灵活。DNS不能感知后端服务器的状态只能根据配置策略进行负载均衡无法做到更加灵活的负载均衡策略。比如说某台机器的配置比其他机器要好很多理论上来说应该多分配一些请求给它但DNS无法做到这一点。
所以对于时延和故障敏感的业务有实力的公司可能会尝试实现HTTP-DNS的功能即使用HTTP协议实现一个私有的DNS系统。HTTP-DNS主要应用在通过App提供服务的业务上因为在App端可以实现灵活的服务器访问策略如果是Web业务实现起来就比较麻烦一些因为URL的解析是由浏览器来完成的只有Javascript的访问可以像App那样实现比较灵活的控制。
HTTP-DNS的优缺点有
灵活HTTP-DNS可以根据业务需求灵活的设置各种策略。
可控HTTP-DNS是自己开发的系统IP更新、策略更新等无需依赖外部服务商。
及时HTTP-DNS不受传统DNS缓存的影响可以非常快地更新数据、隔离故障。
开发成本高:没有通用的解决方案,需要自己开发。
侵入性需要App基于HTTP-DNS进行改造。
2.Nginx 、LVS 、F5
DNS用于实现地理级别的负载均衡而Nginx、LVS、F5用于同一地点内机器级别的负载均衡。其中Nginx是软件的7层负载均衡LVS是内核的4层负载均衡F5是硬件的4层负载均衡。
软件和硬件的区别就在于性能硬件远远高于软件Ngxin的性能是万级一般的Linux服务器上装个Nginx大概能到5万/秒LVS的性能是十万级没有具体测试过据说可达到80万/秒F5性能是百万级从200万/秒到800万/秒都有。硬件虽然性能高但是单台硬件的成本也很高一台最便宜的F5都是几十万但是如果按照同等请求量级来计算成本的话实际上硬件负载均衡设备可能会更便宜例如假设每秒处理100万请求用一台F5就够了但用Nginx可能要20台这样折算下来用F5的成本反而低。因此通常情况下如果性能要求不高可以用软件负载均衡如果性能要求很高推荐用硬件负载均衡。
4层和7层的区别就在于协议和灵活性。Nginx支持HTTP、E-mail协议而LVS和F5是4层负载均衡和协议无关几乎所有应用都可以做例如聊天、数据库等。
目前很多云服务商都已经提供了负载均衡的产品例如阿里云的SLB、UCloud的ULB等中小公司直接购买即可。
CDN
CDN是为了解决用户网络访问时的“最后一公里”效应本质上是一种“以空间换时间”的加速策略即将内容缓存在离用户最近的地方用户访问的是缓存的内容而不是站点实时的内容。
下面是简单的CDN请求流程示意图
CDN经过多年的发展已经变成了一个很庞大的体系分布式存储、全局负载均衡、网络重定向、流量控制等都属于CDN的范畴尤其是在视频、直播等领域如果没有CDN用户是不可能实现流畅观看内容的。
幸运的是大部分程序员和架构师都不太需要深入理解CDN的细节因为CDN作为网络的基础服务独立搭建的成本巨大很少有公司自己设计和搭建CDN系统从CDN服务商购买CDN服务即可目前有专门的CDN服务商例如网宿和蓝汛也有云计算厂家提供CDN服务例如阿里云和腾讯云都提供CDN的服务。
多机房
从架构上来说,单机房就是一个全局的网络单点,在发生比较大的故障或者灾害时,单机房难以保证业务的高可用。例如,停电、机房网络中断、地震、水灾等都有可能导致一个机房完全瘫痪。
多机房设计最核心的因素就是如何处理时延带来的影响,常见的策略有:
1.同城多机房
同一个城市多个机房,距离不会太远,可以投入重金,搭建私有的高速网络,基本上能够做到和同机房一样的效果。
这种方式对业务影响很小,但投入较大,如果不是大公司,一般是承受不起的;而且遇到极端的地震、水灾等自然灾害,同城多机房也是有很大风险的。
2.跨城多机房
在不同的城市搭建多个机房机房间通过网络进行数据复制例如MySQL主备复制但由于跨城网络时延的问题业务上需要做一定的妥协和兼容比如不需要数据的实时强一致性只是保证最终一致性。
例如微博类产品B用户关注了A用户A用户在北京机房发布了一条微博B在广州机房不需要立刻看到A用户发的微博等10分钟看到也可以。
这种方式实现简单,但和业务有很强的相关性,微博可以这样做,支付宝的转账业务就不能这样做,因为用户余额是强一致性的。
3.跨国多机房
和跨城多机房类似,只是地理上分布更远,时延更大。由于时延太大和用户跨国访问实在太慢,跨国多机房一般仅用于备份和服务本国用户。
多中心
多中心必须以多机房为前提,但从设计的角度来看,多中心相比多机房是本质上的飞越,难度也高出一个等级。
简单来说多机房的主要目标是灾备当机房故障时可以比较快速地将业务切换到另外一个机房这种切换操作允许一定时间的中断例如10分钟、1个小时而且业务也可能有损失例如某些未同步的数据不能马上恢复或者要等几天才恢复甚至永远都不能恢复了。因此相比多机房来说多中心的要求就高多了要求每个中心都同时对外提供服务且业务能够自动在多中心之间切换故障后不需人工干预或者很少的人工干预就能自动恢复。
多中心设计的关键就在于“数据一致性”和“数据事务性”如何保证,这两个难点都和业务紧密相关,目前没有很成熟的且通用的解决方案,需要基于业务的特性进行详细的分析和设计。以淘宝为例,淘宝对外宣称自己是多中心的,但是在实际设计过程中,商品浏览的多中心方案、订单的多中心方案、支付的多中心方案都需要独立设计和实现。
正因为多中心设计的复杂性不一定所有业务都能实现多中心目前国内的银行、支付宝这类系统就没有完全实现多中心不然也不会出现挖掘机一铲子下去支付宝中断4小时的故障。
有关多中心设计更详细的内容,你可以查看专栏第[28]、[29]、[30]期的内容。
小结
今天我为你讲了互联网架构模板中的“网络层”技术,希望对你有所帮助。
这就是今天的全部内容留一道思考题给你吧为什么可以购买负载均衡和CDN服务但却不能购买多机房和多中心服务

View File

@@ -0,0 +1,92 @@
因收到Google相关通知网站将会择期关闭。相关通知内容
43 互联网架构模板:用户层和业务层技术
上一期,我从计算机网络层的角度谈了应对“高性能”和“高可用”的整体架构设计。今天,我将从“用户层”和“业务层”的角度谈谈常见的应用场景和关键技术。
用户层技术
1.用户管理
互联网业务的一个典型特征就是通过互联网将众多分散的用户连接起来,因此用户管理是互联网业务必不可少的一部分。
稍微大一点的互联网业务肯定会涉及多个子系统这些子系统不可能每个都管理这么庞大的用户由此引申出用户管理的第一个目标单点登录SSO又叫统一登录。单点登录的技术实现手段较多例如cookie、JSONP、token等目前最成熟的开源单点登录方案当属CAS其架构如下https://apereo.github.io/cas/4.2.x/planning/Architecture.html
除此之外当业务做大成为了平台后开放成为了促进业务进一步发展的手段需要允许第三方应用接入由此引申出用户管理的第二个目标授权登录。现在最流行的授权登录就是OAuth 2.0协议,基本上已经成为了事实上的标准,如果要做开放平台,则最好用这个协议,私有协议漏洞多,第三方接入也麻烦。
用户管理系统面临的主要问题是用户数巨大一般至少千万级QQ、微信、支付宝这种巨无霸应用都是亿级用户。不过也不要被这个数据给吓倒了用户管理虽然数据量巨大但实现起来并不难原因是什么呢 因为用户数据量虽然大但是不同用户之间没有太强的业务关联A用户登录和B用户登录基本没有关系。因此虽然数据量巨大但我们用一个简单的负载均衡架构就能轻松应对。
用户管理的基本架构如下:
2.消息推送
消息推送根据不同的途径分为短信、邮件、站内信、App推送。除了App不同的途径基本上调用不同的API即可完成技术上没有什么难度。例如短信需要依赖运营商的短信接口邮件需要依赖邮件服务商的邮件接口站内信是系统提供的消息通知功能。
App目前主要分为iOS和Android推送iOS系统比较规范和封闭基本上只能使用苹果的APNS但Android就不一样了在国外用GCM和APNS差别不大但是在国内情况就复杂多了首先是GCM不能用其次是各个手机厂商都有自己的定制的Android消息推送实现也不完全一样。因此Android的消息推送就五花八门了大部分有实力的大厂都会自己实现一套消息推送机制例如阿里云移动推送、腾讯信鸽推送、百度云推送也有第三方公司提供商业推送服务例如友盟推送、极光推送等。
通常情况下对于中小公司如果不涉及敏感数据Android系统上推荐使用第三方推送服务因为毕竟是专业做推送服务的消息到达率是有一定保证的。
如果涉及敏感数据需要自己实现消息推送这时就有一定的技术挑战了。消息推送主要包含3个功能设备管理唯一标识、注册、注销、连接管理和消息管理技术上面临的主要挑战有
海量设备和用户管理
消息推送的设备数量众多,存储和管理这些设备是比较复杂的;同时,为了针对不同用户进行不同的业务推广,还需要收集用户的一些信息,简单来说就是将用户和设备关联起来,需要提取用户特征对用户进行分类或者打标签等。
连接保活
要想推送消息必须有连接通道,但是应用又不可能一直在前台运行,大部分设备为了省电省流量等原因都会限制应用后台运行,限制应用后台运行后连接通道可能就被中断了,导致消息无法及时的送达。连接保活是整个消息推送设计中细节和黑科技最多的地方,例如应用互相拉起、找手机厂商开白名单等。
消息管理
实际业务运营过程中,并不是每个消息都需要发送给每个用户,而是可能根据用户的特征,选择一些用户进行消息推送。由于用户特征变化很大,各种排列组合都有可能,将消息推送给哪些用户这部分的逻辑要设计得非常灵活,才能支撑花样繁多的业务需求,具体的设计方案可以采取规则引擎之类的微内核架构技术。
3.存储云、图片云
互联网业务场景中,用户会上传多种类型的文件数据,例如微信用户发朋友圈时上传图片,微博用户发微博时上传图片、视频,优酷用户上传视频,淘宝卖家上传商品图片等,这些文件具备几个典型特点:
数据量大用户基数大用户上传行为频繁例如2016年的时候微信朋友圈每天上传图片就达到了10亿张http://mi.techweb.com.cn/tmt/2016-05-252338330.shtml
文件体积小大部分图片是几百KB到几MB短视频播放时间也是在几分钟内。
访问有时效性:大部分文件是刚上传的时候访问最多,随着时间的推移访问量越来越小。
为了满足用户的文件上传和存储需求,需要对用户提供文件存储和访问功能,这里就需要用到前面我在[专栏第40期]介绍“存储层”技术时提到的“小文件存储”技术。简单来说存储云和图片云通常的实现都是“CDN + 小文件存储”现在有了“云”之后除非BAT级别一般不建议自己再重复造轮子了直接买云服务可能是最快也是最经济的方式。
既然存储云和图片云都是基于“CDN + 小文件存储”的技术,为何不统一一套系统,而将其拆分为两个系统呢?这是因为“图片”业务的复杂性导致的,普通的文件基本上提供存储和访问就够了,而图片涉及的业务会更多,包括裁剪、压缩、美化、审核、水印等处理,因此通常情况下图片云会拆分为独立的系统对用户提供服务。
业务层技术
互联网的业务千差万别,不同的业务分解下来有不同的系统,所以业务层没有办法提炼一些公共的系统或者组件。抛开业务上的差异,各个互联网业务发展最终面临的问题都是类似的:业务复杂度越来越高。也就是说,业务层面对的主要技术挑战是“复杂度”。
复杂度越来越高的一个主要原因就是系统越来越庞大,业务越来越多。幸运的是,面对业务层的技术挑战,我们有一把“屠龙宝刀”,不管什么业务难题,用上“屠龙宝刀”问题都能迎刃而解。这把“屠龙宝刀”就是“拆”,化整为零、分而治之,将整体复杂性分散到多个子业务或者子系统里面去。具体拆的方式你可以查看专栏前面可扩展架构模式部分的分层架构、微服务、微内核等。
我以一个简单的电商系统为例,如下图所示:
我这个模拟的电商系统经历了3个发展阶段
第一阶段所有功能都在1个系统里面。
第二阶段将商品和订单拆分到2个子系统里面。
第三阶段商品子系统和订单子系统分别拆分成了更小的6个子系统。
上面只是个样例,实际上随着业务的发展,子系统会越来越多,据说淘宝内部大大小小的已经有成百上千的子系统了。
随着子系统数量越来越多如果达到几百上千另外一个复杂度问题又会凸显出来子系统数量太多已经没有人能够说清楚业务的调用流程了出了问题排查也会特别复杂。此时应该怎么处理呢总不可能又将子系统合成大系统吧最终答案还是“合”正所谓“合久必分、分久必合”但合的方式不一样此时采取的“合”的方式是按照“高内聚、低耦合”的原则将职责关联比较强的子系统合成一个虚拟业务域然后通过网关对外统一呈现类似于设计模式中的Facade模式。同样以电商为样例采用虚拟业务域后其架构如下
小结
今天我为你讲了互联网架构模板中的用户层技术和业务层技术,希望对你有所帮助。
这就是今天的全部内容,留一道思考题给你吧,虚拟业务域划分的粒度需要粗一些还是要细一些?你建议虚拟业务域的数量大概是多少,理由是什么?

View File

@@ -0,0 +1,159 @@
因收到Google相关通知网站将会择期关闭。相关通知内容
44 互联网架构模板:平台技术
当业务规模比较小、系统复杂度不高时,运维、测试、数据分析、管理等支撑功能主要由各系统或者团队独立完成。随着业务规模越来越大,系统复杂度越来越高,子系统数量越来越多,如果继续采取各自为政的方式来实现这些支撑功能,会发现重复工作非常多。因此我们自然而然就会想到将这些支撑功能做成平台,避免重复造轮子,减少不规范带来的沟通和协作成本。
今天,我就来聊聊互联网架构模板的“平台”技术。由于每个平台本身都是一个庞大的体系,专栏只是介绍一下平台的核心职责和关键设计点,具体细节就不详细展开了。
运维平台
运维平台核心的职责分为四大块:配置、部署、监控、应急,每个职责对应系统生命周期的一个阶段,如下图所示:
配置主要负责资源的管理。例如机器管理、IP地址管理、虚拟机管理等。
部署:主要负责将系统发布到线上。例如,包管理、灰度发布管理、回滚等。
监控:主要负责收集系统上线运行后的相关数据并进行监控,以便及时发现问题。
应急主要负责系统出故障后的处理。例如停止程序、下线故障机器、切换IP等。
运维平台的核心设计要素是“四化”:标准化、平台化、自动化、可视化。
1.标准化
需要制定运维标准,规范配置管理、部署流程、监控指标、应急能力等,各系统按照运维标准来实现,避免不同的系统不同的处理方式。标准化是运维平台的基础,没有标准化就没有运维平台。
如果某个系统就是无法改造自己来满足运维标准那该怎么办呢常见的做法是不改造系统由中间方来完成规范适配。例如某个系统对外提供了RESTful接口的方式来查询当前的性能指标而运维标准是性能数据通过日志定时上报那么就可以写一个定时程序访问RESTful接口获取性能数据然后转换为日志上报到运维平台。
2.平台化
传统的手工运维方式需要投入大量人力,效率低,容易出错,因此需要在运维标准化的基础上,将运维的相关操作都集成到运维平台中,通过运维平台来完成运维工作。
运维平台的好处有:
可以将运维标准固化到平台中,无须运维人员死记硬背运维标准。
运维平台提供简单方便的操作,相比之下人工操作低效且容易出错。
运维平台是可复用的,一套运维平台可以支撑几百上千个业务系统。
3.自动化
传统手工运维方式效率低下的一个主要原因就是要执行大量重复的操作,运维平台可以将这些重复操作固化下来,由系统自动完成。
例如,一次手工部署需要登录机器、上传包、解压包、备份旧系统、覆盖旧系统、启动新系统,这个过程中需要执行大量的重复或者类似的操作。有了运维平台后,平台需要提供自动化的能力,完成上述操作,部署人员只需要在最开始单击“开始部署”按钮,系统部署完成后通知部署人员即可。
类似的还有监控有了运维平台后运维平台可以实时收集数据并进行初步分析当发现数据异常时自动发出告警无须运维人员盯着数据看或者写一大堆“grep + awk + sed”来分析日志才能发现问题。
4.可视化
运维平台有非常多的数据如果全部通过人工去查询数据再来判断则效率很低。尤其是在故障应急时时间就是生命处理问题都是争分夺秒能减少1分钟的时间就可能挽回几十万元的损失可视化的主要目的就是为了提升数据查看效率。
可视化的原理和汽车仪表盘类似,如果只是一连串的数字显示在屏幕上,相信大部分人一看到一连串的数字,第一感觉是眼花,而且也很难将数据与具体的情况联系起来。而有了仪表盘后,通过仪表盘的指针偏离幅度及指针指向的区域颜色,能够一目了然地看出当前的状态是低速、中速还是高速。
可视化相比简单的数据罗列,具备下面这些优点:
能够直观地看到数据的相关属性例如汽车仪表盘中的数据最小值是0最大是100单位是MPH。
能够将数据的含义展示出来,例如汽车仪表盘中不同速度的颜色指示。
能够将关联数据整合一起展示,例如汽车仪表盘的速度和里程。
测试平台
测试平台核心的职责当然就是测试了,包括单元测试、集成测试、接口测试、性能测试等,都可以在测试平台来完成。
测试平台的核心目的是提升测试效率,从而提升产品质量,其设计关键就是自动化。传统的测试方式是测试人员手工执行测试用例,测试效率低,重复的工作多。通过测试平台提供的自动化能力,测试用例能够重复执行,无须人工参与,大大提升了测试效率。
为了达到“自动化”的目标,测试平台的基本架构如下图所示:
1.用例管理
测试自动化的主要手段就是通过脚本或者代码来进行测试例如单元测试用例是代码、接口测试用例可以用Python来写、可靠性测试用例可以用Shell来写。为了能够重复执行这些测试用例测试平台需要将用例管理起来管理的维度包括业务、系统、测试类型、用例代码。例如网购业务的订单系统的接口测试用例。
2.资源管理
测试用例要放到具体的运行环境中才能真正执行运行环境包括硬件服务器、手机、平板电脑等、软件操作系统、数据库、Java虚拟机等、业务系统被测试的系统
除了性能测试一般的自动化测试对性能要求不高所以为了提升资源利用率大部分的测试平台都会使用虚拟技术来充分利用硬件资源如虚拟机、Docker等技术。
3.任务管理
任务管理的主要职责是将测试用例分配到具体的资源上执行,跟踪任务的执行情况。任务管理是测试平台设计的核心,它将测试平台的各个部分串联起来从而完成自动化测试。
4.数据管理
测试任务执行完成后需要记录各种相关的数据例如执行时间、执行结果、用例执行期间的CPU、内存占用情况等这些数据具备下面这些作用
展现当前用例的执行情况。
作为历史数据方便后续的测试与历史数据进行对比从而发现明显的变化趋势。例如某个版本后单元测试覆盖率从90%下降到70%。
作为大数据的一部分可以基于测试的任务数据进行一些数据挖掘。例如某个业务一年执行了10000个用例测试另外一个业务只执行了1000个用例测试两个业务规模和复杂度差不多为何差异这么大
数据平台
数据平台的核心职责主要包括三部分:数据管理、数据分析和数据应用。每一部分又包含更多的细分领域,详细的数据平台架构如下图所示:
1.数据管理
数据管理包含数据采集、数据存储、数据访问和数据安全四个核心职责,是数据平台的基础功能。
数据采集:从业务系统搜集各类数据。例如,日志、用户行为、业务数据等,将这些数据传送到数据平台。
数据存储:将从业务系统采集的数据存储到数据平台,用于后续数据分析。
数据访问负责对外提供各种协议用于读写数据。例如SQL、Hive、Key-Value等读写协议。
数据安全:通常情况下数据平台都是多个业务共享的,部分业务敏感数据需要加以保护,防止被其他业务读取甚至修改,因此需要设计数据安全策略来保护数据。
2.数据分析
数据分析包括数据统计、数据挖掘、机器学习、深度学习等几个细分领域。
数据统计根据原始数据统计出相关的总览数据。例如PV、UV、交易额等。
数据挖掘:数据挖掘这个概念本身含义可以很广,为了与机器学习和深度学习区分开,这里的数据挖掘主要是指传统的数据挖掘方式。例如,有经验的数据分析人员基于数据仓库构建一系列规则来对数据进行分析从而发现一些隐含的规律、现象、问题等,经典的数据挖掘案例就是沃尔玛的啤酒与尿布的关联关系的发现。
机器学习、深度学习:机器学习和深度学习属于数据挖掘的一种具体实现方式,由于其实现方式与传统的数据挖掘方式差异较大,因此数据平台在实现机器学习和深度学习时,需要针对机器学习和深度学习独立进行设计。
3.数据应用
数据应用很广泛,既包括在线业务,也包括离线业务。例如,推荐、广告等属于在线应用,报表、欺诈检测、异常检测等属于离线应用。
数据应用能够发挥价值的前提是需要有“大数据”只有当数据的规模达到一定程度基于数据的分析、挖掘才能发现有价值的规律、现象、问题等。如果数据没有达到一定规模通常情况下做好数据统计就足够了尤其是很多初创企业无须一开始就参考BAT来构建自己的数据平台。
管理平台
管理平台的核心职责就是权限管理无论是业务系统例如淘宝网、中间件系统例如消息队列Kafka还是平台系统例如运维平台都需要进行管理。如果每个系统都自己来实现权限管理效率太低重复工作很多因此需要统一的管理平台来管理所有的系统的权限。
权限管理主要分为两部分:身份认证、权限控制,其基本架构如下图所示。
1.身份认证
确定当前的操作人员身份,防止非法人员进入系统。例如,不允许匿名用户进入系统。为了避免每个系统都自己来管理用户,通常情况下都会使用企业账号来做统一认证和登录。
2.权限控制
根据操作人员的身份确定操作权限,防止未经授权的人员进行操作。例如,不允许研发人员进入财务系统查看别人的工资。
小结
今天我为你讲了互联网企业常见的平台以及基本的平台功能,希望对你有所帮助。
这就是今天的全部内容,留一道思考题给你吧,运维平台或者测试平台,有的公司是由中间件团队负责开发,有的是运维和测试团队自己开发,你觉得两种方式各有什么优缺点,分别适用什么场景呢?

View File

@@ -0,0 +1,106 @@
因收到Google相关通知网站将会择期关闭。相关通知内容
45 架构重构内功心法第一式:有的放矢
在[专栏第8期]“架构设计三原则”中的演化原则部分,我提到了系统的架构是不断演化的,少部分架构演化可能需要推倒重来进行重写,但绝大部分的架构演化都是通过架构重构来实现的。相比全新的架构设计来说,架构重构对架构师的要求更高,主要体现在:
业务已经上线,不能停下来
架构重构时业务已经上线运行了重构既需要尽量保证业务继续往前发展又要完成架构调整这就好比“给飞行中的波音747换引擎”而如果是新设计架构业务还没有上线则即使做砸了对业务也不会有太大影响。
关联方众多,牵一发动全身
架构重构涉及的业务关联方很多,不同关联方的资源投入程度、业务发展速度、对架构痛点的敏感度等有很大差异,如何尽量减少对关联方的影响,或者协调关联方统一行动,是一项很大的挑战;而如果是新设计架构,则在新架构上线前,对关联方没有影响。
旧架构的约束
架构重构需要在旧的架构基础上进行,这是一个很强的约束,会限制架构师的技术选择范围;而如果是新设计架构,则架构师的技术选择余地大得多。
即使是我们决定推倒到重来,完全抛弃旧的架构而去设计新的架构,新架构也会受到旧架构的约束和影响,因为业务在旧架构上产生的数据是不能推倒重来的,新架构必须考虑如何将旧架构产生的数据转换过来。
因此,架构重构对架构师的综合能力要求非常高,业务上要求架构师能够说服产品经理暂缓甚至暂停业务来进行架构重构;团队上需要架构师能够与其他团队达成一致的架构重构计划和步骤;技术上需要架构师给出让技术团队认可的架构重构方案。
总之,架构重构需要架构师既要说得动老板,也要镇得住同事;既要技术攻关,又要协调资源;既要保证业务正常发展,又要在指定时间内完成目标……总之就是十八般武艺要样样精通。
说了那么多架构重构的难度千万不要被困难所吓倒架构师正是需要在原来一团乱麻中找到线索然后重新穿针引线帮助业务进一步腾飞发展。接下来我将分3期传授我的架构重构内功心法今天先来看第一式有的放矢。
通常情况下,当系统架构不满足业务的发展时,其表现形式是系统不断出现各种问题,轻微一点的如系统响应慢、数据错误、某些用户访问失败等,严重的可能是宕机、数据库瘫痪、数据丢失等,或者系统的开发效率很低。开始的时候,技术团队可能只针对具体的问题去解决,解决一个算一个,但如果持续时间较长,例如持续了半年甚至一年情况都不见好转,此时可能有人想到了系统的架构是否存在问题,讨论是否是因为架构原因导致了各种问题。一旦确定需要进行架构重构,就会由架构师牵头来进行架构重构的分析。
当架构师真正开始进行架构重构分析时就会发现自己好像进了一个迷雾森林到处都是问题每个问题都需要解决不知道出路在哪里感觉如果要解决所有这些问题架构重构其实也无能为力。有的架构师一上来搜集了系统当前存在的问题然后汇总成一个100行的Excel表格看到这样一个表格就懵了这么多问题要到猴年马月才能全部解决完啊
期望通过架构重构来解决所有问题当然是不现实的,所以架构师的首要任务是从一大堆纷繁复杂的问题中识别出真正要通过架构重构来解决的问题,集中力量快速解决,而不是想着通过架构重构来解决所有的问题。否则就会陷入人少事多头绪乱的处境,团队累死累活弄个大半年,最后发现好像什么都做了,但每个问题都依然存在。尤其是对于刚接手一个新系统的架构师或者技术主管来说,一定要控制住“新官上任三把火”的冲动,避免摊大饼式或者运动式的重构和优化。
我们来看几个具体的重构案例。
1.后台系统重构:解决不合理的耦合
M系统是一个后台管理系统负责管理所有游戏相关的数据重构的主要原因是因为系统耦合了P业务独有的数据和所有业务公用的数据导致可扩展性比较差。其大概架构如下图所示
举一个简单的例子数据库中的某张表一部分字段是所有业务公用的“游戏数据”一部分字段是P业务系统“独有的数据”开发时如果要改这张表代码和逻辑都很复杂改起来效率很低。
针对M系统存在的问题重构目标就是将游戏数据和业务数据拆分解开两者的耦合使得两个系统都能够独立快速发展。重构的方案如下图所示
重构后的效果非常明显重构后的M系统和P业务后台系统每月上线版本数是重构前的4倍
2.游戏接入系统重构:解决全局单点的可用性问题
S系统是游戏接入的核心系统一旦S系统故障大量游戏玩家就不能登录游戏。而S系统并不具备多中心的能力一旦主机房宕机整个S系统业务就不可用了。其大概架构如下图所示可以看出数据库主库是全局单点一旦数据库主库不可用两个集群的写业务都不可用了。
针对S系统存在的问题重构目标就是实现双中心使得任意一个机房都能够提供完整的服务在某个机房故障时另外一个机房能够全部接管所有业务。重构方案如下图所示
重构后系统的可用性从3个9提升到4个9重构前最夸张的一个月有4次较大的线上故障重构后虽然也经历了机房交换机宕机、运营商线路故障、机柜断电等问题但对业务都没有什么大的影响。
3.X系统解决大系统带来的开发效率问题
X系统是创新业务的主系统之前在业务快速尝试和快速发展期间怎么方便怎么操作怎么快速怎么做系统设计并未投入太多精力和时间很多东西都“塞”到同一个系统中导致到了现在已经改不动了。做一个新功能或者新业务需要花费大量的时间来讨论和梳理各种业务逻辑一不小心就踩个大坑。X系统的架构如下图所示
X系统的问题看起来和M系统比较类似都是可扩展性存在问题但其实根本原因不一样M系统是因为耦合了不同业务的数据导致系统可扩展性不足而X系统是因为将业务相关的所有功能都放在同一个系统中导致系统可扩展性不足同时所有功能都在一个系统中也可能导致一个功能出问题整站不可用。比如说某个功能把数据库拖慢了整站所有业务跟着都慢了。
针对X系统存在的问题重构目标是将各个功能拆分到不同的子系统中降低单个系统的复杂度。重构后的架构如下图所示仅仅是示例实际架构远比下图复杂
重构后各个系统之间通过接口交互,虽然看似增加了接口的工作量,但整体来说,各系统的发展和开发速度比原来快了很多,系统也相对更加简单,也不会出现某个子系统有问题,所有业务都有问题。
这三个系统重构的方案现在回过头来看感觉是理所当然的但实际上当时做分析和决策时远远没有这么简单。以M系统为例当时我们接手后遇到的问题有很多例如
数据经常出错。
M系统是单机单机宕机后所有后台操作就不能进行了。
性能比较差,有的操作耗时好久。
界面比较丑,操作不人性化。
历史上经过几手转接,代码比较混乱。
业务数据和游戏数据耦合,开发效率很低。
从这么多问题中识别出重构的目标并不是一目了然的而如果想一下全部解决所有这些问题人力和时间又不够所以架构师需要透过问题表象看到问题本质找出真正需要通过架构重构解决的核心问题从而做到有的放矢既不会耗费大量的人力和时间投入又能够解决核心问题。这对架构师的分析和判断能力要求非常高既不能看到问题就想到要架构重构也不能只是针对问题进行系统优化判断到底是采取架构重构还是采取系统优化可能不同的架构师和团队都有不同的看法。这里分享一个简单的做法假设我们现在需要从0开始设计当前系统新架构和老架构是否类似如果差异不大说明采取系统优化即可如果差异很大那可能就要进行系统重构了。
那原来发现的那些非架构重构问题怎么办呢当然不能放任不管。以M系统为例我们在重构完成后又启动了多个优化的项目去优化这些问题但此时的优化主要由团队内部完成即可和其他团队没有太多关联优化的速度是很快的。如果没有重构就进行优化则每次优化都要拉一大堆关联业务的团队来讨论方案效率非常低下
小结
今天我为你讲了架构重构的时候需要做到有的放矢,避免像通过架构重构来解决所有问题,希望对你有所帮助。
这就是今天的全部内容,留一道思考题给你吧,分析一下你目前开发的系统,你觉得需要架构重构吗?原因和理由是什么?

View File

@@ -0,0 +1,67 @@
因收到Google相关通知网站将会择期关闭。相关通知内容
46 架构重构内功心法第二式:合纵连横
上一期我给你讲了我的架构重构内功心法的第一式:有的放矢,需要架构师透过问题表象看到问题本质,找出真正需要通过架构重构解决的核心问题,而不是想着通过一次重构解决所有问题。
今天我来传授架构重构内功心法的第二式:合纵连横。
合纵
架构重构是大动作,持续时间比较长,而且会占用一定的研发资源,包括开发和测试,因此不可避免地会影响业务功能的开发。因此,要想真正推动一个架构重构项目启动,需要花费大量的精力进行游说和沟通。注意这里不是指办公室政治,而是指要和利益相关方沟通好,让大家对于重构能够达成一致共识,避免重构过程中不必要的反复和争执。
一般的技术人员谈到架构重构时,就会搬出一大堆技术术语:可扩展性、可用性、性能、耦合、代码很乱……但从过往的实际经验来看,如果和非技术人员这样沟通,效果如同鸡同鸭讲,没有技术背景的人员很难理解,甚至有可能担心我们是在忽悠人。
例如:
技术人员说:我们系统现在的可扩展性太差了,改都改不动!
产品人员想:咦,可扩展性,和扩胸运动有关吗?扩展什么呢?怎么会改不动呢?不就是找个地方写代码嘛……
技术人员说我们的可用性太差现在才3个9业界都是4个9
项目经理想什么是3个9三九感冒灵4个9和3个9不就是差个9嘛和可用有什么关系……
技术人员说我们系统设计不合理A业务和B业务耦合
运营人员想耦合莲藕还是藕断丝连A业务和B业务本来就是互相依赖的呀耦合为什么不合理呢
上面的场景仅仅是个示例,并无嘲笑产品、运营和项目人员不懂技术的意思,而是说明有的技术术语并不是很好理解,在跨领域沟通时,很难达成一致共识。
除此以外,在沟通时还经常遇到的一个问题是凭感觉而不是凭数据说话。比如技术人员说“系统耦合导致我们的开发效率很低”,但是没有数据,也没有样例,单纯这样说,其他人员很难有直观的印象。
所以在沟通协调时,将技术语言转换为通俗语言,以事实说话,以数据说话,是沟通的关键!
以[专栏上一期]的M系统为例我们把“可扩展性”转换为“版本开发速度很慢每次设计都要考虑是否对门户有影响是否要考虑对其他业务有影响”然后我们还收集了1个月里的版本情况发现有几个版本设计阶段讨论1周甚至2周时间但开发只有2天时间而且一个月才做了4个版本最极端的一个版本讨论2周开发2天然后等了1个月才和门户系统一起上线项目经理和产品经理一听都被吓到了。
以[上一期]的S系统为例我们并没有直接说可用性是几个9而是整理线上故障的次数、每次影响的时长影响的用户客服的反馈意见等然后再拿其他系统的数据进行对比无论是产品人员、项目人员还是运营人员明显就看出系统的可用性有问题了。
连横
除了上面讨论的和上下游沟通协调,有的重构还需要和其他相关或者配合的系统的沟通协调。由于大家都是做技术的,有比较多的共同语言,所以这部分的沟通协调其实相对来说要容易一些,但也不是说想推动就能推动的,主要的阻力来自“这对我有什么好处”和“这部分我这边现在不急”。
对于“这对我有什么好处”问题,有的人会简单理解为这是自私的表现,认为对方不顾大局,于是沟通的时候将问题人为拔高。例如“你应该站在部门的角度来考虑这个问题”“这对公司整体利益有帮助”等。这种沟通效果其实很差,首先是这种拔高一般都比较虚,无法明确,不同的人理解也不一样,无法达成共识;其次是如果对公司和部门有利,但对某个小组没用甚至不利,那么可能是因为目前的方案不够好,还可以考虑另外的方案。
那如何才能有效地推动呢?有效的策略是“换位思考、合作双赢、关注长期”。简单来说就是站在对方的角度思考,重构对他有什么好处,能够帮他解决什么问题,带来什么收益。
以[上一期]的M系统为例当时有另外一个C系统和M系统通过数据库直连共用数据库我们的重构方案是要去掉两个系统同时在底层操作数据库改为C系统通过调用M系统接口来写入数据库。这个方案对C系统来说很明显的一点就是C系统短期的改动比较大要将十几个功能都从直接读写数据库改为跨系统接口调用。刚开始C系统也是觉得重构对他们没有什么作用后来我们经过分析和沟通了解到C系统其实也深受目前这种架构之苦主要体现在“数据经常出错要排查”因为C系统和M系统都在写同一个数据库逻辑很难保证完全一致、“要跟着M系统同步开发”因为M系统增加表或者字段C系统要从数据库自己读取出来还要理解逻辑、“C系统要连两个数据库出问题不好查”因为C系统自己还有数据库……这些问题其实在M系统重构后都可以解决虽然短期内C系统有一定的开发工作量但从中长期来看C系统肯定可以省很多事情。例如数据问题排查主要是M系统的事情了通过M系统的接口获取数据无须关注数据相关的业务逻辑等。通过这种方式沟通协调C系统很乐意跟我们一起做重构而且事实也证明重构后对C系统和M系统都有很大好处。
当然如果真的出现了对公司或者部门有利,对某个小组不利的情况,那可能需要协调更高层级的管理者才能够推动,平级推动是比较难的。
对于“这部分我们现在不急”问题,有的人可能会认为这是在找借口,我也不排除这种可能性。但就算真的是找借口,那也是因为大家没有达成一致意见,可能对方不好意思直接拒绝。所以这种情况就可以参考上面“这对我有什么好处”问题的处理方法来处理。
如果对方真的是因为有其他更重要的业务此时勉为其难也不好还是那句话换位思考因为大部分重构的系统并不是到了火烧眉毛非常紧急的时候才开始启动的而是有一定前瞻性的规划如果对方真的有其他更加重要的事情采取等待的策略也未尝不可但要明确正式启动的时间。例如3个月后开始、6月份开始千万不能说“以后”“等不忙的时候”这种无法明确的时间点。
除了计划上灵活一点,方案上也可以灵活一点:我们可以先不做这个系统相关的重构,先把其他需要重构的做完。因为大部分需要重构的系统,需要做的事情很多,分阶段处理,在风险规避、计划安排等方面更加灵活可控。
小结
今天我为你讲了架构重构中的沟通和推动方法,希望对你有所帮助。
这就是今天的全部内容,留一道思考题给你吧,有的人认为:架构师不是技术岗位吗,为何还要做这些事情,沟通和推动的事情让项目经理做就可以了!你怎么看这个观点?

View File

@@ -0,0 +1,81 @@
因收到Google相关通知网站将会择期关闭。相关通知内容
47 架构重构内功心法第三式:运筹帷幄
在前面的架构重构内功心法“[有的放矢]”和“[合纵连横]”中,我提到架构师需要从一大堆问题中识别关键的复杂度问题,然后有的放矢地通过架构重构来解决。但是通常情况下,需要架构重构的系统,基本上都是因为各种历史原因和历史问题没有及时处理,遗留下来逐渐积累,然后到了一个临界点,各种问题开始互相作用,集中爆发!到了真正要开始重构的时候,架构师识别出系统关键的复杂度问题后,如果只针对这个复杂度问题进行架构重构,可能会发现还是无法落地,因为很多条件不具备或者有的问题没解决的情况下就是不能做架构重构。因此,架构师在识别系统关键的复杂度问题后,还需要识别为了解决这个问题,需要做哪些准备事项,或者还要先解决哪些问题。这就需要我今天要和你分享的架构重构内功心法第三式:运筹帷幄。
经过分析和思考我们可能从最初的100个问题列表挑选出其中50个是需要在架构重构中解决的其中一些是基础能力建设或者准备工作而另外一些就是架构重构的核心工作。有了这样一个表格后那我们应该怎么去把这50个问题最终解决呢
最简单的做法是每次从中挑一个解决,最终总会把所有的问题都解决。这种做法操作起来比较简单,但效果会很差,为什么呢?
第一个原因是没有区分问题的优先级,所有问题都一视同仁,没有集中有限资源去解决最重要或者最关键的问题,导致最后做了大半年,回头一看好像做了很多事情,但没取得什么阶段性的成果。
第二个原因是没有将问题分类,导致相似问题没有统筹考虑,方案可能出现反复,效率不高。
第三个原因是会迫于业务版本的压力,专门挑容易做的实施,到了稍微难一点的问题的时候,就因为复杂度和投入等原因被搁置,达不到重构的真正目的。
以X系统为例在我加入前其实也整理了系统目前存在的问题大的项包括可用性、性能、安全、用户体验等每个大项又包括十几二十个子项。但是实施时基本上就是挑软柿子捏觉得哪个好落地、占用资源不太多就挑来做结果做了半年好像做了很多功能但整体却没什么进展。
后来我们成立了一个“X项目”在原来整理的问题基础上识别出架构的核心复杂度体现在庞大的系统集成了太多功能可扩展性不足但目前系统的可用性也不高经常出线上问题耗费大量的人力去处理。因此我们又识别出如果要做架构重构就需要系统处于一个比较稳定的状态不要经常出线上问题。而目前系统的可用性性不高有的是因为硬件资源不够用了或者某些系统组件使用不合理有的是因为架构上存在问题。
基于这些分析,我们制定了总体的策略,如下图所示:
可以看到,真正的架构重构在第三阶段,第一阶段和第二阶段都是为了第三阶段做准备而已,但如果没有第一阶段和第二阶段的铺垫,直接开始第三阶段的架构重构工作,架构重构方案需要糅合第一阶段和第二阶段的一些事项(例如,业务降级、接入服务中心等),会导致架构重构方案不聚焦,而且异常复杂。
为什么最终采用这样一个策略呢?主要还是为了集中有限的资源,某个阶段集中解决某一类问题。这样做首先是效率高,因为阶段目标比较明确,做决策和方案的时候无须进行太多选择;其次是每个阶段都能看到明显的成果,给团队很大的信心。比如说第一阶段的“救火”,做完之后,系统很少有因为机器过载、缓存响应慢、虚拟机挂死等问题导致的故障了;完成第二阶段的事项后,因为组件、外部系统故障导致系统故障的问题也很少了。完成前两个阶段后,我们就可以安心地做第三阶段的“服务化”工作了。
S系统的重构做法也是类似但S系统当时面临的主要问题就是可用性不高并没有系统耦合的问题所以我们当时的策略是“先救火、后优化、再重构”。“救火”阶段做了扩容防止资源不足导致系统被压死和Nginx一键切换功能故障时快速切换优化阶段将一些明显的可用性问题解决包括性能问题等重构阶段将原来的单点数据库改为多中心。
总结一下重构的做法,其实就是“分段实施”,将要解决的问题根据优先级、重要性、实施难度等划分为不同的阶段,每个阶段聚焦于一个整体的目标,集中精力和资源解决一类问题。这样做有几个好处:
每个阶段都有明确目标,做完之后效果明显,团队信心足,后续推进更加容易。
每个阶段的工作量不会太大,可以和业务并行。
每个阶段的改动不会太大,降低了总体风险。
具体如何制定“分段实施”的策略呢?分享一下我的经验。
1.优先级排序
将明显且又比较紧急的事项优先落地解决目前遇到的主要问题。例如扩容在S系统和X系统中都是最优先实施的因为如果不扩容系统隔三差五一会出现响应超时报警一会来个过载报警一会来个大面积不可用……这些问题耗费大量的人力和精力也就没法做其他事情了。
2.问题分类
将问题按照性质分类每个阶段集中解决一类问题。例如X系统的第二阶段我们将多个底层系统切换到公司统一的公共组件提升整体可用性。
3.先易后难
这点与很多人的直觉不太一样,有的人认为应该先攻克最难的问题,所谓“擒贼先擒王”,解决最难的问题后其他问题就不在话下。这样看起来很美好,但实际上不可行。
首先,一开始就做最难的部分,会发现想要解决这个最难的问题,要先解决其他容易的问题。
其次,最难的问题解决起来耗时都比较长,占用资源比较多,如果一开始做最难的,可能做了一两个月还没有什么进展和成果,会影响相关人员对项目的评价和看法,也可能影响团队士气。
第三,刚开始的分析并不一定全面,所以一开始对最难的或者最关键的事项的判断可能会出错。
采取“先易后难”的策略,能够很大程度上避免“先难后易”策略的问题。
首先,随着项目的推进,一些相对简单的问题逐渐解决,会发现原来看起来很难的问题已经不那么难了,甚至有的问题可能都消失了。
其次,先易后难能够比较快地看到成果,虽然成果可能不大,但至少能看到一些成效了,对后续的项目推进和提升团队士气有很大好处。
第三,随着项目的进行,原来遗漏的一些点,或者分析和判断错误的点,会逐渐显示出来,及时根据实际情况进行调整,能够有效地保证整个重构的效果。
4.循序渐进
按照前3个步骤划分了架构重构的实施阶段后就需要评估每个阶段所需要耗费的时间很可能会出现有的阶段耗时可能只要1个月而有的却需要6个月虽然这可能确实是客观事实但通常情况下按照固定的步骤和节奏更有利于项目推进。我的经验是每个阶段最少1个月最长不要超过3个月如果评估超过3个月的那就再拆分为更多阶段。就像X项目我们先划分了阶段每个阶段又分了任务子集当任务子集比较小的时候多个任务子集可以并行当任务子集比较大的时候就当成一个独立的里程碑推进。
小结
今天我为你讲了架构重构中分阶段有序推进的技巧,希望对你有所帮助。
这就是今天的全部内容留一道思考题给你吧如果一个架构重构项目最后规划要2年才完成你会怎么处理

View File

@@ -0,0 +1,141 @@
因收到Google相关通知网站将会择期关闭。相关通知内容
48 再谈开源项目:如何选择、使用以及二次开发?
我在专栏特别放送第3期谈了如何高效地学习开源项目主要聊了我在学习开源项目的一些看法和步骤。今天我们再聊开源项目谈谈如何选择、使用以及二次开发。
软件开发领域有一个流行的原则DRYDont repeat yourself。翻译过来更通俗易懂不要重复造轮子。开源项目的主要目的是共享其实就是为了让大家不要重复造轮子尤其是在互联网这样一个快速发展的领域速度就是生命引入开源项目可以节省大量的人力和时间大大加快业务的发展速度何乐而不为呢
然而现实往往没有那么美好,开源项目虽然节省了大量的人力和时间,但带来的问题也不少,相信绝大部分技术人员都踩过开源软件的坑,小的影响可能是宕机半小时,大的问题可能是丢失几十万条数据,甚至灾难性的事故是全部数据都丢失。
除此以外虽然DRY原则摆在那里但实际上开源项目反而是最不遵守DRY原则的重复的轮子好多你有MySQL我有PostgreSQL你有MongoDB我有Cassandra你有Memcached我有Redis你有Gson我有Jackson你有Angular我有React……总之放眼望去其实相似的轮子很多相似轮子太多如何选择就成了让人头疼的问题了。
怎么办?完全不用开源项目几乎是不可能的,架构师需要更加聪明地选择和使用开源项目。形象点说:不要重复发明轮子,但要找到合适的轮子!但别忘了,如果你开的是保时捷,可别找个拖拉机的轮子。
选:如何选择一个开源项目
1.聚焦是否满足业务
架构师在选择开源项目时一个头疼的问题就是相似的开源项目较多而且后面的总是要宣称比前面的更加优秀。有的架构师在选择时有点无所适从总是会担心选择了A项目而错过了B项目。这个问题的解决方式是聚焦于是否满足业务而不需要过于关注开源项目是否优秀。
Tokyo Tyrant的教训
在开发一个社交类业务时我们使用了TTTokyo Tyrant开源项目觉得既能够做缓存取代Memcached又有持久化存储功能还可以取代MySQL觉得很强大于是就在业务里面大量使用了。但后来的使用过程让人很郁闷主要表现为
不能完全取代MySQL因此有两份存储设计时每次都要讨论和决策究竟什么数据放MySQL什么数据放TT。
功能上看起来很高大上但相应的bug也不少而且有的bug是致命的。例如所有数据不可读后来是自己研究源码写了一个工具才恢复了部分数据。
功能确实强大,但需要花费较长时间熟悉各种细节,不熟悉随便用很容易踩坑。
后来我们反思和总结其实当时的业务Memcached + MySQL完全能够满足而且大家都熟悉其实完全不需要引入TT。
简单来说如果你的业务要求1000 TPS那么一个20000 TPS 和50000 TPS的项目是没有区别的。有的架构师可能会担心TPS不断上涨怎么办其实不用过于担心架构是可以不断演进的等到真的需要这么高的时候再来架构重构这里的设计决策遵循架构设计原则中的“合适原则”和”演化原则”。
2.聚焦是否成熟
很多新的开源项目往往都会声称自己比以前的项目更加优秀性能更高、功能更强、引入更多新概念……看起来都很诱人但实际上都有意无意地隐藏了一个负面的问题更加不成熟不管多优秀的程序员写出来的项目都会有bug千万不要以为作者历害就没有bugWindows、Linux、MySQL的开发者都是顶级的开发者系统一样有很多bug。
不成熟的开源项目应用到生产环境风险极大轻则宕机重则宕机后重启都恢复不了更严重的是数据丢失都找不回来。还是以我上面提到的TT为例我们真的遇到异常断电后文件被损坏重启也恢复不了的故障。还好当时每天做了备份于是只能用1天前的数据进行恢复但当天的数据全部丢失了。后来我们花费了大量的时间和人力去看源码自己写工具恢复了部分数据还好这些数据不是金融相关的数据丢失一部分问题也不大否则就有大麻烦了。
所以在选择开源项目时,尽量选择成熟的开源项目,降低风险。
你可以从这几个方面考察开源项目是否成熟:
版本号除非特殊情况否则不要选0.X版本的至少选1.X版本的版本号越高越好。
使用的公司数量:一般开源项目都会把采用了自己项目的公司列在主页上,公司越大越好,数量越多越好。
社区活跃度:看看社区是否活跃,发帖数、回复数、问题处理速度等。
3.聚焦运维能力
大部分架构师在选择开源项目时,基本上都是聚焦于技术指标,例如性能、可用性、功能这些评估点,而几乎不会去关注运维方面的能力。但如果要将项目应用到线上生产环境,则运维能力是必不可少的一环,否则一旦出问题,运维、研发、测试都只能干瞪眼,求菩萨保佑了!
你可以从这几个方面去考察运维能力:
开源项目日志是否齐全:有的开源项目日志只有寥寥启动停止几行,出了问题根本无法排查。
开源项目是否有命令行、管理控制台等维护工具,能够看到系统运行时的情况。
开源项目是否有故障检测和恢复的能力,例如告警、切换等。
如果是开源库例如Netty这种网络库本身是不具备运维能力的那么就需要在使用库的时候将一些关键信息通过日志记录下来例如在Netty的Handler里面打印一些关键日志。
用:如何使用开源项目
1.深入研究,仔细测试
很多人用开源项目其实是完完全全的“拿来主义”看了几个Demo把程序跑起来就开始部署到线上应用了。这就好像看了一下开车指南知道了方向盘是转向、油门是加速、刹车是减速然后就开车上路了其实是非常危险的。
Elasticsearch的案例
我们有团队使用了Elasticsearch基本上是拿来就用倒排索引是什么都不太清楚配置都是用默认值跑起来就上线了结果就遇到节点ping时间太长剔除异常节点太慢导致整站访问挂掉。
MySQL的案例
很多团队最初使用MySQL时也没有怎么研究过经常有业务部门抱怨MySQL太慢了。但经过定位发现最关键的几个参数例如innodb_buffer_pool_size、sync_binlog、innodb_log_file_size等都没有配置或者配置错误性能当然会慢。
你可以从这几方面进行研究和测试,更详细的完整方法可以参考专栏特别放送[《如何高效的学习开源项目》]
通读开源项目的设计文档或者白皮书,了解其设计原理。
核对每个配置项的作用和影响,识别出关键配置项。
进行多种场景的性能测试。
进行压力测试连续跑几天观察CPU、内存、磁盘I/O等指标波动。
进行故障测试kill、断电、拔网线、重启100次以上、切换等。
2.小心应用,灰度发布
假如我们做了上面的“深入研究、仔细测试”,发现没什么问题,是否就可以放心大胆地应用到线上了呢?别高兴太早,即使你的研究再深入,测试再仔细,还是要小心为妙,因为再怎么深入地研究,再怎么仔细地测试,都只能降低风险,但不可能完全覆盖所有线上场景。
Tokyo Tyrant的教训
还是以TT为例其实我们在应用之前专门安排一个高手看源码、做测试做了大约1个月但最后上线还是遇到各种问题。线上生产环境的复杂度真的不是测试能够覆盖的必须小心谨慎。
所以,不管研究多深入、测试多仔细、自信心多爆棚,时刻对线上环境和风险要有敬畏之心,小心驶得万年船。我们的经验就是先在非核心的业务上用,然后有经验后慢慢扩展。
3.做好应急,以防万一
即使我们前面的工作做得非常完善和充分也不能认为万事大吉尤其是刚开始使用一个开源项目运气不好可能遇到一个之前全世界的使用者从来没遇到的bug导致业务都无法恢复尤其是存储方面一旦出现问题无法恢复可能就是致命的打击。
MongoDB丢失数据
某个业务使用了MongoDB结果宕机后部分数据丢失无法恢复也没有其他备份人工恢复都没办法只能接一个用户投诉处理一个导致DBA和运维从此以后都反对我们用MongoDB即使是尝试性的。
虽然因为一次故障就完全反对尝试是有点反应过度了但确实故障也给我们提了一个醒对于重要的业务或者数据使用开源项目时最好有另外一个比较成熟的方案做备份尤其是数据存储。例如如果要用MongoDB或者Redis可以用MySQL做备份存储。这样做虽然复杂度和成本高一些但关键时刻能够救命
改:如何基于开源项目做二次开发
1.保持纯洁,加以包装
当我们发现开源项目有的地方不满足我们的需求时,自然会有一种去改改的冲动,但是怎么改是个大学问。一种方式是投入几个人从内到外全部改一遍,将其改造成完全符合我们业务需求。但这样做有几个比较严重的问题:
投入太大一般来说Redis这种级别的开源项目真要自己改至少要投入2个人搞1个月以上。
失去了跟随原项目演进的能力:改的太多,即使原有开源项目继续演进,也无法合并了,因为差异太大。
所以我的建议是不要改动原系统而是要开发辅助系统监控、报警、负载均衡、管理等。以Redis为例如果我们想增加集群功能则不要去改动Redis本身的实现而是增加一个proxy层来实现。Twitter的Twemproxy就是这样做的而Redis到了3.0后本身提供了集群功能原有的方案简单切换到Redis 3.0即可(详细可参考这里)。
如果实在想改到原有系统怎么办呢我们的建议是直接给开源项目提需求或者bug但弊端就是响应比较缓慢这个就要看业务紧急程度了如果实在太急那就只能自己改了如果不是太急建议做好备份或者应急手段即可。
2.发明你要的轮子
这一点估计让你大跌眼镜,怎么讲了半天,最后又回到了“重复发明你要的轮子”呢?
其实选与不选开源项目,核心还是一个成本和收益的问题,并不是说选择开源项目就一定是最优的项目,最主要的问题是:没有完全适合你的轮子!
软件领域和硬件领域最大的不同就是软件领域没有绝对的工业标准大家都很尽兴想怎么玩就怎么玩。不像硬件领域你造一个尺寸与众不同的轮子其他车都用不上你的轮子工艺再高质量再好也是白费软件领域可以造很多相似的轮子基本上能到处用。例如把缓存从Memcached换成Redis不会有太大的问题。
除此以外开源项目为了能够大规模应用考虑的是通用的处理方案而不同的业务其实差异较大通用方案并不一定完美适合具体的某个业务。比如说Memcached通过一致性Hash提供集群功能但是我们的一些业务缓存如果有一台宕机整个业务可能就被拖慢了这就要求我们提供缓存备份的功能。但Memcached又没有而Redis当时又没有集群功能于是我们投入2~4个人花了大约2个月时间基于LevelDB的原理自己做了一套缓存框架支持存储、备份、集群的功能后来又在这个框架的基础上增加了跨机房同步的功能很大程度上提升了业务的可用性水平。如果完全采用开源项目等开源项目来实现是不可能这么快速的甚至开源项目完全就不支持我们的需求。
所以如果你有钱有人有时间投入人力去重复发明完美符合自己业务特点的轮子也是很好的选择毕竟很多财大气粗的公司BAT等都是这样做的否则我们也就没有那么多好用的开源项目了。
小结
今天我从如何选、如何用和如何改三个方面,为你讲了如何才能用好开源项目,希望对你有所帮助。
这就是今天的全部内容留一道思考题给你吧目前的云计算厂商很多都提供了和开源项目类似的系统例如阿里云的云数据库HBase你倾向于购买云厂商提供的系统还是只是将开源系统部署在云服务器上理由是什么

View File

@@ -0,0 +1,82 @@
因收到Google相关通知网站将会择期关闭。相关通知内容
49 谈谈App架构的演进
专栏截止到上一期架构设计相关的理念、技术、实践已经基本讲完相信你一路学习过来会有一种感觉这些内容主要都是讲后端系统的架构设计例如存储高可用、微服务、异地多活等都是后端系统才会涉及。事实上确实也是如此通常情况下我们讲架构设计主要聚焦在后端系统但这并不意味着App、前端就没有架构设计了专栏所讲述的整套架构设计理念虽然是来源于我的后端设计经验但一旦形成完善的技术理论后同样适应于App和前端。
首先,先来复习一下我的专栏所讲述的架构设计理念,可以提炼为下面几个关键点:
架构是系统的顶层结构。
架构设计的主要目的是为了解决软件系统复杂度带来的问题。
架构设计需要遵循三个主要原则:合适原则、简单原则、演化原则。
架构设计首先要掌握业界已经成熟的各种架构模式,然后再进行优化、调整、创新。
复习完我们就可以进入今天的正题我来谈谈App架构的演进以及上面这些架构设计关键点是如何体现的。
Web App
最早的App有很多采用这种架构大多数尝试性的业务一开始也是这样的架构。Web App架构又叫包壳架构简单来说就是在Web的业务上包装一个App的壳业务逻辑完全还是Web实现App壳完成安装的功能让用户看起来像是在使用App实际上和用浏览器访问PC网站没有太大差别。
为何早期的App或者尝试新的业务采用这种架构比较多呢简单来说就是当时业务面临的复杂度决定的。我们以早期的App为例大约在2010年前后移动互联网虽然发展很迅速但受限于用户的设备、移动网络的速度等约束PC互联网还是主流移动互联网还是一个新鲜事物未来的发展前景和发展趋势其实当年大家也不一定能完全看得清楚。例如淘宝也是在2013年才开始决定“All in 无线”的在这样的业务背景下当时的业务重心还是在PC互联网上移动互联网更多是尝试性的。既然是尝试那就要求快速和低成本虽然当时的Android和iOS已经都有了开发App的功能但原生的开发成本太高因此自然而然Web App这种包壳架构就被大家作为首选尝试架构了其主要解决“快速开发”和“低成本”两个复杂度问题架构设计遵循“合适原则”和“简单原则”。
原生App
Web App虽然解决了“快速开发”和“低成本”两个复杂度问题但随着业务的发展Web App的劣势逐渐成为了主要的复杂度问题主要体现在
移动设备的发展速度远远超过Web技术的发展速度因此Web App的体验相比原生App的体验差距越来越明显。
移动互联网飞速发展趋势越来越明显App承载的业务逻辑也越来越复杂进一步加剧了Web App的体验问题。
移动设备在用户体验方面有很多优化和改进而Web App无法利用这些技术优势只有原生App才能够利用这些技术优势。
因此随着业务发展和技术演进移动开发的复杂度从“快速开发”和“低成本”转向了“用户体验”而要保证用户体验采用原生App的架构是最合适的这里的架构设计遵循“演化原则”。
原生App解决了用户体验问题没记错的话大约在2013年前后开始快速发展那个时候的Android工程师和iOS工程师就像现在的人工智能工程师一样非常抢手很多同学也是那时候从后端转行到App开发的。
Hybrid App
原生App很好的解决了用户体验问题但业务和技术也在发展移动互联网此时已经成为明确的大趋势团队需要考虑的不是要不要转移动互联网的问题而是要考虑如何在移动互联网更具竞争力的问题因此各种基于移动互联网特点的功能和体验方式不断被创造出来大家拼的竞争方式就是看谁更快抓住用户需求和痛点。因此移动开发的复杂度又回到了“快速开发”这时就发现了原生App开发的痛点由于Android、iOS、Windows Phone你没看错当年确实是这三个主流平台的原生开发完全不能兼容同样的功能需要三个平台重复开发每个平台还有一些差异因此自然快不起来。
为了解决“快速开发”的复杂度问题大家自然又想到了Web的方式但Web的体验还是远远不如原生怎么解决这个问题呢其实没有办法完美解决但可以根据不同的业务要求选取不同的方案例如对体验要求高的业务采用原生App实现对体验要求不高的可以采用Web的方式实现这就是Hybrid App架构的核心设计思想主要遵循架构设计的“合适原则”。
组件化 & 容器化
Hybrid App能够较好的平衡“用户体验”和“快速开发”两个复杂度问题注意是“平衡”不是“同时解决”但对于一些超级App来说随着业务规模越来越大、业务越来越复杂虽然在用户看来可能是一个App但事实上承载了几十上百个业务。
以手机淘宝为例阿里确认“All in无线”战略后手机淘宝定位为阿里集团移动端的“航空母舰”上面承载了非常多的子业务下图是淘宝的首页第一屏相关的子业务初步估计就有10个以上。
再以微信为例作为腾讯在移动互联网的“航空母舰”其业务也是非常的多如下图“发现”tab页就有7个子业务。
这么多业务集中在一个App上每个业务又在不断地扩展后续又可能会扩展新的业务并且每个业务就是一个独立的团队负责开发因此整个App的可扩展性引入了新的复杂度问题。
我在[专栏第32期]提到可扩展的基本思想就是“拆”但是这个思想应用到App和后端系统时具体的做法就明显不同了。简单来说App和后端系统存在一个本质的区别App是面向用户的后端系统是不面向用户的因此App再怎么拆对用户还是只能呈现同一个App不可能将一个App拆分为几十个独立App而后端系统就不一样了采用微服务架构后后端系统可以拆分为几百上千个子服务都没有问题。同时App的业务再怎么拆分技术栈是一样的不然没法集成在一个App里面而后端就不同了不同的微服务可以用不同的技术栈开发。
在这种业务背景下组件化和容器化架构应运而生其基本思想都是将超级App拆分为众多组件这些组件遵循预先制定好的规范独立开发、独立测试、独立上线。如果某个组件依赖其他组件组件之间通过消息系统进行通信通过这种方式来实现组件隔离从而避免各个团队之间的互相依赖和影响以提升团队开发效率和整个系统的可扩展性。组件化和容器化的架构出现遵循架构设计的“演化原则”只有当业务复杂度发展到一定规模后才适应因此我们会看到大厂应用这个架构的比较多而中小公司的App业务没那么复杂其实并不一定需要采用组件化和容器化架构。
对于组件化和容器化并没有非常严格的定义我理解两者在规范、拆分、团队协作方面都是一样的区别在于发布方式组件化采用的是静态发布即所有的组件各自独自开发测试然后跟随App的某个版本统一上线容器化采用的是动态发布即容器可以动态加载组件组件准备好了直接发布容器会动态更新组件无需等待某个版本才能上线。
关于手机淘宝App更详细的架构演进可以参考《Atlas手淘Native容器化框架和思考》微信App的架构演进可以参考《微信Android客户端架构演进之路》。
跨平台App
前面我介绍的各种App架构除了Web App外其他都面临着同一个问题跨平台需要重复开发。同一个功能和业务Android开发一遍iOS也要开发一遍这里其实存在人力投入的问题违背了架构设计中的“简单原则”。站在企业的角度来讲当然希望能够减少人力投入成本虽然我站在程序员的角度来讲是不希望程序员被减少的因此最近几年各种跨平台方案不断涌现比较知名的有Facebook的React Native、阿里的Weex、Google的Flutter。虽然也有很多公司在尝试使用但目前这几个方案都不算很成熟且在用户体验方面与原生App还是有一定差距例如Airbnb就宣布放弃使用 React Native回归使用原生技术https://www.oschina.net/news/97276/airbnb-sunsetting-react-native
前端的情况也是类似的有兴趣的同学可以看看玉伯的文章《Web研发模式演变》专栏里我就不在赘述了。
小结
今天我为你讲了App架构演进背后的原因和架构分析希望对你有所帮助。
这就是今天的全部内容留一道思考题给你吧你认为App架构接下来会如何演进谈谈你的思考和分析。

View File

@@ -0,0 +1,250 @@
因收到Google相关通知网站将会择期关闭。相关通知内容
50 架构实战:架构设计文档模板
在前面的专栏里,有同学留言说想看看具体的架构设计文档。由于信息安全的原因,再加上稍微复杂的系统,设计文档都是几十页,因此专栏无法直接给出详细的文档案例。但我认为提供一个架构设计文档模板还是很有必要的,可以方便你在实际进行架构设计的时候更好地编写相关文档。我还以前面讲过的“前浪微博”消息队列为例,给出架构设计中最重要的两个文档的模板和关键说明。这个案例文档仅给出一些关键内容供你参考,部分细节无法全面覆盖或者完全保证正确。
备选方案模板
1.需求介绍
[需求介绍主要描述需求的背景、目标、范围等]
随着前浪微博业务的不断发展,业务上拆分的子系统越来越多,目前系统间的调用都是同步调用,由此带来几个明显的系统问题:
性能问题当用户发布了一条微博后微博发布子系统需要同步调用“统计子系统”“审核子系统”“奖励子系统”等共8个子系统性能很低。
耦合问题:当新增一个子系统时,例如如果要增加“广告子系统”,那么广告子系统需要开发新的接口给微博发布子系统调用。
效率问题:每个子系统提供的接口参数和实现都有一些细微的差别,导致每次都需要重新设计接口和联调接口,开发团队和测试团队花费了许多重复工作量。
基于以上背景,我们需要引入消息队列进行系统解耦,将目前的同步调用改为异步通知。
2.需求分析
[需求分析主要全方位地描述需求相关的信息]
5W
[5W指Who、When、What、Why、Where。
Who需求利益干系人包括开发者、使用者、购买者、决策者等。
When需求使用时间包括季节、时间、里程碑等。
What需求的产出是什么包括系统、数据、文件、开发库、平台等。
Where需求的应用场景包括国家、地点、环境等例如测试平台只会在测试环境使用。
Why需求需要解决的问题通常和需求背景相关]
消息队列的5W分析如下
Who消息队列系统主要是业务子系统来使用子系统发送消息或者接收消息。
When当子系统需要发送异步通知的时候需要使用消息队列系统。
What需要开发消息队列系统。
Where开发环境、测试环境、生产环境都需要部署。
Why消息队列系统将子系统解耦将同步调用改为异步通知。
1H
[这里的How不是设计方案也不是架构方案而是关键业务流程。消息队列系统这部分内容很简单但有的业务系统1H就是具体的用例了有兴趣的同学可以尝试写写ATM机取款的业务流程。如果是复杂的业务系统这部分也可以独立成“用例文档”]
消息队列有两大核心功能:
业务子系统发送消息给消息队列。
业务子系统从消息队列获取消息。
8C
[8C指的是8个约束和限制即Constraints包括性能Performance、成本Cost、时间Time、可靠性Reliability、安全性Security、合规性Compliance、技术性Technology、兼容性Compatibility]
注:需求中涉及的性能、成本、可靠性等仅仅是利益关联方提出的诉求,不一定准确;如果经过分析有的约束没有必要,或成本太高、难度太大,这些约束是可以调整的。
性能需要达到Kafka的性能水平。
成本参考XX公司的设计方案不超过10台服务器。
时间期望3个月内上线第一个版本在两个业务尝试使用。
可靠性按照业务的要求消息队列系统的可靠性需要达到99.99%。
安全性:消息队列系统仅在生产环境内网使用,无需考虑网络安全;如消息中有敏感信息,消息发送方需要自行进行加密,消息队列系统本身不考虑通用的加密。
合规性消息队列系统需要按照公司目前的DevOps规范进行开发。
技术性目前团队主要研发人员是Java最好用Java开发。
兼容性:之前没有类似系统,无需考虑兼容性。
3.复杂度分析
[分析需求的复杂度,复杂度常见的有高可用、高性能、可扩展等,具体分析方法请参考专栏前面的内容]
注:文档的内容省略了分析过程,实际操作的时候每个约束和限制都要有详细的逻辑推导,避免完全拍脑袋式决策,具体请参考[专栏第10期]的分析。
高可用
对于微博子系统来说如果消息丢了导致没有审核然后触犯了国家法律法规则是非常严重的事情对于等级子系统来说如果用户达到相应等级后系统没有给他奖品和专属服务则VIP用户会很不满意导致用户流失从而损失收入虽然也比较关键但没有审核子系统丢消息那么严重。
综合来看,消息队列需要高可用性,包括消息写入、消息存储、消息读取都需要保证高可用性。
高性能
前浪微博系统用户每天发送1000万条微博那么微博子系统一天会产生1000万条消息平均一条消息有10个子系统读取那么其他子系统读取的消息大约是1亿次。将数据按照秒来计算一天内平均每秒写入消息数为115条每秒读取的消息数是1150条再考虑系统的读写并不是完全平均的设计的目标应该以峰值来计算。峰值一般取平均值的3倍那么消息队列系统的TPS是345QPS是3450考虑一定的性能余量。由于现在的基数较低为了预留一定的系统容量应对后续业务的发展我们将设计目标设定为峰值的4倍因此最终的性能要求是TPS为1380QPS为13800。TPS为1380并不高但QPS为13800已经比较高了因此高性能读取是复杂度之一。
可扩展
消息队列的功能很明确,基本无须扩展,因此可扩展性不是这个消息队列的关键复杂度。
4.备选方案
[备选方案设计至少3个备选方案每个备选方案需要描述关键的实现无须描述具体的实现细节。此处省略具体方案描述详细请参考[专栏第11期]]
备选方案1直接引入开源Kafka
[此处省略方案描述]
备选方案2集群 + MySQL存储
[此处省略方案描述]
备选方案3集群 + 自研存储
[此处省略方案描述]
5.备选方案评估
[备选方案360度环评详细请参考[专栏第12期]。注意备选方案评估的内容会根据评估会议的结果进行修改,也就是说架构师首先给出自己的备选方案评估,然后举行备选方案评估会议,再根据会议结论修改备选方案文档]
架构设计模板
[备选方案评估后会选择一个方案落地实施,架构设计文档就是用来详细描述细化方案的]
1.总体方案
[总体方案需要从整体上描述方案的结构,其核心内容就是架构图,以及针对架构图的描述,包括模块或者子系统的职责描述、核心流程]
2.架构总览
[架构总览给出架构图以及架构的描述]
架构关键设计点:
采用数据分散集群的架构,集群中的服务器进行分组,每个分组存储一部分消息数据。
每个分组包含一台主MySQL和一台备MySQL分组内主备数据复制分组间数据不同步。
正常情况下,分组内的主服务器对外提供消息写入和消息读取服务,备服务器不对外提供服务;主服务器宕机的情况下,备服务器对外提供消息读取的服务。
客户端采取轮询的策略写入和读取消息。
3.核心流程
消息发送流程
[此处省略流程描述]
消息读取流程
[此处省略流程描述]
4.详细设计
[详细设计需要描述具体的实现细节]
高可用设计
消息发送可靠性
业务服务器中嵌入消息队列系统提供的SDKSDK支持轮询发送消息当某个分组的主服务器无法发送消息时SDK挑选下一个分组主服务器重发消息依次尝试所有主服务器直到发送成功如果全部主服务器都无法发送SDK可以缓存消息也可以直接丢弃消息具体策略可以在启动SDK的时候通过配置指定。
如果SDK缓存了一些消息未发送此时恰好业务服务器又重启则所有缓存的消息将永久丢失这种情况SDK不做处理业务方需要针对某些非常关键的消息自己实现永久存储的功能。
消息存储可靠性
消息存储在MySQL中每个分组有一主一备两台MySQL服务器MySQL服务器之间复制消息以保证消息存储高可用。如果主备间出现复制延迟恰好此时MySQL主服务器宕机导致数据无法恢复则部分消息会永久丢失这种情况不做针对性设计DBA需要对主备间的复制延迟进行监控当复制延迟超过30秒的时候需要及时告警并进行处理。
消息读取可靠性
每个分组有一主一备两台服务器,主服务器支持发送和读取消息,备服务器只支持读取消息,当主服务器正常的时候备服务器不对外提供服务,只有备服务器判断主服务器故障的时候才对外提供消息读取服务。
主备服务器的角色和分组信息通过配置指定通过ZooKeeper进行状态判断和决策。主备服务器启动的时候分别连接到ZooKeeper在/MQ/Server/[group]目录下建立EPHEMERAL节点假设分组名称为group1则主服务器节点为/MQ/Server/group1/master备服务器的节点为/MQ/Server/group1/slave。节点的超时时间可以配置默认为10秒。
高性能设计
[此处省略具体设计]
可扩展设计
[此处省略具体设计。如果方案不涉及,可以简单写上“无”,表示设计者有考虑但不需要设计;否则如果完全不写的话,方案评审的时候可能会被认为是遗漏了设计点]
安全设计
消息队列系统需要提供权限控制功能,权限控制包括两部分:身份识别和队列权限控制。
身份识别
消息队列系统给业务子系统分配身份标识和接入keySDK首先需要建立连接并进行身份校验消息队列服务器会中断校验不通过的连接。因此任何业务子系统如果想接入消息队列系统都必须首先申请身份标识和接入key通过这种方式来防止恶意系统任意接入。
队列权限
某些队列信息可能比较敏感,只允许部分子系统发送或者读取,消息队列系统将队列权限保存在配置文件中,当收到发送或者读取消息的请求时,首先需要根据业务子系统的身份标识以及配置的权限信息来判断业务子系统是否有权限,如果没有权限则拒绝服务。
其他设计
[其他设计包括上述以外的其他设计考虑点,例如指定开发语言、符合公司的某些标准等,如果篇幅较长,也可以独立进行描述]
消息队列系统需要接入公司已有的运维平台,通过运维平台发布和部署。
消息队列系统需要输出日志给公司已有的监控平台,通过监控平台监控消息队列系统的健康状态,包括发送消息的数量、发送消息的大小、积压消息的数量等,详细监控指标在后续设计方案中列出。
部署方案
[部署方案主要包括硬件要求、服务器部署方式、组网方式等]
消息队列系统的服务器和数据库服务器采取混布的方式部署一台服务器上部署同一分组的主服务器和主MySQL或者备服务器和备MySQL。因为消息队列服务器主要是CPU密集型而MySQL是磁盘密集型的所以两者混布互相影响的几率不大。
硬件的基本要求32核48G内存512G SSD硬盘考虑到消息队列系统动态扩容的需求不高且对性能要求较高因此需要使用物理服务器不采用虚拟机。
5.架构演进规划
[通常情况下,规划和设计的需求比较完善,但如果一次性全部做完,项目周期可能会很长,因此可以采取分阶段实施,即:第一期做什么、第二期做什么,以此类推]
整个消息队列系统分三期实现:
第一期实现消息发送、权限控制功能预计时间3个月。
第二期实现消息读取功能预计时间1个月。
第三期实现主备基于ZooKeeper切换的功能预计时间2周。

View File

@@ -0,0 +1,303 @@
因收到Google相关通知网站将会择期关闭。相关通知内容
51 如何画出优秀的软件系统架构图?
你好,我是华仔。
你现在看到的这篇文章是我在2022年新写的。《从0开始学架构》这门课更新结束后我又探索了很多和架构相关的事情。这期间新的经历和尝试也让我有了更多的思考。
所以,有了今天这篇文章,把我在软件系统架构图上的实践分享给你。
很多同学技术能力很强,架构设计也做得很好,但是在给别人讲解的时候,总感觉像是“茶壶里煮饺子,有货倒不出”。
其实,在为新员工培训系统架构、给领导汇报技术规划、上技术大会做演讲或者向晋升评委介绍工作贡献的时候,如果你能画出一张优秀的软件系统架构图,就可以大大提升自己的讲解效果,让对方轻松地理解你想表达的关键点。
这一讲,我就会为你分享软件系统架构图的画图技巧。
4+1视图
说起软件系统架构图你可能会想到4+1视图毕竟很多学习资料上都说它是架构图的标准。那么到底什么是4+1视图呢是不是只要按照4+1视图的标准去画就没有问题呢
我们还是从它的由来说起。1995年Philippe Kruchten在论文中指出了过去用单一视图描述软件系统架构的问题并提出了4+1视图作为解决方案。
有时,软件架构的问题来源于系统设计者过早地划分软件或者过分地强调软件开发的某一个方面,比如数据工程、运行时效率、开发策略或团队组织。此外,软件架构往往不能解决它的所有“用户”的问题。……作为补救措施,我们建议使用几个并发视图来组织对软件架构的描述,其中每个视图分别解决一组特定的问题。
不同视图之间的关系如下图所示:
4+1视图的核心理念是从不同的角度去剖析系统看看系统的结构是什么样的具体每个视图的含义是
逻辑视图:从终端用户角度看系统提供给用户的功能,对应 UML的 class 和 state diagrams。
处理视图:从动态的角度看系统的处理过程,对应 UML 的 sequence 和 activity diagrams。
开发视图:从程序员角度看系统的逻辑组成,对应 UML 的 package diagrams。
物理视图:从系统工程师角度看系统的物理组成,对应 UML 的 deployment diagrams。
场景视图:从用户角度看系统需要实现的需求,对应 UML 的 use case diagrams。
(备注:逻辑视图看到的“功能”和场景视图看到的“需求”是一回事吗?答案是否定的。一个需求可能涉及多个功能,例如“取款”这个场景涉及“插卡”“密码验证”“出钞”等功能;而多个需求可能涉及同一个功能,例如“取款”和“转账”是两个不同的需求,但是都涉及“密码验证”这个功能。)
我们可以看到4+1视图本身很全面也很规范但是为什么在实际工作中真正按照这个标准来画架构图的公司和团队并不多呢
我认为原因主要有三点:
架构复杂度增加1995年的时候系统大部分还是单体系统而现在分布式系统越来越多。如果我们用4+1视图来表示分布式系统的话就会遇到困难比如微服务架构下有那么多的微服务Development view 就不好表示。
绑定 UML 图UML 图画架构图存在问题,主要问题是不美观,表达能力弱。
备注左图是用UML工具画的右图是用Visio画的对比之下UML图的缺点十分明显。
理解困难:逻辑视图、开发视图和处理视图比较容易混淆。比如说,有人把逻辑视图理解为软件开发的类结构图,也有人把处理视图和开发视图等同,还有人认为逻辑视图就是开发视图。
这些原因导致4+1视图在目前的实际工作中并不是很实用。那么我们到底要怎么画软件系统架构图呢
核心指导思想4R架构定义
其实,很多人之所以画不好架构图,最大的痛点就是不好把握到底要画哪些内容,画得太少担心没有展现关键信息,画得太多又觉得把握不住重点。
所以现在的问题变成了:应该按照什么样的标准来明确架构图要展现的内容呢?
答案就是我在[第1讲]中介绍的4R架构定义。
软件架构指软件系统的顶层Rank结构它定义了系统由哪些角色Role组成角色之间的关系Relation和运作规则Rule
4R是指4个关键词RankRoleRelation和Rule。既然可以通过4R来定义软件系统的架构那么按照4R架构定义的思路来画架构图也是很合情合理的具体步骤如下
第一步明确Rank也就是说不要事无巨细地把一个大系统的方方面面都在一张架构图中展现出来而应该明确你要阐述的系统所属的级别L0L4然后只描述这个级别的架构信息。
第二步画出Role从不同的角度来分解系统看看系统包含哪些角色角色对应架构图中的区块、图标和节点等。
第三步画出Relation有了角色后画出角色之间的关系对应架构图中角色之间的连接线不同的连接线可以代表不同的关系。
第四步最后画出Rule挑选核心场景画出系统角色之间如何协作来完成某项具体的业务功能对应系统序列图。
我把描述Role和Relation的架构图称为静态架构图描述Rule的系统序列图称为动态架构图。
从某一个角度去看静态架构图的数量跟系统复杂度有关一般是12张如果比较简单用一张图就够了如果比较复杂就要分别用两张图来展现而动态架构图是一般是多张因为核心场景数量不止一个对应的系统序列图有多张。
常见架构图
刚才介绍4+1视图的时候我提到过从不同的角度去剖析系统就会得到不同的视图。其实按照4R架构定义来画架构图也是这样用不同的方式去划分系统就会得到不同类型的架构分别对应不同类型的架构图。常见的类型整理如下
接下来,我就为你详细地讲解每一类架构图的特点。
1. 业务架构图
【定义】
描述系统对用户提供了什么业务功能,类似于 4+1 视图的场景视图。
【使用场景】
产品人员规划业务:比如说我们经常在产品规划和汇报会议上看到产品人员会用业务架构图来展现业务全局状态。
给高 P 汇报业务对于P7+以上级别的技术人员,在汇报的时候不能光讲技术,也要讲业务的发展情况,用业务架构图就比较容易的展现业务整体情况。
给新员工培训业务。
【画图技巧】
通过不同颜色来标识业务状态:比如说哪些业务发展状态好,哪些问题比较多,哪些比较稳定,哪些竞争比较激烈等。
业务分组管理:将类似的业务放在一个分组里面展现,用虚线框或者相同背景将其标识出来。
区块对齐:为了美观,可以改变不同区块的长短大小进行对齐,让整体看起来更美观。
【参考案例】
AlipayHK的一个业务架构图如下所示
这张业务架构图有三点关键信息:
“MTR”区块是浅红色的“人传人”区块是绿色的浅红色代表正在进行的绿色代表明年规划的。
分了4组钱包业务、第三方业务、商家服务和用户管理。
“转账”和“社交红包”等区块比较长,只是为了对齐后更美观,不代表业务本身的量级或者重要程度,如果要表示这样的信息,那么可以用颜色来表示。
注意千万不要画得五颜六色一般一张图的颜色数量控制在3种以内是比较好的。所以在画图的时候你要想清楚到底哪些信息是要放在业务架构图中重点展示的关键信息哪些信息顺带讲一下就可以了。
2. 客户端和前端架构图
【定义】
描述客户端和前端的领域逻辑架构,关注的是从逻辑的角度如何分解客户端或者前端应用。
【使用场景】
整体架构设计:由客户端或者前端架构师完成本领域的架构设计。
架构培训。
【画图技巧】
通过不同颜色来标识不同角色。
通过连接线来表示关系,如果有多种关系,例如有的是直接调用,有的是事件通知,那么可以用不同形状的线条来表示。
分层或分组:将类似的角色分层或者分组管理。
【参考案例】
微信客户端架构3.x的架构图如下所示
这张客户端架构图有三点关键信息:
图中用了灰色app:UI等、蓝色Net Scene等、深灰色Storage、浅蓝色Network来表示不同类型的模块。
图中有两类连接线双向的WebViewUI和app:UI单向的app:UI和Net Scene等
整体上分为4组对应图中背景色不同的四个大的区块。
3. 系统架构图
【定义】
描述后端的逻辑架构,又叫“后端架构”或“技术架构”,不管是业务系统、中间件系统,还是基础的操作系统、数据库系统等,系统架构都是软件系统架构的核心。
【使用场景】
整体架构设计。
架构培训。
【画图技巧】
通过不同颜色来标识不同角色。
通过连接线来表示关系。
逻辑分组。
【参考案例】
如果系统比较简单可以参考MongoDB Sharding的系统架构图如下所示
如果系统相对复杂建议首先用一张图来展示系统架构里面的角色Role以及每个角色的核心功能然后再用一张图来展示角色之间的关系Relation可以参考一个支付中台的系统架构图如下所示
(备注:完整的支付中台关系图太大了,这张关系图只是摘取其中一部分作为示意图,供你参考。)
4. 应用架构图
【定义】
描述后端系统由哪些应用组成,一个应用就是一个可部署发布运行的程序,它是项目开发过程中,开发测试运维团队协作的基础。
【使用场景】
项目开发、测试。
运维部署发布。
子域架构设计。
【画图技巧】
通过不同颜色来标识不同角色。
通过连接线来表示关系。
复杂系统分域来画。
【参考案例】
如果系统比较简单那么基本上应用架构和系统架构是等价的可以参考MongoDB Sharding的应用架构图如下所示
我们可以看到这张图中的Routermongos、Config Servers 和 Shardreplica set既包含了系统架构的角色信息Router、Config Servers 和 Shard又包含了应用信息mongos、Config Servers 和 Shard
如果系统比较复杂,按照架构分层的角度来看,应用架构已经到了可执行程序这一层,例如支付中台这一类的系统,包含的应用可能有几百上千个,如果把整个支付中台所有的应用都在一张图里面展示出来,信息太多太密,可能会导致架构图都看不清。
这种情况下,应用架构一般都是按照子域来画应用架构图,可以参考支付中台的会员域的应用架构图,如下所示:
5. 部署架构图
【定义】
描述后端系统具体是如何部署的,主要包含机房信息、网络信息和硬件信息等。
【使用场景】
总体架构设计。
运维规划和优化。
【画图技巧】
用图标代替区块,这样看起来更加美观和容易理解。
【参考案例】
一个简单的支付系统的部署架构图如下所示:
6. 系统序列图
【定义】
描述某个业务场景下,系统各个角色如何配合起来完成业务功能。
【使用场景】
结合“系统架构、应用架构和部署架构”来使用。
【画图技巧】
使用UML的序列图来画。
【参考案例】
“扫码支付”这个支付核心场景的系统序列图如下所示:
(备注:这张序列图的角色对应前面“系统架构”这一小节的支付中台系统的关系图。)
补充说明
如果你曾经研究过架构图的标准那么除了4+1视图以外你可能还看到过TOGAF的“业务架构跟这一讲的业务架构名字相同但是意义不同、数据架构不是指大数据平台架构而是指数据资产的架构、应用架构和技术架构”这种说法或者还看到过C4架构模型Context、Container、Component和Code等等。
但其实目前业界并没有就架构图标准达成共识刚才提到的TOGAF是企业级的架构基本上要到CTO这个级别才能接触的而C4模型的表达能力又不够。
所以我并没有直接套用这些内容而是根据个人经验将我认为最有效果的架构图整理出来。这些架构图都是我在不同类型不同规模不同业务的公司华为、UC、阿里和蚂蚁等里面验证过的你可以放心地使用。
小结
今天我为你介绍了画软件系统架构图的总体思路,以及常见架构图的应用场景和画图技巧,希望对你有所帮助。
这就是今天的全部内容,留一道思考题给你吧:为什么后端架构可以直接被称为“系统架构”,通常我们说的系统不是应该包含客户端和前端在内的一个整体吗?

View File

@@ -0,0 +1,71 @@
因收到Google相关通知网站将会择期关闭。相关通知内容
加餐|业务架构实战营开营了
你好,我是华仔!
感谢你订阅我的架构专栏架构专栏从2018年上线至今已经有超过50000的用户订阅相信你也已经从架构专栏中收获良多。
开设架构训练营的初衷是什么?
架构专栏以及对应的书籍《从零开始学架构》上线后,我收到了很多用户的反馈和评价,对于感谢和夸奖的部分我就不多说了,我来谈谈一个比较有意思的现象。
有一部分用户认为专栏内容是系统化的,将架构设计的本质讲清楚了,学习专栏需要一定的基础和经验,例如:
而另外一部分用户的评价截然相反,认为架构专栏和书籍是适合入门科普的,学习专栏主要是知道了一大堆架构相关的概念,例如:
也就是说,同样的内容,一部分用户认为是适合入门和科普,一部分用户却认为需要一定的架构基础才能看懂,这是怎么回事呢?
其实从我个人的写作目的来说,我的本意是将我自己多年架构设计经验和思考提炼成通用的架构方法论,剖析常见的架构模式的本质,希望通过“授人鱼不如授人渔”的方式,让你能够从本质上掌握架构设计的方法,从而能够自如地将“面向复杂度的架构设计方法论”应用到你的实际业务系统中。
那为什么会出现两种截然不同的评价和看法呢?
我认真思考了一下,其实并不是架构专栏的内容本身有问题,而是不同基础的用户对内容的理解差异导致了不同的评价和看法。
其道理和钓鱼是类似的:
如果我直接把鱼钓上来给你,那你肯定没法学会自己钓鱼,我不把鱼给你你就没鱼吃。
如果你已经有了钓鱼的一些经验,此时我把我的更好的钓鱼方法传授给你,能够纠正你的一些错误做法,解答你的一些困惑,教你一些更实用的技巧,你肯定会觉得收获很大。这就是很多用户评价架构专栏的内容很有收获的原因。
但是如果你从来没钓过鱼,此时我把我的钓鱼方法传授给你,你虽然知道了一大堆的钓鱼概念和方法,但是实际自己去钓鱼的时候,肯定会遇到如何将理论落到实践的问题。这就是部分用户认为架构专栏适合入门和科普的原因。
也就是说,专栏的内容是没问题的,用户的感受也是没问题的,但正如有个用户说的,“任何一本书都有其面向人群”,专栏的内容不可能涵盖架构设计的方方面面,做到面面俱到。
因此,为了满足目前还没有多少架构设计基础,但是期望提升自己架构设计能力的这部分用户,我想通过更有针对性的内容来讲解架构设计,这就是接下来我想给你介绍的“业务架构训练营”的设计初衷。
架构训练营的设计思路是什么?
你可能会有疑问:既然有了架构专栏,那么架构训练营与专栏的区别是什么?会不会只是将专栏内容搬过来而已?
关于架构训练营的思路,一开始我们就确定了一个总原则:“架构训练营不是将专栏内容改成视频形式”。我们先后讨论了几个思路,包括通过代码来训练架构能力、通过剖析开源系统来训练架构能力、通过实现单个复杂业务来训练架构能力,最终,我们确定了“面向业务的架构实战”这个思路。
简单来说,架构专栏的思路是从架构设计本身出发,给你讲述完整的架构设计方法论和常见架构模式的理论分析。当你有了一定的架构设计经验后,架构专栏能够帮助你系统地理解架构设计,将你的经验串起来形成方法论。
而架构训练营的思路是从实战出发,给你讲述如何分析业务的架构需求,如何设计合理的架构来满足业务需求,其内容除了涵盖架构专栏的核心内容外,还会增加多种业务案例来讲解具体如何结合业务来做架构。
总体上来看,架构专栏更偏理论一些,架构训练营更强调实战。
业务架构实战营可以学到什么?
训练营的第一部分,将完整介绍“面向复杂度”的架构设计方法论,目的在于帮助你系统地掌握架构领域的基础知识和技能。
具体内容涵盖架构设计相关概念、架构设计的历史和本质、架构设计原则、架构设计流程我们可以和编程领域的知识做个类比“面向复杂度”对应“面向对象”、“架构设计流程”对应“敏捷开发流程”、“架构设计三原则”对应“SOLID、DRY”等原则。
在训练营第二部分,主要是讲解业务架构设计套路,目的在于帮助你快速掌握架构设计技巧,做到即学即用。
具体内容涵盖高性能高可用存储、高性能高可用计算、微服务、异地多活这4种常见的架构类型每种套路的具体内容都包括相关的成熟架构模式讲解、常见开源系统分析、以及如何选择成熟技术搭建适合业务需求的架构。
当然你也可能会遇到没有合适的可选方案需要自己搭建系统的情况针对这个情况我会给出一个实际案例并且手把手带你从0到1搭建一个高性能高可用的复杂系统。
训练营的第三部分内容主要是架构设计案例实战,我会通过一个完整案例来展示如何整合第一部分的基础知识技能和第二部分的架构设计套路,完成实际的业务架构设计。
为了避免你需要花费大量的精力来理解业务我挑选了最容易理解的IM业务同时为了演示不同业务需求导致的不同的架构需求我设计了一个从10万用户到亿级用户的业务发展路径分阶段讲解不同规模的业务对架构的要求具体如何做出合理的架构设计以及如何进行架构演进。
总体来说,架构训练营期望达到的目的是能够让你即学即用,既能够系统地掌握架构设计方法论的核心内容,又能够在实际的工作中很快地基于业务来实现合理的架构。

View File

@@ -0,0 +1,93 @@
因收到Google相关通知网站将会择期关闭。相关通知内容
加餐|单服务器高性能模式性能对比
你好,我是华仔。
我们架构课的[第18讲]和[第19讲]主题是单服务器高性能模式我们讲了PPC与TPC、Reactor与Proactor从理论上跟你详细讲述了不同模式的实现方式和优缺点但是并没有给出详细的测试数据对比原因在于我自己没有整套的测试环境也不能用公司的服务器做压力测试因此留下了一个小小的遗憾。
幸运的是最近我在学习的时候无意中在网络上找到一份非常详尽的关于Linux服务器网络模型的详细系列文章。作者通过连载的方式将iterative、forking对应专栏的PPC模式、preforked对应专栏的prefork模式、threaded对应专栏的TPC模式、prethreaded对应专栏的prethread模式、poll、epoll对应专栏的Reactor模式共7种模式的实现原理、实现代码、性能对比都详尽地进行了阐述完美地弥补了专栏内容没有实际数据对比的遗憾。
因此我把核心的测试数据对比摘录出来,然后基于数据来进一步阐释,也就有了这一讲的加餐。我想第一时间分享给你,相信今天的内容可以帮助我们加深对课程里讲过的理论的理解。
下面是作者对7种模式的性能测试对比结果表格作者在文章中并没有详细地介绍测试环境只是简单提到了测试服务器是租来的云服务器CPU只有1核没有说明具体的CPU型号对于内存、带宽、磁盘等信息并没有介绍我们假设这些硬件相关性能都足够。从理论上来说网络模型的核心性能部件就是CPU因此如下数据是具备参考意义的。
这张图的数据比较多,如何去看懂这样的性能测试数据表格呢?我来分享一个有用的技巧:横向看对比,纵向看转折。
横向看对比
比如当并发连接数是1000的时候可以看出preforking、prethreaded、epoll三种模式性能是相近的也意味着epoll并不是在任何场景都具备相比其它模式的性能优势。
纵向看转折
比如prethreaded模式作者源码中设置了100个线程在11000并发的时候性能有2200但12000并发连接的时候性能急剧下降到只有970这是什么原因呢我推测是12000并发的时候触发了C10K问题线程上下文切换的性能消耗超越了IO处理成为了系统的处理瓶颈。
按照上述“横向看对比,纵向看转折”的方式,我给你分享一下我的一些解读和发现。
创建进程的消耗是创建线程的消耗的4倍左右。
并发2000以内时preforked、prethreaded、epoll的性能相差无几甚至preforked和prethreaded的性能有时候还稍微高一些。
这也是内部系统、中间件等并发数并不高的系统并不一定需要epoll的原因用preforked和prethreaded模式能够达到相同的性能并且实现要简单。
当并发数达到8000以上只有pthreaded和epoll模式能够继续运行但性能也有下降epoll的下降更加平稳一些。
prethreaded模式在12000并发连接的时候性能急剧下降。
推测是触发了C10K问题线程上下文切换的性能消耗超越了IO处理的性能消耗。
poll模式随着并发数增多稳定下降因为需要遍历的描述符越多其性能越低。
类似的还有select模式作者没有单独写select因为两者原理基本类似区别是select的最大支持连接数受限于FD_SETSIZE这个参数。
epoll在并发数超过10000的时候性能开始下降但下降比较平稳。
这个结论看起来比较简单但是却隐含着一个关键的设计点epoll不是万能的连接数太多的时候单进程epoll也是不行的。这也是为什么Redis可以用单进程Reactor模式而Nginx必须用多进程Reactor模式因为Redis的应用场景是内部访问并发数一般不会超过10000而Nginx是互联网访问并发数很容易超过10000。
以上是我从性能对比数据中的一些发现,这些发现能够让我们更进一步理解专栏内容中讲到的理论知识和优缺点对比,这些数据也可以指导我们在实际的架构设计中根据应用场景来选择合适的模式。
最后,我也希望你能掌握“横向看对比,纵向看转折”这个分析技巧。这个技巧在查阅和审核性能测试数据以及各种对比数据的时候,能够帮助你发现很多数据背后隐含的观点和结论。
拓展阅读与学习指南:
原作者的系列文章请参考https://unixism.net/2019/04/linux-applications-performance-introduction/
原作者的测试代码GitHub仓库地址https://github.com/shuveb/zerohttpd
原作者的代码实现了一个完整的基本功能的HTTP服务器采用的是短链接的方式还用到了Redis来保存内容有的代码逻辑是比较复杂的尤其是epoll的实现部分。如果你想自己简单的只是验证网络模型的性能可以去掉其源码中HTTP的实现部分只是简单地返回“hello world”这样的字符串即可。

View File

@@ -0,0 +1,123 @@
因收到Google相关通知网站将会择期关闭。相关通知内容
加餐|扒一扒中台皇帝的外衣
你好,我是华仔。
今天这期加餐,我想和你聊聊中台这个话题。
自从2015阿里巴巴提出中台概念和战略“中台”这个技术术语逐渐火热起来尤其是从2019年开始各类技术大会、各类公众号都在大力宣扬中台出版社也趁着热点赶紧出版各类中台书籍一时间中台有“旧时王谢堂前燕飞入寻常百姓家”的感觉。如果你跟人聊技术的时候不发表一些中台的言论不讨论一些中台的问题那肯定会显得你技术有点落伍了
如果我们仔细阅读这些文章,可能会发现一个有趣的现象,绝大部分谈中台的都是做中台的,很少看到用中台的人出来评价。从人性的角度来讲,做中台的肯定不会说中台不好,毕竟还要靠这个恰饭,王婆卖瓜不自夸的话,买瓜的人自然会少。
偶尔有几篇说中台有问题的文章,例如《中台翻车纪实:一年叫停,员工转岗被裁,资源全浪费》《中台,我信了你的邪 | 深氪》,也很快会有人跳出来说“你们能力不行,所以中台没做好”、“中台是一个业务、组织、技术的协同,你们肯定是组织没保证”……
总而言之,现在到处都能看到做中台的人说中台如何如何好,偶尔有几个跳出来说不好的都会被质疑能力不行!
按照我的技术理念来看,没有完美的技术,没有放之四海皆好的技术,如果你只能看到一项技术的好处,而看不到坑,那实际上很可能就会掉到坑里去。
我虽然没有真正负责做过中台但我做过平台和中间件更为特别的是我参与了两个基于中台的业务项目一个项目是将手游交易系统迁移到电商中台另一个项目是在支付中台上从0到1搭建一个钱包。这两个项目让我亲自实践了一下在阿里和蚂蚁的中台上做项目让我有机会近距离观察中台的运作机制在一次次与中台的讨论、PK的过程中对中台也有了更深的理解和认识。
从我个人的经历和理解来看,目前关于中台的很多说法是言过其实、模棱两可、甚至是错误的,接下来我将给大家谈谈实际上的中台到底是怎么运作的,会有哪些坑。由于我真正就是用的阿里电商中台和蚂蚁的支付中台,因此不用质疑中台能力不行和组织能力不行才会有我说的那些问题。
中台的价值到底是什么?
关于中台的价值,你看到的是这样的(来源《一文读懂「中台」的前世今生》):
可以让各业务部门保持相对的独立和分权,保证对业务的敏感性和创新性;另一方面,用一个强大的平台来对这些部门进行总协调和支持,平衡集权与分权,并为新业务新部门提供生长的空间,从而大幅降低组织变革的成本。中台部门提炼各业务线的共性需求,最大限度地减少“重复造轮子”。
实际上的中台是这样的。
1. 业务部门并不独立。
基于中台的业务会被分为不同优先级大业务对于中台的影响力远远大于小业务核心业务对中台的影响力远远大于新业务。形象点来说中台抱大业务的大腿小业务抱中台的大腿因为中台也是有KPI的中台的KPI怎么来当然是大部分来源于支持的业务了大业务天然会有KPI数据上的优势。
这会导致什么问题呢?大业务的创新不管是不是共性的,中台会鼎力支持,毕竟判断是不是共性需求是中台判断的,而不是每次有个新业务的时候拉上所有业务方来评估和投票;小业务就比较悲催了,中台要拒绝你,只需简单一句话“你这个业务不通用”,“你这个需求太特殊”。
如果小业务不服气能怎么办没什么办法不会存在仲裁委员会之类的机构就算有你去仲裁的时间都够你自己把业务实现5遍了就算中台认为你的需求是共性需求如果你是小业务很可能也会以优先级的原因被排在很后面这里的“很后面”可不是几天几周很可能就是几个月半年了而恰恰很多初创业务一开始规模肯定是比较小的基于中台的开发模式很可能会制约创新业务的快速发展除非这个创新业务一开始就有重量级人物挂帅对中台能够有足够的影响力。
2. 中台并不总是能够提炼共性需求。
注意这里的需求不是指电商中台里面“交易”这个粒度的需求,而是指“交易”系统里面一个个实际的功能点,你要是坚持说“交易”是共性需求,这实际上是一句正确的废话。
事实上提炼共性需求主要是中台从0到1的建设的时候因为这个时候已经有多个业务需求的样本存在哪些是共性哪些是个性是比较容易分析出来的但一旦建成后后续的业务发展和创新中台和业务方天然存在对共性需求的不同诉求。
业务方总是期望将自己的需求划为共性需求,因为这样就能够让中台出人来实现需求;中台总是期望先不要把需求划为共性需求,而是等到多个业务都有类似需求的时候再由中台来抽象提炼,这样才能最大化复用,否则中台每个需求都认为是共性需求的话,中台会累死。
而事实上几乎不太可能出现多个业务同时提出某个需求除了国家颁布的法律法规相关的需求外绝大部分业务需求都是由某个业务方先提出来的这个时候中台是无法判断是否为共性需求的只能又回到前面说的潜规则来操作优先满足大业务拒绝小业务。反正大业务的需求不管是不是共性的做了后数据肯定好看中台KPI有保证小业务就算以后被证明是共性的前期做了也没多少数据中台很可能费力不讨好。
3. 中台的“轮子”会不断变化。
很多朋友看到“避免重复造轮子”就以为中台把轮子造好了,业务方只管用就可以了,而实际情况是中台确实把轮子造好了,但是它每隔一段时间就会把轮子换一遍,例如中台的数据模型、接口、架构等其实都是需要根据业务发展不断变化的。
为了达到中台“复用”的目标通常情况下中台在推出新轮子后就不会再长期维护老轮子否则如果中台同时维护4~5个相似的轮子复用就无从谈起。这就要求基于中台的业务都必须在某个时间段内完成轮子的切换相当于是业务方进行了一次被动架构演进。
当然,如果没有中台,业务方的架构肯定也要随着业务的发展而演进,那这和跟随中台被动演进有什么区别呢?最主要的区别是中台的架构演进频率会比独立的业务架构演进要快,道理很简单:中台融合了多个相似业务的发展!
举个简单例子如果中台支持10个业务其中有1个业务发展很快中台就必须跟着演进剩余的9个业务即使没有任何发展也必须跟着被动演进更何况如果这10个业务本身都在不断发展那中台的演进会更快那是否存在某种牛逼的技术让中台的演进不影响业务呢绝大部分情况下都不可能这是由中台演进的本质决定的中台演进的绝大部分动力来源于业务它的演进不可能做到反过来不影响业务。这点和技术平台存储、消息队列这类不一样技术平台演进的动力来源于技术更新是可以做到不影响业务的。
4. 中台是某类业务的中台,不是所有业务的中台。
中台的本质是提炼共性需求复用,如果业务差异太大的话,复用度不高,提炼和维护中台花费的代价抵不上中台复用带来的价值。所以,实际上应该叫“电商中台”、“支付中台”、“物流中台”、“出行中台”、“视频中台”、“保险中台”,而不应该是“阿里中台”、“腾讯中台”、“百度中台”、“滴滴中台”。
当然因为现在“中台”概念火爆出现了原来很多做中间件和技术平台的团队纷纷将自己负责的“XX平台”改为“技术中台”从广义的角度来说也可以因为这确实是“各业务线共性需求”毕竟存储、缓存、消息队列这些肯定是各业务线的共性需求但通常情况下我们说中台时所指的“需求”还是指“业务需求”即客户可以使用到的功能。
所以即使只是看到“茅台云商”这种中台项目的PPT也能大概率地推断这个项目失败的可能性会非常高图片来源中台我信了你的邪 | 深氪):
中台的效果是怎样的?
关于中台的效果,你看到的是这样的(摘自《中台的问题,是技术的问题,还是人的问题》):
因为阿里巴巴的生态非常复杂,很多业务方本身也很年轻,要怎么去做,下层到底能提供什么样的支撑是不清楚的。当有大中台思路之后,第一,我们这个体系里有什么样的能力,可以让各业务很清楚地知道,也可以让前台业务方更快的理解、选择和使用中台能力。第二、我们提供了基础解决方案,业务方根据需要做定制开发满足自己的业务特性,对前台的业务来说会更快。
实际的中台是这样的。
1. 中台的体系有什么样的功能,业务方根本不是很清楚地知道,而是很清楚地不知道。
事实上几乎没有人能完整地知道中台里面各个域各个子系统的能力更加谈不上业务方更快的理解、选择和使用了。你可以随便打开一张某个技术大会上分享的中台架构满满的一页甚至几页PPT大框小框几十上百个对应到具体的线上运行的系统可能几百上千个这么复杂的一个系统怎么可能有人知道所有的功能和细节更何况是业务方了。
至于说中台提供完整的解决方案,业务方只要定制一下就可以快速上线,说起来容易做起来难,除非业务方是准备复制一个和已有业务基本一样的业务,否则基本上是不可能实现的。原因在于中台提供的解决方案,必然是基于已有的业务来抽象出来的“共性需求”的大集合,如果这个解决方案可定制化度很高,那么就说明可复用度比较低;如果这个解决方案的可定制化度很低,虽然可复用度高但业务可扩展度比较低。
而从中台的本质出发,中台必然会选取“可定制度低可复用度高”的方向,这就约束了只有复制一个非常类似的新业务的时候中台的解决方案才有很大价值,但是对于同一个公司来说,为何要复制两个基本一样的业务呢?如果真的有中台选择了“可定制度高”的解决方案,当新业务和已有业务有较大差异的时候,中台的解决方案并没有什么很大价值,因为大量的工作量会耗费在定制那部分。
实际上我接触的中台是这样运作的:中台会分为很多“域”,例如“交易”、“搜索”、“会员”等,然后业务方将自己的业务需求写出来,然后拉上各个域的产品接口人和技术接口人,一个域一个域的讨论。
以“会员域”为例,讨论的时候,会员域的产品接口人技术接口人肯定很熟悉会员的功能、模型和接口,业务方需要跟中台子域接口人讲解业务需求,然后中台子域接口人来评估会员当前的能力哪些是支持的,哪些是不支持的,不支持的部分是属于共性需求还是属于个性需求,如果是共性需求,需要给出中台的修改的方案,而且修改方案还要会员域的架构师进行评审,因为改中台是影响所有业务的;如果是个性需求,需要设计出中台和业务方交互的方式,例如是提供接口、配置、扩展包等。
所以如果你真正基于中台做过项目你就会发现编码的时间确实少了但是前期的沟通和后期的联调非常耗时间随便做个什么项目拉上20~30人算一般的稍微大点的项目拉上50~100人也是很正常的。
以淘宝的中台为例如下是淘宝电商中台共享事业部里面交易中心的结构图片来源《企业IT 架构转型之道》):
注意交易中心只是共享事业部的一个业务域同级别的业务域从公开资料来看有10来个而交易中心内部就有13个子功能了这些子功能最后都可能对应1个或者多个实际运行的系统。
2. 中台的所谓的“快”,并没有严谨的衡量。
目前各类文章都会说有了中台后业务开发快,但并没有详细的案例和分析到底有多快,仅有的几个案例看起来很夸张,但没有详细背景描述其实并没有说服力。例如说某个业务新上线很快,到底是因为第一版功能很简单,还是第一个版本投入了大量的人力来做,还是真的是因为用了中台?
事实上我经历过的两个接入中台项目并不能看出来快,从实际的开发经验来看,业务方和中台的需求讲解和讨论,业务方和中台方案的设计讨论,后期的业务系统和中台系统联调这些工作量并没有减少,反而还会有一定程度的增加,因为中台在分析需求、设计方案、联调测试的时候需要考虑对其它业务的影响。
中台能够带来的效率提升主要体现在两方面:一是编码的工作量确实会少,毕竟还是有大量的代码可以重用的;二是中台的人员都对已有的类似业务和技术很熟悉,需求的确认和方案的设计会更高效,全流程综合来看,很难判断效率到底高还是低。
事实上我之前在阿里游戏做过几个从0到1的项目因为老板重视都是将原来类似的系统已有的核心开发人员拉过去开发新项目最初的代码也是从原系统拷贝过去改开发效率一样很高核心原因简单来说就是“熟手开发”。
以上是我从基于中台的开发项目中观察到的一些问题,归纳总结出来的一些经验。总体来看好像我在质疑中台,其实不然。关于中台的好处已经有太多的文章了,这一讲试图提供从使用者的视角来看中台所看到的一些信息和问题,目的在于帮助大家更加全面地了解中台。
咱们这门“从零开始学架构”的课程提到了架构设计的三原则,第一条就是“合适原则”,这个原则对中台也是适应的。总结来说就是:中台不是灵丹妙药,不要有问题就想着用中台解决;中台也不是放之四海而皆准,明确中台的适应场景和可能的坑,才能更好地落地中台!
其实提出中台的逍遥子已经早就谆谆告诫了(来源《中台,我信了你的邪 | 深氪》):
中台并不适用于每家公司的每个阶段。在独立业务拓展期、突破期,“一定用独立团、独立师、独立旅建制来做”,否则就会变成瓶颈;但发展到一定阶段,出现太多山头时,就要“关停并转、要合并同类项。问管理要效率,取消重复性建设”。
好了,关于中台的分享就到这里。听了今天的内容,你对中台有更多理解了吗?欢迎留言区分享你的思考,我们一起交流。

View File

@@ -0,0 +1,162 @@
因收到Google相关通知网站将会择期关闭。相关通知内容
如何高效地学习开源项目 华仔,放学别走! 第3期
你好,我是华仔。今天这期“特别放送”,我想和你聊聊如何高效地学习开源项目,一方面澄清开源项目学习过程中的几个误区,另一方面谈谈我自己具体实践时的一套方法论。
得益于开源运动的蓬勃发展,众多技术顶尖的公司、团队或者个人通过开源的方式向技术社区贡献了许多优秀的开源项目,一方面大大促进了整体技术的发展,另一方面大大减轻了中小公司和团队在技术方面的投入压力,让团队能够更加聚焦于业务。
开源项目对团队和业务有很大好处,但对于技术人员来说,如果只是简单的采取“拿来主义”,那就变成一个陷阱:看似很快的用开源项目实现了需求,但自己的技术水平并没有什么提升;甚至可能出现看起来用了很多开源项目,知道很多项目名称,但技术水平止步不前的窘境。
因此,对于开源项目,不能简单的采取“拿来主义”,而要比较深入的去学习开源项目,做到“知其然,知其所以然”,一方面是为了更好地应用这些开源项目,另一方面也是为了通过学习优秀的开源项目来提升自己的能力。
很多技术同学确实也想深入学习一些业界成熟和优秀的开源项目例如Nginx、Redis、Netty等但是在具体实践的时候常常因为一些不正确的观点而误入歧途例如
只有开发这些开源项目的人才能真正理解,我没法参与这个项目开发,因此我很难深入理解。
我的项目没有用Redis不用的话很难深入理解。
数据结构和算法很重要所以我只要研究其数据结构和算法就够了例如Nginx用的红黑树。
“Talk is cheap, show me the code”一头扎进源码逐行阅读。
这些观点要么让自己望而生畏从而轻易放弃,要么让自己浪费大量时间而没有多大收获。那究竟要怎样做才是正确的呢?下面我结合自己的经验谈谈我对如何学习开源项目的看法。
首先,需要树立正确的观念:不管你是什么身份,都可以从开源项目中学到很多东西。
例如要理解Redis的网络模型我们不需要成为Redis的开发者也不需要一定要用到Redis只要具备一定的网络编程基础再通过阅读Redis的源码都可以学习Redis这种单进程的Reactor模型。
其次,不要只盯着数据结构和算法,事实上这两点在学习开源项目的时候并没有那么重要。
例如Nginx使用红黑树来管理定时器对于绝大部分人来说只要知道这点就够了并不需要去研究Nginx实现红黑树的源码是如何写的除非你需要修改这部分但我认为极少人会有这个需求。
第三,采取“自顶向下”的学习方法,源码不是第一步,而是最后一步。
不要一上来就去看源码,而是要基本掌握了功能、原理、关键设计之后再去看源码,看源码的主要目的是为了学习其代码的写作方式,以及关键技术的实现。
例如Redis的RDB持久化模式“会将当前内存中的数据库快照保存到磁盘文件中”那这里所谓的“数据库快照”到底是怎么做的呢在Linux平台上其实就是fork一个子进程来保存就可以了那为何fork子进程就生成了数据库快照了呢这又和Linux的父子进程机制以及copy-on-write技术相关了。
通过这种方式既能够快速掌握系统设计的关键点Redis的RDB模式又能够掌握具体的编程技巧内存快照
接下来我详细谈谈“自顶向下”的学习方法和步骤。
第一步:安装
很多人看到“安装”这个步骤都可能会觉得有点不以为然:“不就是对照手册执行一下命令么,没什么技术含量,用的时候装一下就可以了”。事实上,安装步骤远远不止这么简单,通过具体的安装过程,你可以获取到如下一些关键信息:
这个系统的依赖组件,而依赖的组件是系统设计和实现的基础
以Nginx为例源码安装Nginx依赖的库有pcre、pcre-devel、openssl、openssl-devel、zlib光从名字上看都能够了解一些信息例如openssl可能和https有关zlib可能和压缩有关。
再以Memcache为例最大的依赖就是libevent而根据libevent是一个高性能的网络库我们就能大概推测Memcache的网络实现应该是Reactor模型的。
安装目录也能够提供一些使用和运行的基本信息
例如Nginx安装完成后目录如下
这个目录提供的信息有conf是存放配置文件的logs是存放日志的sbin是运行程序但是html是什么呢这个疑问会促使你继续去研究和学习。
再来看看Redis安装完成后目录下只有一个bin目录具体如下
我相信大部分人看到这目录都会感到有点惊讶这也太简单了吧尤其是与Nginx相比因此也会自然而然的有一些疑问例如Redis如何配置Redis日志保存在哪里这些疑问同样会促使你继续去研究和学习带着问题去学习效率是最高的。
系统提供了哪些工具方便我们使用
同样以Redis为例你可以看到redis-benchmark、redis-check-aof等程序从名字能够大概猜出这些工具的基本使用场景而这些工具在后面故障定位和处理、性能测试等场景可能非常方便。
第二步:运行
安装完成后,我们需要真正将系统运行起来,运行系统的时候有两个地方要特别关注:命令行和配置文件,它们主要提供了两个非常关键的信息:系统具备哪些能力和系统将会如何运行。这些信息是我们窥视系统内部运行机制和原理的一扇窗口。
例如下面是Memcache的启动参数一部分
通过这几个启动参数,你可以获取如下一些信息:
Memcache支持UNIX socket通信和TCP通信。
Memcache可以指定内存大小。
lock memory看起来和内存有关但具体是什么意思配置和不配置有什么区别么
通常情况下如果我们将每个命令行参数和配置项的作用和原理都全部掌握清楚了的话基本上对系统已经很熟悉了。我的一个习惯是不管三七二十一先把所有的配置项全部研究一遍包括配置项的原理、作用、影响并且尝试去修改配置项然后看看系统会有什么变化。例如将Memcache的“conn-limit”改为1后查看多个连接请求时Memecache会返回什么错误、记录什么日志等。
第三步:原理研究
完成前两个步骤后,我们对系统已经有了初步的感觉和理解,此时可以更进一步去研究其原理。其实在研究命令行和配置项的时候已经涉及一部分原理了,但是还不系统,因此我们要专门针对原理进行系统性的研究。这里的关键就是“系统性”三个字,怎么才算系统性呢?主要体现在如下几个方面:
关键特性的基本实现原理
每个流行的开源项目之所以能够受到大众的欢迎肯定是有一些卖点的常见的有高性能、高可用、可扩展等特性那到底这些项目是如何做到其所宣称的那么牛的呢这些牛X的技术实现就是我们要学习的地方。
例如Memcache的高性能具体是怎么做到的呢首先是基于libevent实现了高性能的网络模型其次是内存管理Slab Allocator机制。为了彻底理解Memcache的高性能网络模型我们需要掌握很多知识多路复用、Linux epoll、Reactor模型、多线程等通过研究Memcache的高性能网络模型我们能够学习一个具体的项目中如何将这些东西全部串起来实现了高性能。
再以React为例Virtual DOM的实现原理是什么、为何要实现Virtual DOM、React是如何构建Virtual DOM树、Virtual DOM与DOM什么关系等通过研究学习Virtual DOM即使不使用React我们也能够学习如何写出高性能的前端的代码。
优缺点对比分析
这是我想特别强调的一点,只有清楚掌握技术方案的优缺点后才算真正的掌握这门技术,也只有掌握了技术方案的优缺点后才能在架构设计的时候做出合理的选择。
优缺点主要通过对比来分析,即:我们将两个类似的系统进行对比,看看它们的实现差异,以及不同的实现优缺点都是什么。
典型的对比有Memcache和Redis例如仅举例说明实际上对比的点很多Memcache用多线程Redis用单进程各有什么优缺点Memcache和Redis的集群方式各有什么优缺点
即使是Redis自身我们也可以对比RDB和AOF两种模式的优缺点。
在你了解了什么是“系统性”后,我来介绍一下原理研究的手段,主要有三种:
通读项目的设计文档例如Kafka的设计文档基本涵盖了消息队列设计的关键决策部分Disruptor的设计白皮书详细的阐述了Java单机高性能的设计技巧。
阅读网上已有的分析文档:通常情况下比较热门的开源项目,都已经有非常多的分析文档了,我们可以站在前人的基础上,避免大量的重复投入。但需要注意的是,由于经验、水平、关注点等差异,不同的人分析的结论可能有差异,甚至有的是错误的,因此不能完全参照。一个比较好的方式就是多方对照,也就是说看很多篇分析文档,比较它们的内容共同点和差异点。
Demo验证如果有些技术点难以查到资料自己又不确定则可以真正去写Demo进行验证通过打印一些日志或者调试能清晰的理解具体的细节。例如写一个简单的分配内存程序然后通过日志和命令行jmap、jstat、jstack等来查看Java虚拟机垃圾回收时的具体表现。
第四步:测试
通常情况下如果你真的准备在实际项目中使用某个开源项目的话必须进行测试。有的同学可能会说网上的分析和测试文档很多直接找一篇看就可以了如果只是自己学习和研究这样做是可以的因为构建完整的测试用例既需要耗费较多时间又需要较多机器资源如果每个项目都这么做的话投入成本有点大但如果是要在实践项目中使用必须自己进行测试因为网上搜的测试结果不一定与自己的业务场景很契合如果简单参考别人的测试结果很可能会得出错误的结论。例如开源系统的版本不同测试结果可能差异较大。同样是K-V存储别人测试的value是128字节而你的场景value都达到了128k字节两者的测试结果也差异很大不能简单照搬。
测试阶段需要特别强调的一点就是测试一定要在原理研究之后做不能安装完成立马就测试原因在于如果对系统不熟悉很可能出现命令行、配置参数没用对或者运行模式选择不对导致没有根据业务的特点搭建正确的环境、没有设计合理的测试用例从而使得最终的测试结果得出了错误结论误导了设计决策。曾经有团队安装完成MySQL 5.1后就进行性能测试测试结果出来让人大跌眼镜经过定位才发现innodb_buffer_pool_size使用的是默认值8M。
第五步:源码研究
源码研究的主要目的是学习原理背后的具体编码如何实现通过学习这些技巧来提升我们自己的技术能力。例如Redis的RDB快照、Nginx的多Reactor模型、Disruptor如何使用volatile以及CAS来做无锁设计、Netty的Zero-Copy等这些技巧都很精巧掌握后能够大大提升自己的编码能力。
通常情况下不建议通读所有源码因为想掌握每行代码的含义和作用还是非常耗费时间的尤其是MySQL、Nginx这种规模的项目即使是他们的开发人员都不一定每个人都掌握了所有代码。带着明确目的去研究源码做到有的放矢才能事半功倍这也是源码研究要放在最后的原因。
对于一些基础库除了阅读源码外还可以自己写个Demo调用基础库完成一些简单的功能然后通过调试来看具体的调用栈通过调用栈来理解基础库的处理逻辑和过程这比单纯看代码去理解逻辑要高效一些。例如下面是Netty 4.1版本的telnet服务器样例调试的堆栈通过堆栈我们可以看到完整的调用栈
时间分配
前面介绍的“自顶向下”5个步骤完整执行下来需要花费较长时间而时间又是大部分技术人员比较稀缺的资源。很多人在学习技术的时候都会反馈说时间不够版本进度很紧很难有大量的时间进行学习但如果不学习感觉自己又很难提升面对这种两难问题具体该如何做呢
通常情况下以上5个步骤的前3个步骤不管是已经成为架构师的技术人员还是立志成为架构师的技术人员在研究开源项目的时候都必不可少第四步可以在准备采用开源项目的时候才实施第五步可以根据你的时间来进行灵活安排。这里的“灵活安排”不是说省略不去做而是在自己有一定时间和精力的时候做因为只有这样才能真正理解和学到具体的技术。
如果感觉自己时间和精力不够,与其蜻蜓点水每个开源项目都去简单了解一下,还不如集中精力将一个开源项目研究通透,就算是每个季度只学习一个开源项目,积累几年后这个数量也是很可观的;而且一旦你将一个项目研究透以后,再去研究其他类似项目,你会发现自己学习的非常快,因为共性的部分你已经都掌握了,只需要掌握新项目差异的部分即可。
今天我给你分享了我对于学习开源项目的看法和步骤希望对你有所帮助。如果你在工作、学习中遇到什么问题不论是技术、管理或者其他方面欢迎在“特别放送”里给我留言可能你的问题就是“华仔放学别走第4期”的主题。

View File

@@ -0,0 +1,21 @@
因收到Google相关通知网站将会择期关闭。相关通知内容
新书首发 《从零开始学架构》
我是李运华。在完成「从0开始学架构」专栏后今天又有一件意义重大的事想与你分享那就是我的新书《从零开始学架构》出版了并且已经提前上架极客商城。
从书名你可以看出来,这本书脱胎于我的专栏,书中涵盖了专栏所有精华内容,经过我的重新梳理,这本书会显得更加紧凑,相信一定可以让你耳目一新。
我的专栏有超过2.6万同学一起学习,你们的留言总是可以让我重新思考过去我所掌握的技术,也为这本书的优化提供了很多建议,可以说这也是我和你共同完成的图书,这本书的问世,离不开你的帮助。
对已经订阅了专栏的同学来说,《从零开始学架构》这本书的内容我就不多赘述了。作为一本纸质图书,它可以和专栏组成一套完整的学习体系,专栏的音频和图文结合纸质图书的阅读体验,这种学习方式是非常有效的,所以我也一直期待这本书可以尽快出版。
《从零开始学架构》现已在极客商城提前预售作为订阅专栏的老同学我给你申请了专属优惠码可以与限时优惠价叠加使用你可以在极客商城待付款订单中点击“使用优惠”输入优惠码“jiagou”在结算时再减6元。
最后,我还是要感谢你跟我一起创作了这本书,技术成就梦想,坚持就能成功。

View File

@@ -0,0 +1,97 @@
因收到Google相关通知网站将会择期关闭。相关通知内容
架构专栏特别放送 华仔放学别走第1期
各位同学晚上好我就是那位在每期专栏最后都会乱入进来的编辑Shawn[捂脸],对,我是来送福利的。
“从0开始学架构”专栏已经更新了9期概念和基础已经讲了不少不知道你掌握的如何呢每期华仔都会在最后提出一个思考题希望能让你在学习后有一个思考提升的过程既可以记下心得体会也许还能碰撞出新的想法。
以周为单位今天我会让华仔选出01-07期的优质精选留言送给入选的同学价值68元的专栏阅码作为鼓励。入选的留言的标准既可以是经过深度思考的回答也可以是对其他同学有启发的经验分享更可以是产生共鸣的疑问。
在公布上榜精选留言前,应广大同学的强烈呼声“华仔,放学别走!”,问他几个在评论中大家普遍感兴趣的问题。
Shawn看到有同学提到“能看到资深技术专家的分享实属不易感觉自己像是站在巨人的肩膀上学习机会难得”华仔你是怎么看待知识分享的
华仔:首先,知识分享能够促进知识的传播和发展,其实我们都是站在前人的肩膀上才能有今天的成就;其次,知识分享对于作者来说也是一个自我提升的过程,很多知识和技术,没分享出来的时候我觉得自己很清楚了,但真正去写才会发现,这里有个细节没考虑,那里有个疑问需要澄清,只有真正写完了才会觉得自己基本掌握了;同时分享出去后,会有很多读者帮忙审核检阅,会提出自己的一些看法,通过这些交流又能够进一步加深理解。
所以很多朋友问我怎么提升技术,我推荐的一个方法就是写博客,既能够加深自己对知识的理解,又能够锻炼自己的表达能力,还能够磨练自己的意志力(坚持写很不容易),一举三得。某个方面的博客写多了,也许哪天你也能够出一个专栏。
Shawn华仔现在专栏更新到第9期还在讲理论和基础已经有同学提出想学实战技巧你怎么看待理论与实践之间的关系
华仔架构设计也需要知行合一知是行之始行是知之成所以我在开始的时候讲述了架构设计相关的理论知识例如架构设计的本质、目的、原则等只有掌握了这些内容才能在架构设计实践的时候有理可依有据可循而不是凭感觉、拍脑袋、照猫画虎等。其实架构设计和编程一样我们要学Java编程肯定要先熟悉Java的语法、API然后才能开始编码再通过实际编码实践加深对这些理论知识的理解。
我在带团队的时候,发现很多技术人员在做架构设计的时候,最缺乏的就是架构设计的理论体系,在设计的时候摸着石头过河,踩了一个坑就积累了一点经验,但是下次换个业务换个场景,又要踩其他坑。这也是我萌生写这样一个专栏的一个推动因素,因为我们的学校没有教架构相关的课程,架构领域也缺乏经典的体系化的书籍,导致技术人员在架构方面的能力提升速度较慢。
具体的实战技巧其实不用担心专栏后面的内容大部分都是讲具体的实战技巧例如高性能架构模式、高可用架构模式、FMEA、CAP、异地多活、互联网架构演进等。
Shawn介绍一下你每天学习新知识的方式吧或者你觉得怎样学习你的架构专栏效果会更好
华仔:我是坐地铁上班,一般我都是在地铁上看书或者看专栏,晚上睡觉前和周末也会挤出时间来看书或者学习,更详细的做法可以参考我的一个公开演讲稿《吃的草够多,你也能成为大牛》。
我的专栏是我自己多年经验和思考的总结积累,是一套完整的架构设计方法论,涵盖的内容较多,所以要想学好,首先不能着急,循序渐进,争取每篇都有一些收获,可以尝试写一些笔记、心得;其次需要知行合一,学习了专栏的内容后,尽量结合自己的业务和系统,尝试拿这套方法论去分析,看看有什么收获或者疑问,注意并不是一定要亲自做架构才能实践,针对已有的系统进行分析,学习业界已有的架构案例都可以,当然如果有实践机会那就更好;第三多交流,一个人的思维难免有局限性和思维盲点,如果能和同事或者朋友一起学习,然后一起讨论,互相印证,效果会更好。
Shawn总有同学在问专栏以外有没有推荐的参考书或资料华仔能不能推荐几种
华仔技术方面我推荐《UNIX编程艺术》这本书里面的思想和原则无论对于编码还是架构设计都很有指导意义。
个人成长方面我推荐《异类》这本书通过很多的案例来说明究竟怎么样才能成功10000小时理论只是其中的一部分还有很多有趣的发现例如如何才算赢在起跑线上等。
人生境遇方面我推荐《羊皮卷》,其中有一篇《选择的力量》,我看了后醍醐灌顶,真的是就像佛家禅宗说的突然“悟道”一样深受启发,从此以后很多为人处世方式都因此而改变了。
Shawn看到那么多同学的留言有什么想说的吗
华仔:非常感谢每一位同学的积极参与,很多同学留言表示感谢,让我感到很开心,说明专栏能够真正帮助大家学习架构设计的技术和提升自己的能力。
很多同学的评论内容质量很高,感谢你们的分享,通过自己的思考,自己有收获,同时也能帮助其他同学。
也有很多同学基于自己的业务进行了思考和提出了一些疑问,这是非常好的学习方式,也是知行合一的一种行动方式,我也会尽量一一回复,帮助你解决一些实际的问题。
再次感谢你对架构专栏的厚爱,让我们一起加油,一起成长!
留言精选
华仔:做技术里面最擅长讲故事的,讲故事里面最擅长做技术的,说的就是你

华仔:说的这么好,除了赞同就是鼓掌了

华仔:用马哲来思考架构设计,我表示这高度我要仰望一下
华仔实现财富自由迎娶白富美当上CTO走向人生巅峰就靠你的第3句话了

华仔:感谢,我要去查查这位大神,学习一下。

华仔:非常好的实践方法,我们在架构设计流程中会讲到,就是指设计“备选方案”。

华仔:其实我最开始构思的时候是想写一本架构师工作指南,包括技术、管理、沟通等,后来发现目标太宏伟,时间精力有限,最后决定还是聚焦技术,你说的内容非常对,架构师在设计的时候还要考虑团队人员和组织的复杂度和能力水平。

View File

@@ -0,0 +1,90 @@
因收到Google相关通知网站将会择期关闭。相关通知内容
架构专栏特别放送 华仔,放学别走! 第2期
各位同学晚上好我是架构专栏的编辑Shawn。今天又到周五啦没错我又出来送福利了[捂脸]。
[“华仔放学别走”第1期]不知道你看了没有华仔回答了关于知识分享、理论与实践、专栏学习方法、推荐的参考书等几个问题希望你从中能够有所收获。今天是“华仔放学别走”第2期继续回答你所关注的问题然后展示出08 ~ 13期被选中的精选留言并给留言被选中的同学送出价值68元的专栏阅码。话不多说开始今天的问答环节。
Shawn有做公司架构/网站架构/App架构的同学这个专栏能帮助到他们吗
华仔有的同学在学习了一段时间后跟我留言交流说感觉专栏的内容好像比较适合做互联网后台架构不太适合企业应用、客户端这类系统。其实这是一个误解我之所以在前面花费很大篇幅来讲架构设计的目的、架构设计原则、架构设计流程等看起来比较偏理论的内容而没有一上来就讲异地多活、高性能架构之类的怎么做原因就在于这是一套完整的架构设计理论体系不管是企业应用还是客户端应用都可以按照这个设计理论体系去操作。我以手机App为例首先我们分析一下App的复杂度主要来源是什么通常情况下App的主要复杂度就是可扩展因为要不断地开发新的需求高性能和高可用也涉及高性能主要和用户体验有关高可用主要是减少崩溃。其次再看App的架构需要遵循架构设计原则么答案是肯定需要。刚开始为了业务快速开发可能用“原生+H5”混合架构后来业务发展功能更复杂了H5可能难以满足体验架构又需要演进到“纯原生”如果业务再发展规模太庞大则架构又可能需要演进到“组件化、容器化”。以上通过手机App的为例说明这套架构设计理论是通用的有兴趣的同学可以按照这种方式分析一下企业应用会发现这套理论也是适应的。
Shawn讲讲你总结“架构设计三原则”的过程吧
华仔“架构设计三原则”是综合各方面的信息和思考得来的。首先是我自己的经验包括成功的经验和失败的教训其次是分析了很多业界的架构演讲和技术发展历史第三是看了一些关于技术本质的书籍而受到的启发例如《技术的本质》《系统之美》等。其实最初整理的架构设计原则有10多条但我觉得10多条太多了不聚焦也不利于理解因此去芜存菁最终得到了“架构设计三原则”这三个原则是最重要也是最核心的。
如下是我原来整理的设计原则可以看到一共有14条
Shawn“PPT架构师”的口头禅是“细节不讨论”一个优秀的架构师需要对细节有多少考虑呢
华仔这是一个非常好的问题也是很多同学困惑的问题我分享一下我的做法以我学习Elasticsearch为例具体的做法是
搭建一个单机伪集群,搭建完成后看看安装路径下的文件和目录,看看配置文件有哪些配置项,不同的配置项会有什么样的影响。
执行常用的操作,例如创建索引,插入、删除、查询文档,查看一下各种输出。
研究其基本原理,例如索引、分片、副本等,研究的时候要多思考,例如索引应该如何建,分片数量和副本数量对系统有什么影响等。
和其他类似系统对比例如Solr、Sphinx研究其优点、缺点、适用场景。
模拟一个案例看看怎么应用。例如假设我用Elasticsearch来存储淘宝的商品信息我应该如何设计索引和分片。
查看业界使用的案例,思考一下别人为何这么用;看看别人测试的结果,大概了解性能范围。
如果某部分特别有兴趣或者很关键可能去看源码例如Elasticsearch的选举算法我目前还没看^_^)。
如果确定要引入,会进行性能和可用性测试。
这样一套组合拳下来,基本上能够满足在架构设计时进行选型判断,而且花费的时间也不多。我并不建议拿到一个系统一开始就去读源码,效率太低,而且效果也不好。
Shawn谈谈架构师沟通能力的重要性吧
华仔:架构师是业务和技术之间的桥梁,同时通常情况下还会确定整体项目的步骤。因此,架构师的沟通能力非常重要,既要说得动老板,让老板支持自己的设计决定;又要镇得住技术人员,让技术人员信服自己的设计选择;同时还要能够理解业务,结合业务不同发展阶段设计合适的架构,所以也要参与产品和项目决策。由于架构设计过程中存在很多判断和选择,而且不一定都有明确量化的标准,因此不同的人有不同的看法是普遍情况。这种情况下架构师既需要专业能力过硬,又需要具备良好的沟通技巧,才能促使业务、项目、技术三方达成一致。
当然,架构师的核心能力还是技术能力,过硬的技术才是良好沟通的基础,否则单纯靠沟通技巧甚至花言巧语,一次两次可能奏效,但后面被打脸打多了,也就没人信任了。
Shawn有同学留言说给企业做项目甲方会不顾业务需要只要是业界流行的技术就要求在项目中采用这种情况下怎样才能符合“架构设计三原则”
华仔首先业务第一先把订单签下来才有后面的架构设计如果硬要说甲方的要求不合理不满足“架构设计三原则”结果订单都拿不到那是没有意义的。其次这种情况我把它归为“架构约束”即这不是架构师能够选择的而是架构师必须遵守的因此这里不需要使用“架构设计三原则”来判断。第三这种情况下架构师还是可以应用“架构设计三原则”来指导架构设计比如说客户要求采用DockerDocker的网络模式有5种host模式使用起来比bridge模式简单那我们就用host模式如果客户再要求需要对Docker进行统一管理那我们是自己研发Docker管理平台还是直接用Kubernetes呢按照简单原则来说肯定用Kubernetes了。
通过这个示例也可以看出,“架构设计三原则”主要是指架构师在选择和判断时采取的指导原则;但如果是架构的基本需求或者约束必须被满足时,架构师此时的选择是采取什么样的方案能够更好的满足这些需求和约束。
留言精选
华仔:有个懂技术的好老大是一件多么幸福的事情:)
华仔有钱也不能任性微软95年也不可能开发出Windows 10操作系统业务量大了重构甚至重写那是自然而然的不会浪费也不会导致错失产品机会Windows、Android、淘宝、QQ都是这么过来的。
华仔:终于明白了我一开始就提架构设计的核心目的的良苦用心了吧
华仔实现起来细节较多但没有想象的那么复杂一般的公司如果有人力的话做一个简单够用的消息队列不难用MySQL做存储的话不到1万行代码就可以搞定。
华仔:如有雷同,实属巧合,确认过眼神,我不是你们公司的人 ^_^
华仔:架构师确实需要在技术广度和技术深度两方面都要兼顾,但如何把握技术深度这个“度”,不同架构师有不同的理解,但千万不能说“细节不讨论”“你上网搜”,这样会没有技术公信力。

View File

@@ -0,0 +1,126 @@
因收到Google相关通知网站将会择期关闭。相关通知内容
架构师必读书单 华仔,放学别走! 第5期
你好,我是华仔。
在专栏更新的时候很多同学留言希望我推荐一些书籍可以课后继续学习正好我自己也是一个爱读书的人最近7 ~ 8年平均每年读书超过50本因此今天就从我读过的书籍中选择一些让我印象非常深刻的推荐给你。我把这些书分为成长、技术和业务三个方面因为架构师本身就是一个比较综合的职位对综合技能要求很高需要你从各方面提升自己。
我推荐的书是我从几百本中挑出来的可以说是经典中的经典了但这并不意味着只要看完这些书就够了读书和技术提升是类似的都是一个长期积累的过程积累越多、收获越大。关于技术人员具体如何学习、如何提升可以参考我之前在InfoQ上发表的文章《佛系程序员的月薪五万指南》。
每本书我习惯用“一句话推荐”,虽然显得比较“简短”,但我认为推荐语太多会框住你对书的理解,也担心剧透太多会影响你的阅读体验。好书就像美酒一样,一定要自己品尝才能真正体会其中美妙的滋味。
成长篇
《异类》

一句话推荐颠覆你对成功的认知例如什么才是赢在起跑线为何现在的富人都是大约生于1955年左右
《随机漫步的傻瓜》

一句话推荐:只要看这一本书,你就能免受所有鸡汤的毒害!
《一万小时天才理论》

一句话推荐1万小时理论实践版详细阐述了1万小时天才理论的3个关键点。
《情商》

一句话推荐:如果你认为你的老板还不如你聪明,那你需要好好看看这本书。
《优秀到不能被忽视》

一句话推荐:不管是工作还是爱好,要想成功的原则是什么?很简单,“做别人愿意买单的事情”!
《影响力大师》

一句话推荐天天立flag月月打自己的脸不是你意志力不行而是你方法不对这本书可以给你一套完善、可操作的方法。我以前读的版本叫《关键影响力》新版改名叫《影响力大师》。
技术篇
推荐技术书籍实际上是有一定局限性的因为每个技术领域其实差异还是挺大的就算都叫程序员前端程序员、客户端程序员、后端程序员之间差异就很大即使都是后端程序员Linux开发和Windows开发所需要的技术也不一样。因此我提炼了一个通用的技术书籍学习路径不同技术领域可以按照这个路径去拆解
深度学习你的代码运行环境例如Linux程序员一定要深入学习Linux和UNIX的操作系统iOS程序员要深入学习iOS系统前端程序员要深入学习浏览器原理以此类推。
深入学习你的核心工具例如Java程序员的核心工具是Java嵌入式程序员是C而DBA就不是学编程语言而是学MySQL或者Oracle了。
深度学习领域基础知识例如后端程序员的网络编程前端程序员的动效知识Android客户端程序员的渲染知识以及所有程序员都要求的算法知识等。
广泛学习技术领域的通用成熟技术例如前端程序员要学的React和VueJava程序员要学的Netty、Spring互联网后端程序员的标配MySQL、Redis等。
下面我以Linux后端Java程序员为例给你推荐相关技术书籍。
《UNIX编程艺术》

一句话推荐经典书籍结合UNIX的历史来讲UNIX设计哲学改变你对编程的认知和理解。
《UNIX网络编程卷1

一句话推荐:经典书籍,网络编程必读。书很厚,重点是前三部分,不需要一次全部读懂,先通读,后面经常参考并且加深理解。
《UNIX环境高级编程》

一句话推荐经典书籍Linux/UNIX C/C++程序员必读就算是Java、PHP、Python等程序员也要通读一遍了解系统底层能力有助于理解编程语言的各种实现。
《Linux系统编程》

一句话推荐和《UNIX环境高级编程》类似Linux平台可以看这本。
《TCP/IP详解卷1

一句话推荐经典书籍全面介绍TCP/IP协议栈各种协议重点看TCP和IP部分。
《算法之美》

一句话推荐:讲算法非常有趣的一本书,告诉你如何将算法应用于恋爱、生活、工作!
《算法设计与应用》

一句话推荐:将算法与实际应用结合起来,从应用引出算法然后进行算法推理,如果你数学很牛,可以挑战一下这本书;如果你数学很菜,那我更加推荐这本书,因为其中的算法原理和应用场景分析得清晰易懂。
《Java编程思想》

一句话推荐经典书籍全面介绍Java编程入门必备。
《深入理解Java虚拟机》

一句话推荐全面理解Java虚拟机原理介绍得深入浅出很少有技术书籍我会优先推荐国内作者而这本是我大力推荐的。
《C++ Primer》

一句话推荐经典书籍全面介绍C++编程。当年我看了很多C++书籍都不得要领,看了这本后豁然开朗。
业务篇
不管是普通程序员还是架构师,实践工作中都需要有一定的业务理解能力,而架构师的业务理解能力要求更高。理解业务一方面有利于更好地设计有针对性的架构或者方案,另外一方面也可以防止被产品经理坑
《增长黑客》
一句话推荐:肖恩·埃利斯和摩根·布朗的这本书理论体系完整,既给出了很多实践技巧,又总结了很多经验和需要避开的陷阱。
《需求》
一句话推荐:如何理解用户需求、如何满足用户需求、同样产品为何有的公司失败而有的公司取得了巨大成功?这本书让我茅塞顿开,建议技术同学都推荐这本书给你们的产品经理。
《淘宝十年产品事》

一句话推荐这本书总结了淘宝10多年发展过程中产品遇到的各种坑和挑战让你明白“罗马不是一天建成的”产品也是逐步演化的这也是我的“架构设计三原则”中的“演化原则”
《定位》

一句话推荐:告诉你如何做业务战略规划,有些偏重理论,架构师需要学习,程序员可以先放一边。
《宝洁制胜战略》

一句话推荐结合宝洁的经验提出了一套完善的战略规划和落地方法理论与实践兼备架构师必备拿着这套方法论就可以PK你的老板了。
最后我想说,收藏书单和囤书不是目的,更不能收获成长,只有像学习专栏那样坚持下来,坚持阅读、坚持记录、坚持分享,才能让你从书中品尝到最妙的美酒。
编辑乱入:华仔推荐的图书现已上架“极客商城”,价格比其他电商平台更美丽哦~现在订购,请从“极客时间发现页”下滑进入“极客商城”,即可选购华仔推荐图书。

View File

@@ -0,0 +1,162 @@
因收到Google相关通知网站将会择期关闭。相关通知内容
架构师成长之路 华仔,放学别走! 第4期
你好我是华仔。《从0开始学架构》专栏已经全部更新完毕我在专栏里给你讲述了我的完整架构设计方法论包括架构设计的概念、原则、步骤、技巧、模式等这些内容是我融合多年来的学习、实践、思考总结得出来的精华。“王婆自夸”一下专栏就相当于一部《九阳真经》你按照武功秘籍的方法去修炼自然能够比站在村口大树下打木人桩效率要高得多。然而要成为高手光知道招式还远远不够更重要的是内功和判断能够一眼看出对手的弱点或者破绽知道“什么时候用什么招式”“遇到什么对手用什么招式”更重要。
以架构设计原则的“合适原则”为例专栏讲述了架构设计要遵循“合适原则”不要过度设计这个点非常关键能够避免架构设计的时候盲目超前设计。但是我们在具体架构设计的时候到底什么是“合适”专栏也无法给出一个明确的标准可以放之四海而皆准去套用因为“合适”和很多因素有关业务发展、团队规模、技术实力、领导的喜好等。此时到底什么是“合适”就依赖架构师的“内功”了很有可能同一个团队A架构师认为X方案是合适的B架构师认为Y方案是合适的原因就在于不同的架构师“内功”不一样。
我认为,架构师的内功主要包含三部分:判断力、执行力、创新力,简单解释如下:
判断力:能够准确判断系统的复杂度在哪里,就像武侠高手一样,能准确地看出对手的破绽和弱点。
执行力:能够使用合适的方案解决复杂度问题,就像武侠高手一样,能选择合适的招式或者方法打败对手。
创新力:能够创造新的解决方案解决复杂度问题,就像武侠世界里,小一些的创新是创新招式,而武学宗师能够创立新的武学或者心法,例如张三丰创立太极拳一样。
因此,要成为一个优秀的架构师,就需要不断地提升自己这几方面的内功,而这三方面的能力主要来源于经验、视野、思考。
经验:设计过的系统越多、系统越复杂,架构师的内功也就越强,不管是成功的架构,还是失败的架构,不管是踩坑的经验,还是填坑的经验,都将成为架构师内功的一部分。
视野:掌握的知识和技能越多、越深,架构师的内功也就越强,他山之石可以攻玉,站在巨人的肩膀上会看的更高更远。
思考:经验和视野都是外部输入,类似于我们吃的食物,但光吃还不行,还要消化,将其变为我们自己的营养,这就是思考的作用。思考能够将经验和视野中的模式、判断、选择、技巧等提炼出来为我所用,思考也能促使我们产生新的创意和灵感。
结合上面的分析,从程序员到架构师的成长之路,总的指导原则是:积累经验,拓宽视野,深度思考。按照这个总的原则为指导,接下来我们看看从程序员到架构师的成长过程中,具体如何实践。
我把程序员到架构师的技术成长之路分为几个典型的阶段:工程师 - 高级工程师 - 技术专家 - 初级架构师 - 中级架构师 - 高级架构师。虽然总的指导原则是一样的,但具体的实践方法有很大差别,如果在正确的阶段采取了错误的方法,可能会出现事倍功半的问题。
工程师
【阶段描述】
成为一个合格的工程师需要1 ~ 3年时间其典型特征是“在别人的指导下完成开发”这里的“别人”主要是“高级工程师”或者“技术专家”通常情况下高级工程师或者技术专家负责需求分析和讨论、方案设计工程师负责编码实现高级工程师或者技术专家会指导工程师进行编码实现。
【成长指导】
工程师阶段是最原始的“基础技能积累阶段”主要积累基础知识包括编程语言、编程工具、各类系统的基本使用。以Java后端工程师为例工程师阶段需要积累的经验和技能有
Java的语法、基本数据结构的使用。
Eclipse、IDEA、Maven、Linux命令行等各种工具。
数据库CRUD操作、缓存的基本使用等。
业务系统的基本流程。
工程师阶段最好的学习方法就是找经典的书籍系统地学习而不要遇到一个问题到网上搜搜然后就解决了事。以Java为例《Java编程思想》《Java核心技术》《TCP/IP协议》这类大部头一定要完整地看一遍即使里面很多内容当前工作暂时用不上。
高级工程师
【阶段描述】
成长为高级工程师需要2 ~ 5年时间其典型特征是“独立完成开发”包括需求分析、方案设计、编码实现其中需求分析和方案设计已经包含了“判断”和“选择”只是范围相对来说小一些更多是在已有架构下进行设计。以Java后端工程师为例高级工程师需要完成的工作包括
MySQL数据库表如何设计是设计成两个表还是三个表
是否要用缓存缓存的Key和Value如何设计缓存的更新策略是什么
产品提出的需求是否合理?是否有更好的方式来满足?
【成长指导】
从普通工程师成长为高级工程师主要需要“积累方案设计经验”简单来说就是业务当前用到的相关技术的设计经验。以Java后端高级工程师为例包括表设计经验、缓存设计经验、业务流程设计经验、接口设计经验等。当接到一个业务需求的时候高级工程师能够组合这些设计经验最终完成业务需求。
高级工程师阶段相比工程师阶段,有两个典型的差异:
深度如果说工程师是要求知道How那高级工程师就要求知道Why了。例如Java的各种数据结构的实现原理因为只有深入掌握了这些实现原理才能对其优缺点和使用场景有深刻理解这样在做具体方案设计的时候才能选择合适的数据结构。
理论理论就是前人总结出来的成熟的设计经验例如数据库表设计的3个范式、面向对象的设计模式、SOLID设计原则、缓存设计理论缓存穿透、缓存雪崩、缓存热点等。
针对技术深度我的建议还是系统地学习包括看书和研究源码。例如研究Java虚拟机可以看《深入理解Java虚拟机》、研究MySQL可以看《MySQL技术内幕InnoDB存储引擎》、研究Memcache可以去看其源码。
针对设计理论,由于涉及的点很多,没有一本书能够涵盖这么多的设计点,因此更多的是依靠自己去网上搜索资料学习。那我们怎么知道哪些地方会有设计理论呢?简单来说,就是假设每个设计环节都有设计理论,然后带着这种假设去搜索验证看看是否真的有很熟的设计理念。
技术专家
【阶段描述】
成长为技术专家需要4 ~ 8年时间其典型的特征是“某个领域的专家”通俗地讲只要是这个领域的问题技术专家都可以解决。例如Java开发专家、PHP开发专家、Android开发专家、iOS开发专家、前端开发专家等。通常情况下“领域”的范围不能太小例如我们可以说“Java开发专家”但不会说“Java多线程专家”或“Java JDBC专家”。
技术专家与高级工程师的一个典型区别就是高级工程师主要是在已有的架构框架下完成设计而技术专家会根据需要修改、扩展、优化架构。例如同样是Java开发高级工程师关注的是如何优化MySQL的查询性能而技术专家可能就会考虑引入Elasticsearch来完成搜索。
【成长指导】
从高级工程师成长为技术专家主要需要“拓展技术宽度”因为一个“领域”必然会涉及众多的技术面。以Java后端开发为例要成为一个Java开发专家需要掌握Java多线程、JDBC、Java虚拟机、面向对象、设计模式、Netty、Elasticsearch、Memcache、Redis、MySQL等众多技术。常见的拓展技术宽度的方法有
学习业界成熟的开源方案例如Java开发可以去学习Redis、Memcache、Netty等Android开发可以去研究Retrofit、Fresco、OkHttp等。
研究业界的经验分享例如BAT、FANG等大公司的经验可以通过参加技术大会等方式去近距离了解。
需要注意的是拓展技术宽度并不意味着仅仅只是知道一个技术名词而是要深入去理解每个技术的原理、优缺点、应用场景否则就会成为传说中的“PPT技术专家”。例如以Java开发为例知道Netty是个高性能网络库是远远不够的还需要学习Netty的原理以及具体如何使用Netty来开发高性能系统。
初级架构师
【阶段描述】
成长为初级架构师需要5 ~ 10年时间其典型特征就是能够“独立完成一个系统的架构设计”可以是从0到1设计一个新系统也可以是将架构从1.0重构到2.0。初级架构师负责的系统复杂度相对来说不高例如后台管理系统、某个业务下的子系统、100万PV量级的网站等。
初级架构师和技术专家的典型区别是:架构师是基于完善的架构设计方法论的指导来进行架构设计,而技术专家更多的是基于经验进行架构设计。简单来说,即使是同样一个方案,初级架构师能够清晰地阐述架构设计的理由和原因,而技术专家可能就是因为自己曾经这样做过,或者看到别人这样做过而选择设计方案。
但在实践工作中技术专家和初级架构师的区别并不很明显事实上很多技术专家其实就承担了初级架构师的角色因为在系统复杂度相对不高的情况下架构设计的难度不高用不同的备选方案最终都能够较好地完成系统设计。例如设计一个日PV 100万的网站MySQL + Memcache + Spring Boot可以很好地完成MongoDB + Redis + Nginx + php-fpm也可以很好地完成备选方案设计和选择并不太难更多的是看团队熟悉哪个技术。
【成长指导】
从技术专家成长为初级架构师,最主要的是形成自己的“架构设计方法论”,我的架构设计专栏其实就是讲述完整的架构设计方法论,包括架构设计目的、架构设计原则、架构设计步骤、架构设计模式等,类似的架构设计方法论还有《恰如其分的软件架构:风险驱动的设计方法》和《领域驱动设计》等。
要形成自己的架构设计方法论,主要的手段有:
系统学习架构设计方法论,包括订阅专栏或者阅读书籍等。
深入研究成熟开源系统的架构设计这个手段在技术专家阶段也会用到但关注点不一样同样是研究开源系统技术专家阶段聚焦于如何更好地应用开源项目初级架构师阶段聚焦于学习其架构设计原理和思想例如Kafka的文档中就有关于消息队列架构设计的分析和取舍。
结合架构设计方法论,分析和总结自己团队甚至公司的各种系统的架构设计优缺点,尝试思考架构重构方案。如果在这个基础上真的能够推动架构重构,那就更好了,既能够实践自己的架构设计方法论,同时积累经验,又能够展现自己的技术实力,拿到结果。
中级架构师
【阶段描述】
成长为中级架构师需要8年以上时间其典型特征是“能够完成复杂系统的架构设计”包含高性能、高可用、可扩展、海量存储等复杂系统例如设计一个和Kafka性能匹敌的消息队列系统、将业务改造为异地多活、设计一个总共100人参与开发的业务系统等。
中级架构师与初级架构师的典型区别在于系统复杂度的不同,中级架构师面对的系统复杂度要高于初级架构师。以开源项目为例,初级架构师可能引入某个开源项目就可以完成架构设计,而中级架构师可能发现其实没有哪个开源项目是合适的,而需要自己开发一个全新的项目,事实上很多开源项目就是这样诞生出来的。
【成长指导】
从初级架构师成长为中级架构师,最关键的是“技术深度和技术理论的积累”,例如:
技术理论CAP、BASE是异地多活的设计理论基础、Paxos是分布式一致性的基础算法、2PC、3PC是分布式事务的基础算法等。
技术深度Kafka用磁盘存储还能做到高效是因为磁盘顺序写Disruptor高性能是结合CPU预读取机制、缓存行、无锁设计等基础技术Storm的高效异或确认机制Flink的分布式快照算法等。
很多同学对这点可能有疑问,这些技术理论和技术深度的事情不应该是高级工程师阶段或者技术专家阶段就应该积累的么?为何到了中级架构师阶段反而是成长的关键了呢?主要原因在于高级工程师或者技术专家阶段即使去学习这些技术,实际上也比较难理解透彻,更加难以有机会去应用,更多的时候只是了解有这个技术点而已;而到了中级架构师阶段,面对高复杂度的系统,很多时候就是几个关键技术细节决定整个架构设计的成败,或者某个设计方案理论上就是不可行的,如果不深刻理解理论和相关的关键技术点,很难设计优秀的架构。
以我做过的异地多活设计方案为例之前很早我就知道CAP理论了但也仅仅只是知道几个概念而已。真正做异地多活的时候开始的时候还是走了不少弯路试图做一个完美的异地多活系统最终发现这其实是不可能的某天突然顿悟其实CAP理论已经明确指出来了这点但最初学习CAP理论的时候很难有这样深刻的理解。
高级架构师
【阶段描述】
成长为高级架构师需要10年以上时间其典型特征是“创造新的架构模式”例如
谷歌大数据论文创造了分布式存储架构、分布式计算MapReduce架构、列式存储架构开创了大数据时代。
在有MapReduce分布式计算架构的背景下Storm又创造了流式计算架构。
在虚拟机很成熟的背景下Docker创造了容器化的技术潮流。
高级架构师与中级架构师相比,典型区别在于“创造性”,高级架构师能够创造新的架构模式,开创新的技术潮流。
【成长指导】
坦白地说,对于从中级架构师如何才能成长为高级架构师,我并没有太好的指导,一个原因是我自我评价目前顶多算个中级架构师;另外一个原因是一旦涉及“创造性”,其实和艺术就比较类似了,创造性实际上是很难学会的,也很难由老师教会,更多是天分,或者某种场景下灵感爆发。
参考技术界几个创造性的架构案例,我总结出几个可能诞生创造性架构的背景条件:
足够复杂的业务场景例如谷歌的大数据、阿里的双十一、Facebook的海量用户等业务场景越复杂给技术带来的挑战更大更有可能产生创造性的技术突破。
足够强大的技术团队:绝大部分创造性的架构都来源于大公司,或者知名的研究机构;没有技术实力支撑,想突破也是心有余而力不足。
不满足于现状的态度例如虚拟机很成熟但是资源占用太多所以发明DockerMapReduce难以做到实时运算所以创造Storm流式运算。
尊重技术价值的文化创造性的东西往往需要投入大量的人力和时间而且刚开始一般都不会很成熟如果完全结果导向、KPI导向创新技术很可能在萌芽阶段就被否定。
总结
关于如何在专业领域内提升有条著名的“10000小时定律”简单来说要成为某个领域顶尖的专业人才需要持续不断10000小时的练习例如小提琴、足球、国际象棋、围棋等领域无一例外都遵循这个定律。我认为技术人员成长也基本遵循这个定律我在文章中试图提炼一条通用的成长路径供你参考但其实最关键的还是技术人员对技术的热情以及持续不断地投入包括学习、实践、思考、总结等。
最后,你可以统计一下自己从头到尾认真读过的技术书籍数量、系统研究过的开源项目的数量,然后自我评估一下自己目前处于哪个层级,看看是否有什么发现?

View File

@@ -0,0 +1,37 @@
因收到Google相关通知网站将会择期关闭。相关通知内容
结束语 坚持,成就你的技术梦想
“从0开始学架构”专栏历经4个月的时间现在到了跟你说再见的时候了。一路走来非常感谢你的坚持。看到专栏的内容能够帮助你理解架构设计这个看起来高大上的技术我非常欣慰也算完成了自己3年前的一个心愿帮助更多同学更快更好地掌握架构设计的技术。
专栏的结束意味着你已经完成整套架构设计方法论的学习,但这不是提升架构设计能力的结束,而是架构设计能力提升的开始。我在专栏[特别放送第4期“架构师成长之路”]中给出了一个完整的架构师成长路线图里面分享了我的一些想法和建议这些方法和技巧都是需要我们投入大量时间和精力的也遵循我提到的“10000小时理论”。10000小时简单计算一下就是10年每年投入1000小时平均每天投入大约3小时这个时间其实不短。对于绝大部分人来说也许理论、方法、技巧都知道但最难的就是“坚持”所以在专栏结束之际聊聊我对坚持的理解。
我想跟你分享的第一个坚持:坚持梦想!
几乎每个技术人员心中都有一个架构师的梦想,毕竟架构师代表了技术路线发展的巅峰。但既然是巅峰,就像登山一样,必然会有一段很长的路,路途中也会有很多的障碍,也肯定会有很多的迷茫,甚至很多时候会感到痛苦……但我希望对技术有热情的同学,当你遇到这些问题的时候,心中一定要坚持自己的梦想,因为所有的这些问题都是正常的,也是必须的。所谓成长,其实就是不断学习、不断踩坑、不断填坑的过程。
回想我自己的成长过程,也曾遇到自己开发的系统上线就回滚,也曾经为了异地多活方案而想破脑袋,当时也觉得很困难,但现在回过头来看,正是经历困难以后自己的收获才最大。而一旦成长为架构师,看到自己亲自设计的系统上线,那种创造的感觉真的是让人感到喜悦和自豪,就像我们千辛万苦登山一样,前面的山路越崎岖,山顶的风景才会越美!
我想跟你分享的第二个坚持:坚持学习!
通过“架构师成长之路”中的描述可以看到,从工程师成长为架构师的过程,其实就是一个不断学习的过程,学基础知识、学理论知识、学业界新的技术、研究开源系统、研究业界实践,既要有技术广度,又要有技术深度……总之就是学无止境。但我想,这就是技术的趣味所在,总是有更好的、更新的、更厉害的东西出来。
当年谷歌发布大数据论文的时候我觉得MapReduce好牛没想到后面Storm流式计算更厉害当我觉得Storm好厉害的时候Flink又创造了更厉害的流式计算架构。虽然我有时也会心里想“求求各位大神别变了学不动了”但当我带着好奇心深入去研究和学习的时候心里不由得感叹其设计和技术确实非常厉害虽然才疏学浅只会说一声“牛X”但其实心里那种感觉就像欣赏一幅优美的画作、观看一部震撼的电影、阅读一本精彩的书籍一样。
当然,坚持学习的一个典型难题就是时间的投入,这个我已经有专门的文章进行阐述,详细可以参考我的文章《大牛养成指南:吃的草够多,你也能成为大牛》。
我想跟你分享的第三个坚持:坚持输出!
输出就是把你所学到的东西,再传授给他人,包括培训、演讲、写博客、写书等,这是一个非常好的提升自己的手段。我相信很多人都有这个感觉,很多东西感觉自己学了也懂了,但一旦跟别人交流有些问题就可能回答不上来,或者一写博客就发现其实还有很多细节没有考虑,需要再重新去研究或者考证。我自己在写专栏的时候也是一样,很多知识点我原来以为自己掌握得很深刻了,但一旦写出来就发现还有很多地方没有考虑到或者没有想清楚。所以,输出是一个非常好的手段,帮助自己更好地去学习和理解。
除此以外输出还能够锻炼自己的表达能力、临场反应能力这些是大多数技术人员比较欠缺但又比较关键的能力。技术人员要想有更好的职业发展不能只是埋头干活也需要跟别人沟通交流而输出是最方便的锻炼方法不需要承担管理职责就可以锻炼任何级别都可以通过输出来锻炼自己的能力不一定需要长篇大论也不一定需要篇篇10W+只要你觉得某个点其他人也可能遇到哪怕写100字的博客也是可以的关键点在于持续地输出。
以上三个“坚持”就是我想跟你分享的除了技术本身以外,技术人员成长的几个关键点。整体来说,从工程师成长为架构师,是个人的一个综合修炼的过程,既需要提升自己的技术能力,也需要修炼自己的综合素质。
坚持,成就技术梦想!与君共勉!