来源: 《从0开始学架构》(极客时间) ---李运华
技术演进的方向
动力
一个行业的发展,归根到底就是业务的发展。而影响一个企业业务的发展主要有 3 个因素:市场、技术、管理,这三者构成支撑业务发展的铁三角,任何一个因素的不足,都可能导致企业的业务停滞不前。
在这个铁三角中,业务处于三角形的中心,毫不夸张地说,市场、技术、管理都是为了支撑企业业务的发展。
我们可以简单地将企业的业务分为两类:一类是产品类,一类是服务类。
- 对于产品类业务,技术创新推动业务发展!
原因在于用户选择一个产品的根本驱动力在于产品的功能是否能够更好地帮助自己完成任务。用户会自然而然地选择那些功能更加强大、性能更加先进、体验更加顺畅、外观更加漂亮的产品,而功能、性能、体验、外观等都需要强大的技术支撑。
- 对于服务类业务,业务发展推动技术的发展!
用户选择服务的根本驱动力与选择产品不同。用户选择一个产品的根本驱动力是其“功能”,而用户选择一个服务的根本驱动力不是功能,而是“规模”。
模式
论什么模式的业务,如果业务的发展需要技术同步发展进行支撑,无一例外是因为业务“复杂度”的上升,导致原有的技术无法支撑。
复杂度要么来源于功能不断叠加,要么来源于规模扩大,从而对性能和可用性有了更高的要求。既然如此,判断到底是什么复杂度发生了变化就显得至关重要了。
那架构师具体应该按照什么标准来判断呢?答案就是基于业务发展阶段进行判断,这也是为什么架构师必须具备业务理解能力的原因。不同的行业业务发展路径、轨迹、模式不一样,架构师必须能够基于行业发展和企业自身情况做出准确判断。
互联网技术演进模式
联网业务发展一般分为几个时期:初创期、发展期、竞争期、成熟期。同时期的差别主要体现在两个方面:复杂性、用户规模。
业务复杂性
1. 初创期
互联网业务刚开始一般都是一个创新的业务点,这个业务点的重点不在于“完善”,而在于“创新”,只有创新才能吸引用户;而且因为其“新”的特点,其实一开始是不可能很完善的。只有随着越来越多的用户的使用,通过快速迭代试错、用户的反馈等手段,不断地在实践中去完善,才能继续创新。
初创期的业务对技术就一个要求:“快”,但这个时候却又是创业团队最弱小的时期,可能就几个技术人员,所以这个时候十八般武艺都需要用上:能买就买,有开源的就用开源的。
2. 发展期
业务快速发展时期的主要目的是将原来不完善的业务逐渐完善,因此会有越来越多的新功能不断地加入到系统中。对于绝大部分技术团队来说,这个阶段技术的核心工作是快速地实现各种需求,只有这样才能满足业务发展的需要。
一般会经历下面几个阶段:
1. 堆功能期
业务进入快速发展期的初期,此时团队规模也不大,业务需求又很紧,最快实现业务需求的方式是继续在原有的系统里面不断地增加新的功能,重构、优化、架构等方面的工作即使想做,也会受制于人力和业务发展的压力而放在一边。
2. 优化期
核心思想是将现有的系统优化。例如,采用重构、分层、优化某个 MySQL 查询语句,将机械硬盘换成 SSD,将数据库从 MySQL 换成 Oracle,增加 Memcache 缓存等。优势是对系统改动较小,优化可以比较快速地实施;缺点就是可能过不了多久,系统又撑不住了。
3. 架构期
经过优化期后,如果业务能够继续发展,慢慢就会发现优化也顶不住了,毕竟再怎么优化,系统的能力总是有极限的,只能进行架构调整。
架构期可以用的手段很多,但归根结底可以总结为一个字“拆”,什么地方都可以拆。
3. 竞争期
当业务继续发展,已经形成一定规模后,一定会有竞争对手开始加入行业来竞争,当竞争对手加入后,大家互相学习和模仿,业务更加完善,也不断有新的业务创新出来,而且由于竞争的压力,对技术的要求是更上一层楼了。
当系统的数量越来越多就会产生质变,主要体现在下面几个方面:
- 重复造轮子
系统越来越多,各系统相似的工作越来越多。
- 系统交互一团乱麻
系统越来越多,各系统的交互关系变成了网状。系统间的交互数量和系统的数量成平方比的关系。
针对这个时期业务变化带来的问题,技术工作主要的解决手段有:
- 平台化
目的在于解决“重复造轮子”的问题。
- 服务化
目的在于解决“系统交互”的问题,常见的做法是通过消息队列来完成系统间的异步通知,通过服务框架来完成系统间的同步调用。
4. 成熟期
此时技术上该拆的也拆了,该平台化的也平台化了,技术上能做的大动作其实也不多了,更多的是进行优化。但有时候也会为了满足某个优化,系统做很大的改变。
这个时候的技术优化没有固定的套路,只能按照竞争的要求,找出自己的弱项,然后逐项优化。在逐项优化时,可以采取之前各个时期采用的手段。
用户规模
用户量增大对技术的影响主要体现在两个方面:性能要求越来越高、可用性要求越来越高。
架构模板
存储层技术
SQL
随着互联网业务的发展,性能要求越来越高,必然要面对一个问题:将数据拆分到多个数据库实例才能满足业务的性能需求。
数据库拆分满足了性能的要求,但带来了复杂度的问题:数据如何拆分、数据如何组合?这个复杂度的问题解决起来并不容易,如果每个业务都去实现一遍,重复造轮子将导致投入浪费、效率降低,业务开发想快都快不起来。
所以互联网公司流行的做法是业务发展到一定阶段后,就会将这部分功能独立成中间件。
当规模继续扩大后,力雄厚的大公司此时一般都会在 SQL 集群上构建 SQL 存储平台,以对业务透明的形式提供资源分配、数据备份、迁移、容灾、读写分离、分库分表等一系列服务。
NoSQL
由于 NoSQL 方案一般自己本身就提供集群的功能,因此 NoSQL 在刚开始应用时很方便,不像 SQL 分库分表那么复杂。
NoSQL 发展到一定规模后,通常都会在 NoSQL 集群的基础之上再实现统一存储平台,统一存储平台主要实现这几个功能:
- 资源动态按需动态分配
- 资源自动化管理
- 故障自动化处理
小文件存储
除了关系型的业务数据,互联网行业还有很多用于展示的数据,这些数据具有数据小,数据量大,访问量巨大的特点。基本上每个业务都会有这样的数据,为了防止重复造轮子,所以一般会将小文件存储做成统一的和业务无关的平台。
得益于开源运动的发展和最近几年大数据的火爆,在开源方案的基础上封装一个小文件存储平台并不是太难的事情。例如,HBase、Hadoop、Hypertable、FastDFS 等都可以作为小文件存储的底层平台,只需要将这些开源方案再包装一下基本上就可以用了。
大文件存储
说到大文件,特别要提到 Google 和 Yahoo,Google 的 3 篇大数据论文(Bigtable/Map- Reduce/GFS)开启了一个大数据的时代,而 Yahoo 开源的 Hadoop 系列(HDFS、HBase 等),基本上垄断了开源界的大数据处理。
对照 Google 的论文构建一套完整的大数据处理方案的难度和成本实在太高,而且开源方案现在也很成熟了,所以大数据存储和处理这块反而是最简单的,因为你没有太多选择,只能用这几个流行的开源方案,例如,Hadoop、HBase、Storm、Hive 等。实力雄厚一些的大公司会基于这些开源方案,结合自己的业务特点,封装成大数据平台。
开发层技术
开发框架
一般互联网公司都会指定一个大的技术方向,然后使用统一的开发框架。例如,Java 相关的开发框架 SSH、SpringMVC、Play,Ruby 的 Ruby on Rails,PHP 的 ThinkPHP,Python 的 Django 等。
对于框架的选择,有一个总的原则:优选成熟的框架,避免盲目追逐新技术!
Web服务器
独立开发一个成熟的 Web 服务器,成本非常高,况且业界又有那么多成熟的开源 Web 服务器,所以互联网行业基本上都是“拿来主义”,挑选一个流行的开源服务器即可。大一点的公司,可能会在开源服务器的基础上,结合自己的业务特点做二次开发。
选择一个Web服务器主要和开发语言相关,例如,Java 的有 Tomcat、JBoss、Resin 等,PHP/Python 的用 Nginx,当然最保险的就是用 Apache 了,什么语言都支持。
容器
容器以docker为代表,已经在BAT级别的公司有了较多的应用。
- 运维方式会发生革命性的变化:Docker 启动快,几乎不占资源,随时启动和停止,基于 Docker 打造自动化运维、智能化运维将成为主流方式。
- 设计模式会发生本质上的变化:启动一个新的容器实例代价如此低,将鼓励设计思路朝“微服务”的方向发展。
服务层技术
1. 配置中心
将配置中心做成通用的系统的好处有:
- 集中配置多个系统,操作效率高。
- 所有配置都在一个集中的地方,检查方便,协作效率高。
- 配置中心可以实现程序化的规则检查,避免常见的错误。比如说检查最小值、最大值、是否 IP 地址、是否 URL 地址,都可以用正则表达式完成。
- 配置中心相当于备份了系统的配置,当某些情况下需要搭建新的环境时,能够快速搭建环境和恢复业务。
一般以“系统标识 + host + port”来标识唯一一个系统运行实例。
2. 服务中心
服务中心的实现一般来说有两种方式:服务名字系统和服务总线系统。
- 服务名字系统(Service Name System)
服务名字系统是为了将 Service 名称解析为“host + port + 接口名称”,但是和 DNS 一样,真正发起请求的还是请求方。
- 服务总线系统
相比服务名字系统,服务总线系统更进一步了:由总线系统完成调用,服务请求方都不需要直接和服务提供方交互了。
3. 消息队列
传统的异步通知方式是由消息生产者直接调用消息消费者提供的接口进行通知的,但当业务变得庞大,子系统数量增多时,这样做会导致系统间交互非常复杂和难以管理,因为系统间互相依赖和调用,整个系统的结构就像一张蜘蛛网。
消息队列就是为了实现这种跨系统异步通知的中间件系统。消息队列既可以“一对一”通知,也可以“一对多”广播。
对比蜘蛛网架构,可以看到引入消息队列后的效果:
- 整体结构从网状结构变为线性结构,结构清晰
- 消息生产和消息消费解耦,实现简单
- 增加新的消息消费者,消息生产者完全不需要任何改动,扩展方便
- 消息队列系统可以做高可用、高性能,避免各业务子系统各自独立做一套,减轻工作量
- 业务子系统只需要聚焦业务即可,实现简单
消息队列系统基本功能的实现比较简单,但要做到高性能、高可用、消息时序性、消息事务性则比较难。业界已经有很多成熟的开源实现方案,如果要求不高,基本上拿来用即可,例如,RocketMQ、Kafka、ActiveMQ 等。但如果业务对消息的可靠性、时序、事务性要求较高时,则要深入研究这些开源方案,否则很容易踩坑。
网络层技术
负载均衡
- DNS
DNS 是最简单也是最常见的负载均衡方式,一般用来实现地理级别的均衡。
- Nginx 、LVS 、F5
DNS 用于实现地理级别的负载均衡,而 Nginx、LVS、F5 用于同一地点内机器级别的负载均衡。其中 Nginx 是软件的 7 层负载均衡,LVS 是内核的 4 层负载均衡,F5 是硬件的 4 层负载均衡。
CDN
CDN 是为了解决用户网络访问时的“最后一公里”效应,本质上是一种“以空间换时间”的加速策略,即将内容缓存在离用户最近的地方,用户访问的是缓存的内容,而不是站点实时的内容。
CDN 经过多年的发展,已经变成了一个很庞大的体系:分布式存储、全局负载均衡、网络重定向、流量控制等都属于 CDN 的范畴,尤其是在视频、直播等领域,如果没有 CDN,用户是不可能实现流畅观看内容的。
幸运的是,大部分程序员和架构师都不太需要深入理解 CDN 的细节,因为 CDN 作为网络的基础服务,独立搭建的成本巨大,很少有公司自己设计和搭建 CDN 系统,从 CDN 服务商购买 CDN 服务即可。
多机房
多机房设计最核心的因素就是如何处理时延带来的影响,常见的策略有:
- 同城多机房
- 跨城多机房
- 跨国多机房
多中心
多中心必须以多机房为前提,但从设计的角度来看,多中心相比多机房是本质上的飞越,难度也高出一个等级。
简单来说,多机房的主要目标是灾备,当机房故障时,可以比较快速地将业务切换到另外一个机房,这种切换操作允许一定时间的中断(例如,10 分钟、1 个小时),而且业务也可能有损失。因此相比多机房来说,多中心的要求就高多了,要求每个中心都同时对外提供服务,且业务能够自动在多中心之间切换,故障后不需人工干预或者很少的人工干预就能自动恢复。
多中心设计的关键就在于“数据一致性”和“数据事务性”如何保证,这两个难点都和业务紧密相关,目前没有很成熟的且通用的解决方案,需要基于业务的特性进行详细的分析和设计。
用户层技术
1. 用户管理
稍微大一点的互联网业务,肯定会涉及多个子系统,这些子系统不可能每个都管理这么庞大的用户,由此引申出用户管理的第一个目标:单点登录(SSO),又叫统一登录。单点登录的技术实现手段较多,例如 cookie、JSONP、token 等,目前最成熟的开源单点登录方案当属 CAS。
除此之外,当业务做大成为了平台后,开放成为了促进业务进一步发展的手段,需要允许第三方应用接入,由此引申出用户管理的第二个目标:授权登录。现在最流行的授权登录就是 OAuth 2.0 协议,基本上已经成为了事实上的标准,如果要做开放平台,则最好用这个协议,私有协议漏洞多,第三方接入也麻烦。
2. 消息推送
消息推送根据不同的途径,分为短信、邮件、站内信、App 推送。除了 App,不同的途径基本上调用不同的 API 即可完成,技术上没有什么难度。
App 目前主要分为 iOS 和 Android 推送,iOS 系统比较规范和封闭,基本上只能使用苹果的 APNS;但 Android 就不一样了,在国外,用 GCM 和 APNS 差别不大;但是在国内,情况就复杂多了:首先是 GCM 不能用;其次是各个手机厂商都有自己的定制的 Android,消息推送实现也不完全一样。
3. 存储云、图片云
需要用到小文件存储技术。简单来说,存储云和图片云通常的实现都是“CDN + 小文件存储”,现在有了“云”之后,除非 BAT 级别,一般不建议自己再重复造轮子了,直接买云服务可能是最快也是最经济的方式。
普通的文件基本上提供存储和访问就够了,而图片涉及的业务会更多,包括裁剪、压缩、美化、审核、水印等处理,因此通常情况下图片云会拆分为独立的系统对用户提供服务。
业务层技术
抛开业务上的差异,各个互联网业务发展最终面临的问题都是类似的:业务复杂度越来越高。也就是说,业务层面对的主要技术挑战是“复杂度”。
针对业务复杂度的调整,唯一的解决办法就是“拆”,化整为零、分而治之,将整体复杂性分散到多个子业务或者子系统里面去。
随着子系统数量越来越多,如果达到几百上千,另外一个复杂度问题又会凸显出来:子系统数量太多,已经没有人能够说清楚业务的调用流程了,出了问题排查也会特别复杂。此时就要按照“高内聚,低耦合”的原则来进行合了,将职责关联比较强的子系统合成一个虚拟业务域,然后通过网关对外统一呈现,类似于设计模式中的 Facade 模式。
平台层技术
运维平台
运维平台核心的职责分为四大块:配置、部署、监控、应急:
- 配置:主要负责资源的管理。例如,机器管理、IP 地址管理、虚拟机管理等。
- 部署:主要负责将系统发布到线上。例如,包管理、灰度发布管理、回滚等。
- 监控:主要负责收集系统上线运行后的相关数据并进行监控,以便及时发现问题。
- 主要负责系统出故障后的处理。例如,停止程序、下线故障机器、切换 IP 等。
运维平台的核心设计要素是“四化”:标准化、平台化、自动化、可视化。
- 标准化
需要制定运维标准,规范配置管理、部署流程、监控指标、应急能力等,各系统按照运维标准来实现,避免不同的系统不同的处理方式。标准化是运维平台的基础,没有标准化就没有运维平台。
- 平台化
传统的手工运维方式需要投入大量人力,效率低,容易出错,因此需要在运维标准化的基础上,将运维的相关操作都集成到运维平台中,通过运维平台来完成运维工作。
运维平台的好处有:
- 可以将运维标准固化到平台中,无须运维人员死记硬背运维标准。
- 运维平台提供简单方便的操作,相比之下人工操作低效且容易出错。
- 运维平台是可复用的,一套运维平台可以支撑几百上千个业务系统。
- 自动化
传统手工运维方式效率低下的一个主要原因就是要执行大量重复的操作,运维平台可以将这些重复操作固化下来,由系统自动完成。
类似的还有监控,有了运维平台后,运维平台可以实时收集数据并进行初步分析,当发现数据异常时自动发出告警,无须运维人员盯着数据看。
- 可视化
可视化的主要目的就是为了提升数据查看效率。
测试平台
测试平台的核心目的是提升测试效率,从而提升产品质量,其设计关键就是自动化。
测试平台的基本架构如下:
- 用例管理
- 资源管理
- 任务管理
- 数据管理
数据平台
数据平台的核心职责主要包括三部分:数据管理、数据分析和数据应用。
- 数据管理
数据管理包含数据采集、数据存储、数据访问和数据安全四个核心职责,是数据平台的基础功能。
- 数据分析
数据分析包括数据统计、数据挖掘、机器学习、深度学习等几个细分领域。
- 数据应用
数据应用很广泛,既包括在线业务,也包括离线业务。例如,推荐、广告等属于在线应用,报表、欺诈检测、异常检测等属于离线应用。
管理平台
管理平台的核心职责就是权限管理,无论是业务系统、中间件系统,还是平台系统,都需要进行管理。如果每个系统都自己来实现权限管理,效率太低,重复工作很多,因此需要统一的管理平台来管理所有的系统的权限。
权限管理主要分为两部分:身份认证、权限控制。
开源项目
不要重复发明轮子,但要找到合适的轮子
如何选择一个开源项目
- 聚焦是否满足业务
聚焦于是否满足业务,而不需要过于关注开源项目是否优秀。
- 聚焦是否成熟
尽量选择成熟的开源项目,可以从以下方面考察开源项目是否成熟:
- 版本号:除非特殊情况,否则不要选 0.X 版本的,至少选 1.X 版本的,版本号越高越好。
- 使用的公司数量:一般开源项目都会把采用了自己项目的公司列在主页上,公司越大越好,数量越多越好。
- 社区活跃度:看看社区是否活跃,发帖数、回复数、问题处理速度等。
- 聚焦运维能力
如果要将项目应用到线上生产环境,则运维能力是必不可少的一环,否则一旦出问题,运维、研发、测试都只能干瞪眼,求菩萨保佑了!
- 开源项目日志是否齐全
- 开源项目是否有命令行、管理控制台等维护工具,能够看到系统运行时的情况。
- 开源项目是否有故障检测和恢复的能力,例如告警、切换等。
如何使用开源项目
- 深入研究,仔细测试
- 通读开源项目的设计文档或者白皮书,了解其设计原理。
- 核对每个配置项的作用和影响,识别出关键配置项。
- 进行多种场景的性能测试。
- 进行压力测试,连续跑几天,观察 CPU、内存、磁盘 I/O 等指标波动。
- 进行故障测试:kill、断电、拔网线、重启 100 次以上、切换等。
- 小心应用,灰度发布
先在非核心的业务上用,然后有经验后慢慢扩展。
- 做好应急,以防万一
如何基于开源项目做二次开发
- 保持纯洁,加以包装
不要改动原系统,而是要开发辅助系统:监控、报警、负载均衡、管理等。
- 发明你要的轮子
选与不选开源项目,核心还是一个成本和收益的问题,并不是说选择开源项目就一定是最优的项目,最主要的问题是:没有完全适合你的轮子!