软件系统架构演进
软件系统架构演进
1,为什么会出现分布式架构?
阿里巴巴在 2009 年发起了一项"去 IOE"运动;
IOE 指的是 IBM 小型机,Oracle 数据库,EMC 的高端存储 。2009 年“去 IOE”战略透露,到 2013 年 5 月 17 日最后一台IBM小型机在支付宝下线。
为什么要去 IOE?
阿里巴巴过去一直采用的是 Oracle 数据库,并利用小型机和高端存储设备提供高性能的数据处理和存储服务。随着业务的不断发展,数据量和业务量呈爆发性增长,传统的集中式 Oracle 数据库架构在扩展性方面遭遇瓶颈。
传统的商业数据库软件(Oracle,DB2),多以集中式架构为主, 这些传统数据库软件的最大特点就是将所有的数据都集中在一个数据库中,依靠大型高端设备来提供高处理能力和扩展性。
集中式数据库的扩展性主要采用向上扩展(Scale up)的方式, 通过增加 CPU,内存,磁盘等方式提高处理能力。这种集中式数据库的架构,使得数据库成为了整个系统的瓶颈,已经越来越不适应海量数据对计算能力的巨大需求
1.2,分布式系统的意义
- 升级单机处理能力的性价比越来越低 ,单机的处理能力主要依靠 CPU,内存,磁盘。通过更换硬件做垂直扩展的方式来提升性能,成本会越来越高。
- 单机处理能力存在瓶颈,单机处理能力存在瓶颈,CPU,内存都会有自己的性能瓶颈, 也就是说就算你是土豪不惜成本去提升硬件,但是硬件的发展速度和性能是有限制的。
- 稳定性和可用性这两个指标很难达到。单机系统存在可用性和稳定性的问题,这两个指标又是我们必须要去解决的。
1.3,分布式架构中的一些概念
1.3.1,集群
集群:在分布式结构中,可能存在单点故障问题,此时就对服务进行备份提供相同的服务,这样就构成了一个“集群”,集群中每一个服务器都是一个节点;为了让这些节点都能有相同的工作量,不至于累的累死闲的闲死,负载均衡器就起作用了。
1.3.2,分布式
我们再来看下分布式的概念;
分布式系统一定是由多个节点组成的系统。
其中,节点指的是计算机服务器,而且这些节点一般不是孤立的,而是互通的。
这些连通的节点上部署了我们的节点,并且相互的操作会有协同。
分布式系统对于用户而言,他们面对的就是一个服务器,提供用户需要的服务而已,
而实际上这些服务是通过背后的众多服务器组成的一个分布式系统,因此分布式系统看起来像是一个超级计算机一样。
熟悉了集群和分布式概念后,我们再来看下分布式和集群的区别:
集群
- 集群是指在几个服务器上部署相同的应用程序来分担客户端的请求,主要用来解决分散用户的访问请求;
- 它是同一个系统部署在不同的服务器上,比如一个登陆系统部署在不同的服务器上,更加强调服务的副本机制,同一个服务部署多个副本,分布在不同节点上,如果一个节点宕机,其他节点上的相同服务继续提供服务;
- 集群主要的使用场景是为了分担请求的压力。
但是,当压力进一步增大的时候,可能在需要存储的部分,比如mysql无法面对大量的“写压力”。
因为在mysql做成集群之后,主要的写压力还是在master的机器上,其他slave机器无法分担写压力,这时,就引出了“分布式”。
分布式
- 分布式是指多个系统协同合作完成一个特定任务的系统。
- 它是不同的系统功能单元部署在不同的服务器上,服务器之间相互调用,协同完成用户的一个请求;
- 分布式是解决中心化管理的问题,把所有的任务叠加到一个节点处理,太慢了。
- 所以把一个大问题拆分为多个小问题,并分别解决,最终协同合作。
1.3.3,节点
节点是指一个可以独立按照分布式协议完成一组逻辑的程序个体。在具体的项目中,一个节点表示的是一个操作系统上的进程。
1.3.4,副本机制
副本(replica/copy)指在分布式系统中为数据或服务提供的冗余。
数据副本指在不同的节点上持久化同一份数据,当出现某一个节点的数据丢失时,可以从副本上读取到数据。数据副本是分布式系统中解决数据丢失问题的唯一手段。服务副本表示多个节点提供相同的服务,通过主从关系来实 服务的高可用方案。
1.3.5,中间件
中间件位于操作系统提供的服务之外,又不属于应用,他是位于应用和系统层之间为开发者方便的处理通信,输入输出的一 类软件,能够让用户关心自己应用的部分。
2,架构发展过程
一个成熟的大型网站系统架构并不是一开始就设计的非常完 美,也不是一开始就具备高性能,高可用,安全性等特性,而是随着用户量的增加,业务功能的扩展逐步完善演变过来的。 在这个过程中,开发模式,技术架构等都会发生非常大的变化。
而针对不同业务特征的系统,会有各自的侧重点,比如像淘宝 这类的网站,要解决的是海量商品搜索,下单,支付等问题;
像腾讯,要解决的是数亿级别用户的实时消息传输;百度所要解决的是海量数据的搜索。每一个种类的业务都有自己不同的系统架构。我们简单模拟一个架构演变过程。
我们以 javaweb 为例,来搭建一个简单的电商系统,从这个系统中来看系统的演变历史;要注意的是,接下来的演示模型, 关注的是数据量,访问量提升,网站结构发生的变化, 而不是 具体关注业务功能点。其次,这个过程是为了更好的了解网站演进过程中的一些问题和应对策略。 假如我们系统具备以下功能:
- 用户模块:用户注册和管理
- 商品模块:商品展示和管理
- 交易模块:创建交易及支付结算
2.1,单体应用架构
网站的初期也可以认为是互联网发展的早起,我们经常会在单 机上跑我们所有的程序和软件。 把所有软件和应用都部署在一台机器上,这样就完成一个简单系统的搭建,这个时候的讲究的是效率 。
优点: 所有功能集成在一个项目工程中,项目架构简单,前期开发成本低,要快速增加新功能或部署发布都比较简单,小型项目的首选,项目初期是一种比较好的方案。
缺点: 对于大型项目不易扩展。 系统性能受限,系统性能扩展只能通过扩展集群节点的方式,成本高,技术栈受限,有瓶颈。
2.1.1,垂直拆分
,拆分为多个子系统,系统之间通过Webservice或者HTTP接口来进行交互,系统不再那么臃肿了。当其中某一个模块使用的频率比较高,就对这个模块进行扩展,即多部署几个节点。再加一个Nginx用于负载均衡,刚开始还没什么大问题,当子系统越来越多的时候,每个子系统前面都要加一层负载,对运维人员来说工作量就增加了,因为要维护的也增多了。所谓的垂直应用架构,就是将原来的一个应用拆成互不相干的几个应用,以提升效率。比如我们可以将上面电商的单体应用拆分成:
2.2,应用服务器和数据库服务器分离
随着网站的上线,访问量逐步上升,服务器的负载慢慢提高, 在服务器还没有超载的时候,我们应该做好规划,提升网站的 负载能力。假如代码层面的优化已经没办法继续提高,在不提 高单台机器的性能,增加机器是一个比较好的方式,投入产出 比非常高。这个阶段增加机器的主要目的是将web 服务器和 数据库服务器拆分,这样不仅提高了单机的负载能力,也提高了容灾能力
2.3,阶段三:应用服务器集群-应用服务器负载告警,如何让应用服 务器走向集群
随着访问量的继续增加,单台应用服务器已经无法满足需求。 在假设数据库服务器还没有遇到性能问题的时候,我们可以增 加应用服务器,通过应用服务器集群将用户请求分流到各个服务器中,从而继续提升负载能力。此时多台应用服务器之间没有直接的交互,他们都是依赖数据库各自对外提供服务
架构发展到这个阶段,各种问题也会慢慢呈现
- 用户请求由谁来转发到具体的应用服务器
- 用户如果每次访问到的服务器不一样,那么如何维护 session。
2.4,阶段四:数据库压力变大,数据库读写分离
架构演变到这里,并不是终点。上面我们把应用层的性能拉上来了, 但是数据库的负载也在慢慢增大,那么怎么去提高数据库层面的负载呢?有了前面的思路以后,自然会想到增加服务器。但是假如我 们单纯的把数据库一分为二,然后对于后续数据库的请求,分别负 载到两台数据库服务器上,那么一定会造成数据库不统一的问题。 所以我们一般先考虑读写分离的方式
这个架构的变化会带来几个问题
- 主从数据库之间的数据同步 ; 可以使用 mysql 自带的 master-slave 方式实现主从复制;
- 对应数据源的选择 ; 采用第三方数据库中间件,例如 mycat;
2.5,阶段五:使用搜索引擎缓解读库的压力
数据库做读库的话,尝尝对模糊查找效率不是特别好,像电商类的 网站,搜索是非常核心的功能,即便是做了读写分离,这个问题也不能有效解决。那么这个时候就需要引入搜索引擎,使用搜索引擎能够大大提高我们的查询速度,但是同时也会带来一 些附加的问题,比如维护索引的构建。
2.6,阶段六:引入缓存机制缓解数据库的压力
随着访问量的持续增加,逐渐出现许多用户访问统一部分内容的情 况,对于这些热点数据,没必要每次都从数据库去读取,我们可以使用缓存技术,比如 memcache,redis 来作为我们应用层的缓存; 另外在某些场景下,比如我们对用户的某些IP的访问频率做限制, 那这个放内存中又不合适,放数据库又太麻烦,这个时候可以使用 Nosql 的方式比如 mongDB 来代替传统的关系型数据库
2.7,阶段七:数据库的水平/垂直拆分
我们的网站演进的变化过程,交易,商品,用户的数据都还在同一 个数据库中,尽管采取了增加缓存,读写分离的方式,但是随着数 据库的压力持续增加,数据库的瓶颈仍然是个最大的问题。因此我 们可以考虑对数据的垂直拆分和水平拆分。
垂直拆分:把数据库中不同业务数据拆分到不同的数据库
水平拆分:把同一个表中的数据拆分到两个甚至跟多的数据库中, 水平拆分的原因是某些业务数据量已经达到了单个数据库的瓶颈, 这时可以采取讲表拆分到多个数据库中
2.8,阶段八:应用的拆分
随着业务的发展,业务越来越多,应用的压力越来越大。工程规模 也越来越庞大。这个时候就可以考虑讲应用拆分,按照领域模型讲 我们的用户,商品,交易拆分成多个子系统.
这样拆分以后,可能会有一些相同的代码,比如用户操作,在商品和交易都需要查询,所以会导致每个系统都会有用户查询访问相关 操作。这些相同的操作一定是要抽象出来,否则就会是一个坑。所以通过走服务化路线的方式来解决 .
那么服务拆分以后,各个服务之间如何进行远程通信呢?
通过 RPC 技术,比较典型的有:webservice,hessian,http,RMI 等等
前期通过这些技术能够很好的解决各个服务之间通信问题,but, 互联网的发展是持续的,所以架构的演变和优化还在持续。
系统架构的演变,总体上经历了一下几个阶段:
单体应用架构,垂直应用架构,分布式架构,SOA架构,微服务架构
2.9,SOA
2.10,微服务
微服务架构在某种程度上是面向服务的架构SOA继续发展的下一步,它更加强调服务于“彻底拆分”。简单来说就是对单体应用进一步拆分成更小的服务,每个服务都是一个可以独立运行的项目。
优点: 通过服务的原子化拆分,以及微服务的独立打包,部署和升级,小团队的交付周期将缩短,运维成 本也将大幅度下降 微服务遵循单一原则。微服务之间采用Restful等轻量协议传输。
缺点: 微服务过多,服务治理成本高,不利于系统维护。 分布式系统开发的技术成本高(容错,分布式事务等)。
SOA与微服务的关系
SOA( Service Oriented Architecture )“面向服务的架构”:是一种设计方法,其中包含多个服 务,服务之间通过相互依赖最终提供一系列的功能。一个服务通常以独立的形式存在与操作系统进程中。各个服务之间 通过网络调用。
微服务架构:其实和 SOA 架构类似,微服务是在 SOA 上做的升华,微服务架构强调的一个重点是“业务需要彻底的组件化和服务化”,原有的单个业务系统会拆分为多个可以独立开发,设计,运行的小应用。 这些小应用之间通过服务完成交互和集成。
分布式核心知识
分布式中的远程调用
在微服务架构中,通常存在多个服务之间的远程调用的需求。远程调用通常包含两个部分:序列化和通信协议。常见的序列化协议包括json,xml,hession,protobuf,thrift,text,bytes
等,目前主流的远程调用技术有基于HTTP的RESTful接口以及基于TCP的RPC协议。
(1)RESTful接口 REST,即Representational State Transfer的缩写,如果一个架构符合REST原则,就称它为RESTful架构。
资源(Resources)
所谓"资源",就是网络上的一个实体,或者说是网络上的一个具体信息。它可以是一段文本,一张图 片,一首歌曲,一种服务,总之就是一个具体的实在。你可以用一个URI(统一资源定位符)指向它, 每种资源对应一个特定的URI。要获取这个资源,访问它的URI就可以,因此URI就成了每一个资源的地 址或独一无二的识别符。REST的名称"表现层状态转化"中,省略了主语。
"表现层"其实指的是"资 源"(Resources)的"表现层"。
表现层(Representation) "资源"是一种信息实体,它可以有多种外在表现形式。我们把"资源"具体呈现出来的形式,叫做它的"表 现层"(Representation)。比如,文本可以用txt格式表现,也可以用HTML格式,XML格式,JSON格 式表现,甚至可以采用二进制格式;图片可以用JPG格式表现,也可以用PNG格式表现。URI只代表资源 的实体,不代表它的形式。严格地说,有些网址最后的".html"后缀名是不必要的,因为这个后缀名表示 格式,属于"表现层"范畴,而URI应该只代表"资源"的位置。
状态转化(State Transfer) 访问一个网站,就代表了客户端和服务器的一个互动过程。
在这个过程中,势必涉及到数据和状态的变 化。互联网通信协议HTTP协议,是一个无状态协议。这意味着,所有的状态都保存在服务器端。因 此,如果客户端想要操作服务器,必须通过某种手段,让服务器端发生"状态转化"(State Transfer)。 客户端用到的手段,只能是HTTP协议。具体来说,就是HTTP协议里面,四个表示操作方式的动词: GET,POST,PUT,DELETE。它们分别对应四种基本操作:GET用来获取资源,POST用来新建资源 (也可以用于更新资源),PUT用来更新资源,DELETE用来删除资源。
综合上面的解释,我们总结一下什么是RESTful架构: 每一个URI代表一种资源; 客户端和服务器之间,传递这种资源的某种表现层; 客户端通过四个HTTP动词,对服务器端资源进行操作,实现"表现层状态转化"。
(2)RPC协议
RPC(Remote Procedure Call ) 一种进程间通信方式。允许像调用本地服务一样调用远程服务。RPC 框架的主要目标就是让远程服务调用更简单,透明。RPC框架负责屏蔽底层的传输方式(TCP或者 UDP),序列化方式(XML/JSON/二进制)和通信细节。开发人员在使用的时候只需要了解谁在什么 位置提供了什么样的远程服务接口即可,并不需要关心底层通信细节和调用过程。