Tagged: 系统架构

大型网站系统架构的演化

一个成熟的大型网站(如淘宝、京东等)的系统架构并不是开始设计就具备完整的高性能、高可用、安全等特性,
它总是随着用户量的增加,业务功能的扩展逐渐演变完善的,在这个过程中,开发模式、技术架构、设计思想也发生了很大的变化,就连技术人员也从几个人发展到一个部门甚至一条产品线。
所以成熟的系统架构是随业务扩展而完善出来的,并不是一蹴而就;不同业务特征的系统,会有各自的侧重点,
例如淘宝,要解决海量的商品信息的搜索、下单、支付,
例如腾讯,要解决数亿的用户实时消息传输,
例如百度,它要处理海量的搜索请求,他们都有各自的业务特性,系统架构也有所不同。
尽管如此我们也可以从这些不同的网站背景下,找出其中共用的技术,这些技术和手段可以广泛运行在大型网站系统的架构中,下面就通过介绍大型网站系统的演化过程,来认识这些技术和手段。

一、最开始的网站架构

最初的架构,应用程序、数据库、文件都部署在一台服务器上,如图:
云厉的博客

二、应用、数据、文件分离

随着业务的扩展,一台服务器已经不能满足性能需求,故将应用程序、数据库、文件各自部署在独立的服务器上,并且根据服务器的用途配置不同的硬件,达到最佳的性能效果。
云厉的博客

三、利用缓存改善网站性能

在硬件优化性能的同时,同时也通过软件进行性能优化,在大部分的网站系统中,都会利用缓存技术改善系统的性能,使用缓存主要源于热点数据的存在,大部分网站访问都遵循28原则(即80%的访问请求,最终落在20%的数据上),所以我们可以对热点数据进行缓存,减少这些数据的访问路径,提高用户体验。
云厉的博客

缓存实现常见的方式是本地缓存、分布式缓存。当然还有CDN、反向代理等,这个后面再讲。本地缓存,顾名思义是将数据缓存在应用服务器本地,可以存在内存中,也可以存在文件,OSCache就是常用的本地缓存组件。本地缓存的特点是速度快,但因为本地空间有限所以缓存数据量也有限。分布式缓存的特点是,可以缓存海量的数据,并且扩展非常容易,在门户类网站中常常被使用,速度按理没有本地缓存快,常用的分布式缓存是Memcached、Redis。

四、使用集群改善应用服务器性能

应用服务器作为网站的入口,会承担大量的请求,我们往往通过应用服务器集群来分担请求数。应用服务器前面部署负载均衡服务器调度用户请求,根据分发策略将请求分发到多个应用服务器节点。
云厉的博客

常用的负载均衡技术硬件的有F5,价格比较贵,软件的有LVS、Nginx、HAProxy。LVS是四层负载均衡,根据目标地址和端口选择内部服务器,Nginx是七层负载均衡和HAProxy支持四层、七层负载均衡,可以根据报文内容选择内部服务器,因此LVS分发路径优于Nginx和HAProxy,性能要高些,而Nginx和HAProxy则更具配置性,如可以用来做动静分离(根据请求报文特征,选择静态资源服务器还是应用服务器)。

五、数据库读写分离和分库分表

随着用户量的增加,数据库成为最大的瓶颈,改善数据库性能常用的手段是进行读写分离以及分表,读写分离顾名思义就是将数据库分为读库和写库,通过主备功能实现数据同步。分库分表则分为水平切分和垂直切分,水平切换则是对一个数据库特大的表进行拆分,例如用户表。垂直切分则是根据业务不同来切换,如用户业务、商品业务相关的表放在不同的数据库中。
云厉的博客

六、使用CDN和反向代理提高网站性能

假如我们的服务器都部署在成都的机房,对于四川的用户来说访问是较快的,而对于北京的用户访问是较慢的,这是由于四川和北京分别属于电信和联通的不同发达地区,北京用户访问需要通过互联路由器经过较长的路径才能访问到成都的服务器,返回路径也一样,所以数据传输时间比较长。对于这种情况,常常使用CDN解决,CDN将数据内容缓存到运营商的机房,用户访问时先从最近的运营商获取数据,这样大大减少了网络访问的路径。比较专业的CDN运营商有蓝汛、网宿。

而反向代理,则是部署在网站的机房,当用户请求达到时首先访问反向代理服务器,反向代理服务器将缓存的数据返回给用户,如果没有没有缓存数据才会继续走应用服务器获取,也减少了获取数据的成本。反向代理有Squid,Nginx。
云厉的博客

七、使用分布式文件系统

用户一天天增加,业务量越来越大,产生的文件越来越多,单台的文件服务器已经不能满足需求。需要分布式的文件系统支撑。常用的分布式文件系统有NFS。
云厉的博客

八、使用NoSql和搜索引擎

对于海量数据的查询,我们使用nosql数据库加上搜索引擎可以达到更好的性能。并不是所有的数据都要放在关系型数据中。常用的NOSQL有mongodb和redis,搜索引擎有lucene。
云厉的博客

九、将应用服务器进行业务拆分

随着业务进一步扩展,应用程序变得非常臃肿,这时我们需要将应用程序进行业务拆分,如百度分为新闻、网页、图片等业务。每个业务应用负责相对独立的业务运作。业务之间通过消息进行通信或者同享数据库来实现。
云厉的博客

十、搭建分布式服务

这时我们发现各个业务应用都会使用到一些基本的业务服务,例如用户服务、订单服务、支付服务、安全服务,这些服务是支撑各业务应用的基本要素。我们将这些服务抽取出来利用分部式服务框架搭建分布式服务。淘宝的Dubbo是一个不错的选择。
云厉的博客

文章来源:http://www.cnblogs.com/leefreeman/p/3993449.html

RESTful 是什么?一起来理解 RESTful 架构

传统上,软件和网络是两个不同的领域,很少有交集;软件开发主要针对单机环境,网络则主要研究系统之间的通信。
互联网的兴起,使得这两个领域开始融合,即 "互联网软件",比网站、网络游戏、各种非单机版APP等,
这种"互联网软件"采用客户端/服务器(C/S)模式,建立在分布式体系上,通过互联网通信,具有高延时(high latency)、高并发等特点。

那么如何开发在互联网环境中使用的软件呢?

云厉的博客

RESTful架构,就是目前非常流行的一种互联网软件架构。
它结构清晰、符合标准、易于理解、扩展方便,所以正得到越来越多网站的采用。
但是,到底什么是RESTful架构,并不是一个容易说清楚的问题。下面,我就谈谈我理解的RESTful架构。

一、起源

REST 这个词,是Roy Thomas Fielding在他2000年的博士论文中提出的。

Fielding是一个非常重要的人,他是HTTP协议(1.0版和1.1版)的主要设计者、Apache服务器软件的作者之一、Apache基金会的第一任主席。
所以,他的这篇论文一经发表,就引起了关注,并且立即对互联网开发产生了深远的影响。
他这样介绍论文的写作目的:

本文研究计算机科学两大前沿----软件和网络----的交叉点。长期以来,软件研究主要关注软件设计的分类、设计方法的演化,很少客观地评估不同的设计选择对系统行为的影响。而相反地,网络研究主要关注系统之间通信行为的细节、如何改进特定通信机制的表现,常常忽视了一个事实,那就是改变应用程序的互动风格比改变互动协议,对整体表现有更大的影响。我这篇文章的写作目的,就是想在符合架构原理的前提下,理解和评估以网络为基础的应用软件的架构设计,得到一个功能强、性能好、适宜通信的架构。
(This dissertation explores a junction on the frontiers of two research disciplines in computer science: software and networking. Software research has long been concerned with the categorization of software designs and the development of design methodologies, but has rarely been able to objectively evaluate the impact of various design choices on system behavior. Networking research, in contrast, is focused on the details of generic communication behavior between systems and improving the performance of particular communication techniques, often ignoring the fact that changing the interaction style of an application can have more impact on performance than the communication protocols used for that interaction. My work is motivated by the desire to understand and evaluate the architectural design of network-based application software through principled use of architectural constraints, thereby obtaining the functional, performance, and social properties desired of an architecture. )

二、名称

Fielding将他对互联网软件的架构原则,定名为REST,即Representational State Transfer的缩写。我对这个词组的翻译是"表现层状态转化"。
如果一个架构符合REST原则,就称它为RESTful架构。
要理解RESTful架构,最好的方法就是去理解Representational State Transfer这个词组到底是什么意思,它的每一个词代表了什么涵义。
如果你把这个名称搞懂了,也就不难体会REST是一种什么样的设计。

三、资源(Resources)

REST的名称"表现层状态转化"中,省略了主语。"表现层"其实指的是"资源"(Resources)的"表现层"。
所谓"资源",就是网络上的一个实体,或者说是网络上的一个具体信息。它可以是一段文本、一张图片、一首歌曲、一种服务,总之就是一个具体的实在。
你可以用一个URI(统一资源定位符)指向它,每种资源对应一个特定的URI。
要获取这个资源,访问它的URI就可以,因此URI就成了每一个资源的地址或独一无二的识别符。
所谓"上网",就是与互联网上一系列的"资源"互动,调用它的URI。

四、表现层(Representation)

"资源"是一种信息实体,它可以有多种外在表现形式。我们把"资源"具体呈现出来的形式,叫做它的"表现层"(Representation)。
比如,文本可以用txt格式表现,也可以用HTML格式、XML格式、JSON格式表现,甚至可以采用二进制格式;图片可以用JPG格式表现,也可以用PNG格式表现。
URI只代表资源的实体,不代表它的形式。严格地说,有些网址最后的".html"后缀名是不必要的,因为这个后缀名表示格式,属于"表现层"范畴,而URI应该只代表"资源"的位置。它的具体表现形式,应该在HTTP请求的头信息中用Accept和Content-Type字段指定,这两个字段才是对"表现层"的描述。

五、状态转化(State Transfer)

访问一个网站,就代表了客户端和服务器的一个互动过程。在这个过程中,势必涉及到数据和状态的变化。
互联网通信协议HTTP协议,是一个无状态协议。这意味着,所有的状态都保存在服务器端。因此,如果客户端想要操作服务器,必须通过某种手段,让服务器端发生"状态转化"(State Transfer)。而这种转化是建立在表现层之上的,所以就是"表现层状态转化"。
客户端用到的手段,只能是HTTP协议。
具体来说,就是HTTP协议里面,四个表示操作方式的动词:GET、POST、PUT、DELETE。
它们分别对应四种基本操作:GET用来获取资源,POST用来新建资源(也可以用于更新资源),PUT用来更新资源,DELETE用来删除资源。

六、综述

综合上面的解释,我们总结一下什么是RESTful架构:

  • 每一个URI代表一种资源;
  • 客户端和服务器之间,传递这种资源的某种表现层;
  • 客户端通过四个HTTP动词,对服务器端资源进行操作,实现"表现层状态转化"。

七、误区

RESTful架构有一些典型的设计误区。

最常见的一种设计错误,就是URI包含动词。因为"资源"表示一种实体,所以应该是名词,URI不应该有动词,动词应该放在HTTP协议中。
举例来说,某个URI是/posts/show/1,其中show是动词,这个URI就设计不理想,正确的写法应该是/posts/1,然后用GET方法表示show。
如果某些动作是HTTP动词表示不了的,你就应该把动作做成一种资源。比如网上汇款,从账户1向账户2汇款500元,错误的URI是:

POST /accounts/1/transfer/500/to/2

正确的写法是把动词transfer改成名词transaction,资源不能是动词,但是可以是一种服务:

POST /transaction HTTP/1.1
Host: 127.0.0.1
from=1&to=2&amount=500.00

另一个设计误区,就是在URI中加入版本号:

http://www.example.com/app/1.0/foo
http://www.example.com/app/1.1/foo
http://www.example.com/app/2.0/foo

因为不同的版本,可以理解成同一种资源的不同表现形式,所以应该采用同一个URI。
版本号可以在HTTP请求头信息的Accept字段中进行区分

Accept: vnd.example-com.foo+json; version=1.0
Accept: vnd.example-com.foo+json; version=1.1
Accept: vnd.example-com.foo+json; version=2.0

MySQL在大型网站的应用架构演变

架构的可扩展性往往和并发是息息相关,没有并发的增长,也就没有必要做高可扩展性的架构,这里对可扩展性进行简单介绍一下,常用的扩展手段有以下两种:
- Scale-up: 纵向扩展,通过替换为更好的机器和资源来实现伸缩,提升服务能力
- Scale-out: 横向扩展, 通过加节点(机器)来实现伸缩,提升服务能力

对于互联网的高并发应用来说,无疑Scale out才是出路,通过纵向的买更高端的机器一直是我们所避讳的问题,也不是长久之计,
在scale out的理论下,可扩展性的理想状态是什么?

可扩展性的理想状态

一个服务,当面临更高的并发的时候,能够通过简单增加机器来提升服务支撑的并发度,
且增加机器过程中对线上服务无影响(no down time),这就是可扩展性的理想状态!

V1.0 简单网站架构

一个简单的小型网站或者应用背后的架构可以非常简单, 数据存储只需要一个MySQL instance就能满足数据读取和写入需求(这里忽略掉了数据备份的实例),处于这个时间段的网站,一般会把所有的信息存到一个database instance里面。
云厉的博客

在这样的架构下,我们来看看数据存储的瓶颈是什么?
- 数据量的总大小 一个机器放不下时
- 数据的索引(B+ Tree)一个机器的内存放不下时
- 访问量(读写混合) 一个实例不能承受时

只有当以上3件事情任何一件或多件满足时,我们才需要考虑往下一级演变。 从此我们可以看出,事实上对于很多小公司小应用,这种架构已经足够满足他们的需求了,初期数据量的准确评估是杜绝过度设计很重要的一环,毕竟没有人愿意为不可能发生的事情而浪费自己的精力。

例如:对于用户信息这类表 (3个索引),16G内存大概能放下2000W行数据的索引,简单的读和写混合访问量3000/s左右没有问题,你的应用场景是否?

V2.0 垂直拆分

一般当V1.0 遇到瓶颈时,首先最简便的拆分方法就是垂直拆分。
何谓垂直?就是从业务角度来看,将关联性不强的数据拆分到不同的instance上,从而达到消除瓶颈的目标。
以图中的为例,将用户信息数据,和业务数据拆分到不同的三个实例上。对于重复读类型比较多的场景,我们还可以加一层cache,来减少对DB的压力。

云厉的博客

在这样的架构下,我们来看看数据存储的瓶颈是什么?
单实例单业务 依然存在V1.0所述瓶颈
遇到瓶颈时可以考虑往本文更高V版本升级, 若是读请求导致达到性能瓶颈可以考虑往V3.0升级, 其他瓶颈考虑往V4.0升级

V3.0 主从架构

此类架构主要解决V2.0架构下的读问题,通过给Instance挂数据实时备份的思路来迁移读取的压力,在MySQL的场景下就是通过主从结构,主库抗写压力,通过从库来分担读压力,对于写少读多的应用,V3.0主从架构完全能够胜任
云厉的博客

在这样的架构下,我们来看看数据存储的瓶颈是什么?
写入量主库不能承受

V4.0 水平拆分

对于V2.0 V3.0方案遇到瓶颈时,都可以通过水平拆分来解决,水平拆分和垂直拆分有较大区别,垂直拆分拆完的结果,在一个实例上是拥有全量数据的,而水平拆分之后,任何实例都只有全量的1/n的数据。
以下图Userinfo的拆分为例,将userinfo拆分为3个cluster,每个cluster持有总量的1/3数据,3个cluster数据的总和等于一份完整数据
注:这里不再叫单个实例 而是叫一个cluster 代表包含主从的一个小MySQL集群
云厉的博客
数据如何路由?

1)Range拆分
sharding key按连续区间段路由,一般用在有严格自增ID需求的场景上,
如Userid, Userid Range的小例子:以userid 3000W 为Range进行拆分:
1号cluster userid 1-3000W
2号cluster userid 3001W-6000W

2)List拆分
List拆分与Range拆分思路一样,都是通过给不同的sharding key来路由到不同的cluster,
但是具体方法有些不同,List主要用来做sharding key不是连续区间的序列落到一个cluster的情况,如以下场景:

假定有20个音像店,分布在4个有经销权的地区,如下表所示:

地区 商店ID
北区 3, 5, 6, 9, 17
东区 1, 2, 10, 11, 19, 20
西区 4, 12, 13, 14, 18
中心区 7, 8, 15, 16

业务希望能够把一个地区的所有数据组织到一起来搜索,这种场景List拆分可以轻松搞定

3)Hash拆分
通过对sharding key 进行哈希的方式来进行拆分,常用的哈希方法有除余,字符串哈希等等。
除余如按userid%n 的值来决定数据读写哪个cluster,其他哈希类算法这里就不细展开讲了。

数据拆分后引入的问题?

数据水平拆分引入的问题主要是只能通过sharding key来读写操作,
例如以userid为sharding key的切分例子,读userid的详细信息时,一定需要先知道userid,这样才能推算出再哪个cluster进而进行查询,
假设我需要按username进行检索用户信息,需要引入额外的反向索引机制(类似HBASE二级索引),
如在redis上存储username->userid的映射,以username查询的例子变成了先通过查询username->userid,再通过userid查询相应的信息。
实际上这个做法很简单,但是我们不要忽略了一个额外的隐患,那就是数据不一致的隐患。
存储在redis里的username->userid和存储在MySQL里的userid->username必须需要是一致的,这个保证起来很多时候是一件比较困难的事情,
举个例子来说,对于修改用户名这个场景,你需要同时修改redis和MySQL,这两个东西是很难做到事务保证的,
如MySQL操作成功 但是redis却操作失败了(分布式事务引入成本较高),
对于互联网应用来说,可用性是最重要的,一致性是其次,所以能够容忍小量的不一致出现。
毕竟从占比来说,这类的不一致的比例可以微乎其微到忽略不计(一般写更新也会采用mq来保证直到成功为止才停止重试操作)

在这样的架构下,我们来看看数据存储的瓶颈是什么?
在这个拆分理念上搭建起来的架构,理论上不存在瓶颈(sharding key能确保各cluster流量相对均衡的前提下),不过确有一件恶心的事情,那就是cluster扩容的时候重做数据的成本,如我原来有3个cluster,但是现在我的数据增长比较快,我需要6个cluster,那么我们需要将每个cluster 一拆为二,一般的做法是
- 摘下一个slave,停同步,
- 对写记录增量log(实现上可以业务方对写操作 多一次写持久化mq 或者MySQL主创建trigger记录写 等等方式)
- 开始对静态slave做数据, 一拆为二
- 回放增量写入,直到追上的所有增量,与原cluster基本保持同步
- 写入切换,由原3 cluster 切换为6cluster

有没有类似飞机空中加油的感觉,这是一个脏活,累活,容易出问题的活,
为了避免这个,我们一般在最开始的时候,设计足够多的sharding cluster来防止可能的cluster扩容这件事情

V5.0 云计算(云数据库)

云计算现在是各大IT公司内部作为节约成本的一个突破口,
对于数据存储的MySQL来说,如何让其成为一个SaaS(Software as a Service)是关键点。
在MS的官方文档中,把构建一个足够成熟的SaaS(MS简单列出了SaaS应用的4级成熟度)所面临的3个主要挑战:
可配置性,可扩展性,多用户存储结构设计称为"three headed monster"
可配置性和多用户存储结构设计在MySQL SaaS这个问题中并不是特别难办的一件事情,所以这里重点说一下可扩展性。

MySQL作为一个SaaS服务,在架构演变为V4.0之后,依赖良好的sharding key设计, 已经不再存在扩展性问题,只是他在面对扩容缩容时,有一些脏活需要干,而作为SaaS,并不能避免扩容缩容这个问题!
所以只要能把V4.0的脏活变成:
- 扩容缩容对前端APP透明(业务代码不需要任何改动)
- 扩容缩容全自动化且对在线服务无影响

那么他就拿到了作为SaaS的门票
云厉的博客

对于架构实现的关键点,需要满足对业务透明,扩容缩容对业务不需要任何改动,那么就必须 “eat our own dog food”,
在你MySQL SaaS内部解决这个问题,一般的做法是我们需要引入一个Proxy,Proxy来解析sql协议,
按sharding key 来寻找cluster, 判断是读操作还是写操作来请求主 或者 从,这一切内部的细节都由proxy来屏蔽。
这里借淘宝的图来列举一下proxy需要干哪些事情
云厉的博客

百度公开的技术方案中也有类似的解决方案:
数据库大会百度Dbproxy中间层架构概述

对于架构实现的关键点,扩容缩容全自动化且对在线服务无影响; 扩容缩容对应到的数据操作即为数据拆分和数据合并,要做到完全自动化有非常多不同的实现方式,总体思路和V4.0介绍的瓶颈部分有关,目前来看这个问题比较好的方案就是实现一个伪装slave的sync slave, 解析MySQL同步协议,然后实现数据拆分逻辑,把全量数据进行拆分。具体架构见下图:

云厉的博客

其中Sync slave对于Original Master来说,和一个普通的MySQL Slave没有任何区别,也不需要任何额外的区分对待。需要扩容/缩容时,挂上一个Sync slave,开始全量同步+增量同步,等待一段时间追数据。以扩容为例,若扩容后的服务和扩容前数据已经基本同步了,这时候如何做到切换对业务无影响? 其实关键点还是在引入的proxy,这个问题转换为了如何让proxy做热切换后端的问题。这已经变成一个非常好处理的问题了.

作者:大熊
原文:http://www.cnblogs.com/Creator

微信公众号:程序员到架构师

最新文章

Return Top