云端开源⾼性能技术架构调研分析报告

并发总是网站架构最大的挑战之一。由于 web 服务的兴起,并发
的数量级在不断增长。热门网站为几十万甚至几百万的同时在线用户
提供服务并不寻常。十年前,并发的主要原因是由于客户端接入速度
慢–用户使用 ADSL 或者拨号商务。现在,并发是由移动终端和新应
用架构所带来,这些应用通常基于持久连接来为客户端提供新闻,微
博,通知等服务。另一个重要的因素就是现代浏览器行为变了,他们
浏览网站的时候会同时打开 4 到 6 个连接来加快页面加载速度。
以 nginx 为例, 从以下几个方面来进行调优:


多路复用


传统基于进程或线程的模型使用单独的进程或线程处理并发连
接,因而会阻塞于网络或 I/O 操作。根据不同的应用,就内存和 CPU
而言,这是非常低效的。派生进程或线程需要准备新的运行环境,包
括在内存上分配堆和栈、生成一个新的运行上下文。创建这些东西还
需要额外的 CPU 时间,而且过度的上下文切换引起的线程抖动最终会
导致性能低下。
Nginx 大量使用多路复用和事件通知,并且给不同的进程分配不同
的任务。数量有限的工作进程( Worker)使用高效的单线程循环处理
连接。每个 worker 进程每秒可以处理数千个并发连接、请求。
nginx 架构图
长连接
HTTP 是建立在 TCP 基础之上的,浏览器是一种 HTTP 软件,它
在与服务器通信时首先需要建立 TCP 连接,这个过程有一定的开销。
如果每次请求后都断掉的话, 太浪费资源。 web 服务器都提供长连接
的方式,所谓长连接就是客户端一次请求完后,不关闭连接,保持一
段时间的连接,下次此客户端再次请求时,不用创建新连接,复用所
保持的连接即可。从理论上,长连接可以免去大量建立和关闭连接的
资源消耗,但同时也有大量连接被占用的代价。因此可以初步判断长
连接比短连接能带来更高的 TPS,更低的 CPU 消耗,更少的 IO,更高
的内存占用。

缓存

构建高性能 web 站点时,抛开基础架构,在应用程序、编码层面
主要要考虑的问题就是缓存的设计,合理的缓存设计可以使提供动态
网页服务的网站性能大幅度提高。
客户端缓存
可以利用客户端浏览器的缓存机制,来减少浏览器对服务端的请
求次数(当然在服务端进行图片等资源合并,并结合 css 图片定位技
术,也可以减少 HTTP 请求),利用好 HTTP 的缓存协商,可以设计
出灵活的客户端缓存方案。在 HTTP 头中下面的内容与缓存协商有
关:
Last-Modified:动态页面通过主动推送该值,暗示浏览器在下次
请求同一个 url 的时候,优先使用 If-Modified-Since 值与服务端进行缓
存协商,如果缓存没有过期,那么服务端可以不用重新计算动态网
页,通过返回 304 通知浏览器。网站的静态资源往往使用这种方法。
但是该方法有一个缺点:有时,文件的最后更改时间虽然改了,但是
内容却没有变,这样无法充分发挥浏览器缓存的能力。
Expires: 通过在 HTTP 头中添加 Expires 标记可以明确的告知浏览
器过期形式,浏览器会彻底减少请求的次数。
Web 服务器缓存
Web 服务器有可能支持基于 url 的缓存(基于 key-value 对),这
类似反向代理缓存。缓存通常可以通过配置存储在内存或磁盘上,在
缓存有效期的问题上, 通常是基于 HTTP 协议中的头部信息判断。但
是使用这样的机制需要注意:
web 服务器还具有缓存文件描述符(类似句柄)的能力,这样可
以减少文件的 open 操作,同样是一种减少系统调用的措施,这对于一
些小文件有些效果,因为文件越小花在 open 上的开销将越来越占有重
要的比例。
应用程序缓存
应用程序本身可以对动态内容进行缓存,这可以体现在三个层
面:
动态脚本缓存:每次脚本解析都需要消耗一定的时间, 为了加快
这个速度,一些服务器端脚本语言都支持动态脚本的预编译,比如我
们熟悉的 ASP.NET、 JSP 都有所谓的中间语言,解析这些中间语言会
快很多。
动态脚本框架支持的缓存:一些动态脚本框架支持缓存,同样在
选型的时候要关注一下这些框架的缓存机制和原理,比如缓存如何存
放,过期如何设置,是否支持局部缓存等。
应用程序自身实现缓存:应用程序根据特定的业务需求独立设计
缓存。
在缓存设计的具体技术上有以下几点:
缓存在内存,这种方式的优点就是减少了磁盘的读写,但是内存
有限,单机不易扩展。
缓存在分布式缓存,这种方式的优点是减少了磁盘读写,并提高
了可靠性和扩展性,但是由于需要网络 IO,性能稍逊于单机缓存。
局部页面缓存,对于一些页面无法完整缓存的,可以考虑局部缓
存。
静态化,将网站静态化可以极大的提高性能,因为用户的请求不
需要动态脚本处理。
Nginx 缓存简介
Nginx 在文件系统上使用分层数据存储实现缓存。缓存主键可配
置,并且可使用不同特定请求参数来控制缓存内容。缓存主键和元数
据存储在共享内存段中,缓存加载进程、缓存管理进程和 worker 进程
都能访问。目前不支持在内存中缓存文件,但可以用操作系统的虚拟
文件系统机制进行优化。每个缓存的响应存储到文件系统上的不同文
件, Nginx 配置指令控制存储的层级(分几级和命名方式)。如果响应
需要缓存到缓存目录,就从 URL 的 MD5 哈希值中获取缓存的路径和
文件名。
将响应内容缓存到磁盘的过程如下:当 nginx 从后端服务器读取
响应时,响应内容先写到缓存目录之外的一个临时文件。 nginx 完成请
求处理后,就将这个临时文件重命名并移到缓存目录。如果用于代理
功能的临时目录位于另外一个文件系统,则临时文件会被拷贝一次,
所以建议将临时目录和缓存目录放到同一个文件系统上。如果需要清
除缓存目录,也可以很安全的删除文件。一些第三方扩展可以远程控
制缓存内容,而且整合这些功能到主发布版的工作已经列入计划。

负载均衡

负载均衡就是将请求分散,这涉及到应当如何设计调度策略,以
让集群发挥最大的性能。当集群中的主机能力相当时应当尽量平均调
度,能力不均时应当能者多劳。随着问题的复杂,要时刻关注调度的
性能,不要让调度成为性能瓶颈。
反向代理负载均衡
反向代理服务器工作在 HTTP 层,类似代理服务器,与普通的代
理服务器不同的是,服务器在代理的后端,而不是客户端在代理的后
端,这类似于 NAT,只是 NAT 工作在网络层。同样是负载均衡,反
向代理服务器强调“转发”而不是“转移”,因为它不仅要转发客户端的
请求,还要转发服务端的响应。
黏滞会话:对于启动 session 保存用户信息,或者后端服务器使用
动态内容缓存的应用,必须将用户在一段会话中的的请求保持在同一
台服务器上。代理服务器一般支持类似的配置。然而,尽量不要使应
用过于本地化,比如可以使用 cookie 保存用户数据,或者分布式
Session 或分布式缓存。
IP 负载均衡
字面上看,便是利用网络层进行请求转发,类似 NAT 网关。然
而,使用网关转发在带宽上可能出现瓶颈,因为出口只有一个,所以
出口的带宽要求较高。
直接路由
直接路由是通过调度器修改数据包的目的 MAC 地址,转发请求
数据包,但是响应数据包可以直接发送给外网的方式。这样做显而易
见的好处就是无需担心网关瓶颈,但是实际的服务器和调度服务器都
需要链接在 WAN 交换机上,并且拥有独立的外网 IP 地址。

数据库扩展

关键:如何更快地读/写数据。
( 1) 建立索引
索引就像是一本书的目录,好的索引可以极大地提升 select 操作
的效率,但会增加 delete/update/insert 的开销。如果你的数据库的读远
多于写,那么索引是非常奏效的。
在哪个字段设置索引:经常出现在 select 语句的 where/order
by/group by 后的字段,都可以考虑设置索引
使用组合索引:如果一条 select 语句的条件过滤中涉及到了多个
field,那可以考虑设置组合索引,组合索引有一个最重要的原则就是
( 2)读写分离
这种方式是指利用数据库的复制或镜像功能,同时在多台数据库
上保存相同的数据,并且将读操作和写操作分开,写操作集中在一台
主数据库上,读操作集中在多台从数据库上,对于读取比写更多的站
点适合使用这种方式。如果不想在应用程序层面维护这种分离映射,
那么可以使用数据库反向代理来自动完成对读写的分离。
( 3)垂直分区 & 水平分区
可以考虑把相对独立的数据表存放在不同的服务器上,然后每一
个都采用读写分离技术,这就是垂直分表,可以进一步将读写压力分
摊到更多的服务器上。
如果垂直分表了还不行,那就考虑将单表进一步拆分(分表),
然后每 n( n>=1)个表部署到独立的服务器上(水平分区)。分表和
水平分区的方法大概有:哈希算法、范围分区、映射关系。
( 4) NoSQL

上一篇:Red Hat集群实现SSH无密码互联


下一篇:分布式开发以及GitHub使用