真正有资格谈Web高性能的,一定是具有丰富实战经验、经过很多次高并发压力考验的架构师。到目前为止我没有这种经验,个人作品访问量几近于零,接外包做的网站访问压力也不大。虽然没有机会披甲上阵,但纸上谈兵总是可以的吧!国内有2本非常不错的Web架构技术书籍,一本是来自阿里巴巴技术专家李智慧的《 大型网站技术架构 》,另一本则是监控宝CTO郭欣写的《 构建高性能Web站点 》。
《大型网站技术架构》所涉及的范围非常广,从5个方面(高性能、高可用、伸缩性、可扩展、安全)谈到了大型网站的架构。高屋建瓴,一目了然,唯一的缺点就是细节不够。而《构建高性能Web站点》则集中精力谈高性能,给出了很多实战操作示例。总的来说,这2本书非常适合搭配着看。
高性能可以分别对Web前端和Web后端来谈,本文是在我草草翻了翻这2本书后,针对如何提高Web后端性能做的一点读书笔记。
性能评价
要知道Web系统的性能怎么样,首先必须要有评价指标和测试方法。
(1)评价指标
一般有下面几个:
- 吞吐率:单位时间内服务器处理的请求次数(requests per second)
- 用户平均请求等待时间:每个用户发起一次请求->得到回应的耗时
- 服务器平均请求处理时间:服务器处理一条请求的耗时
- 系统负载:一般包括CPU使用率、CPU Load、内存使用情况、磁盘I/O、网络I/O等
这里稍微提一下CPU Load,它是指一段时间内CPU正在处理以及等待CPU处理的进程数之和的统计信息。如果服务器是8核,若Load=1,表示只有1个任务正在被处理,此时负载很小;Load=8,表示每个核都在处理一个任务,满负载;Load>8,表示需要处理的任务太多,CPU已经超负荷工作啦!可以用top命令查看CPU Load。
(2)测试方法
如何得到上面的指标呢?可以用Apache2附带的工具ab发起测试请求,用法很简单:
ab -n10000 -c10 http://localhost:5000
-n10000
表示总共发起10000次请求, -c10
表示并发用户数为10,也就是说同一时刻有10个用户访问了网站 http://localhost:5000
,一共访问了10000次(假设每个用户访问网站时仅发起1次请求)。执行完毕后就有blabla一大堆数据出来。
引起评价指标波动的关键因素在于 并发数 ,所以一般会在不同并发数下发起测试,得到指标数据。然后以每个评价指标作为y轴,并发数作为x轴进行描点绘图。一般y=f(x)有一个最大值,这个最大值就是系统能够承受的最大并发数了,高于此并发,系统性能会急剧下降,直至崩溃。下面是吞吐量随并发数的变化曲线示例:
Web系统模型
Web系统一般是由不同组件构成的,必须着眼不同的组件来考虑性能优化。以最简单的Web架构来说吧,如图:
从左往右看:用户发出request,服务器收到后派发给App(就是指网站程序,一般基于某Web开发框架),App解析request,然后根据业务逻辑可能会访问数据库、读写文件等等,把信息整合为response交给Server,Server再传回用户。
用一个隐喻说明上面的关系:Server就像Boss,负责接活/将产品交付用户;App是干活的码农,但他在干活中可能还需要测试部门、安全部门等的支持。很简单吧!下面针对不同组件来分析性能优化方法。
Server性能优化
关键:如何提升网络I/O吞吐率。
(1)并发策略
要想同一时间处理更多请求,自然要考虑并发了。就我的理解来看,并发策略中主要有2个自由度:I/O模型、执行模型。
在这里,I/O模型主要指服务器如何处理网络I/O。《UNIX网络编程》6.2节对此有非常精彩的描述,一般有5种:阻塞、非阻塞、I/O多路复用、信号驱动、异步。前四种都属于同步I/O,就是说用户进程在I/O处理中存在阻塞,而异步I/O则是在底层完成I/O处理后再通过信号机制通知用户进程。
执行模型的话,一般有这些:单进程/线程、多进程/线程(不考虑协程的话)。多进程/线程一般比单进程/线程性能要好,这样可以充分利用多核的优势。
与I/O模型组合起来说就是:到底是用哪一种执行模型,来运行哪一种I/O模型。举一些例子:NodeJS是单线程+异步I/O、Gunicorn是多进程+同步I/O等等。
完全追求I/O性能的话,多线程/进程+异步I/O应该是最牛的吧,比如用 pm2 开启cluster 模式,使用多进程在多核机器上跑NodeJS,性能有多强悍,还没有做过测试...
(2)集群与负载均衡
一个扛不住?那就加更多的服务器一起上吧!还需要使用某种负载均衡技术将网络请求均衡地分配到各个机器上,一般有:HTTP重定向、DNS轮询、反向代理、IP负载均衡、数据链路层负载均衡。均衡算法一般有:轮询、加权轮询、随机、最少连接、源地址散列等。以上这些在《大型网站技术架构》中都有精彩的阐述,就不展开说明了。
值得一提的是,集群和负载均衡是一种非常通用的技术,可以用于很多Web组件中。
(3)持久链接
HTTP是建立在TCP基础之上的,浏览器是一种HTTP软件,它在与服务器通信时首先需要建立TCP连接,这个过程有一定的开销。如果每次请求后都断掉的话,那是不是太浪费资源了?好在现代浏览器和服务器一般都支持长连接(Keep-Alive)技术。服务器在开启Keep-Alive后,会在HTTP应答中加上Connection: Keep-Alive
这个header,然后浏览器就知道需要长时间保持连接不关掉啦!
App性能优化
关键:在接收到request后,如何更快地做出response。
(1)执行速度
不同语言的运行速度会相当不一样,但是运行速度和开发效率是需要相互权衡的。让我用汇编去写Web,打死都不愿意!一般根据业务需求、团队喜好来选择,Java/Python/NodeJS/Go/PHP/Ruby都不错。选定后,可以采用某些措施加速代码的运行,比如使用更好的编译器/解释器、使用C/C++编译的第三方库等等。
(2)算法与数据结构
如果业务逻辑中涉及到一定复杂度的运算,那就可以考虑从算法和数据结构层面进行优化。这个可能对单次request效果不明显,但如果访问量特别大的话,微小的性能提升也会被放大很多倍哦!
(3)I/O模型
你可能比较好奇,不是在Server中已经提了I/O模型了嘛,怎么这里又来一个?请注意,Server中的I/O一般指网络I/O,具体来说是对TCP Socket的读写。而在App代码中也会和各种I/O设备打交道,举例:
- 磁盘I/O:读写文件
- 网络I/O:发起OAuth请求
- 数据库I/O:读写MySQL
- 内存I/O:读写Redis
内存I/O等待一般是可以忽略的(除非是分布式缓存,或者某块内存忽然坏掉-_-),其他I/O则不容忽视。那么从I/O请求到读写完毕之间,到底该怎么处理?一般的Web开发框架都是同步I/O模型,但是也有采用异步模型的,比如NodeJS、Tornado。在App中采用异步模型,必须采用支持异步调用的库。NodeJS中的File、HTTP、Net、Stream等module都是为Node量身打造的,完全异步。Tornado也可以使用到很多第三方库的 异步版 。值得一提的是,在App层面采用的异步I/O库,一般都是在Server的异步I/O机制的基础上打造的:Node自然不用说了,都是基于Node的事件机制;Tornado的异步库也都是基于 tornado.ioloop
的。
天下没有免费的午餐,由于执行模式是异步的,会给编程和调试带来巨大挑战。我们需要采取额外的措施来提升开发体验,事件机制、Promise等都是NodeJS中比较流行的应对方法。
BTW,在App层面采用异步I/O模型,只能解决I/O性能问题。如果业务逻辑是偏CPU运算密集型,提升效果就不明显啦!
(4)缓存
缓存真的是万金油。哪里卡哪里抹一点,从前端到后端,能抹的地方太多啦。对于App来说,下面是几种典型的应用场景:
- 缓存Session
- 缓存HTML页面:比如访问压力较大的首页
- 数据库读缓存/写缓存:读缓存很常见,这里提一下写缓存,有时候我们没有必要太频繁地写数据库,可以将写操作直接在缓存上做,然后累积到一定次数再一起写入数据库中
- 缓存复杂运算结果:比如把计算得出的当前热门话题缓存起来
一般使用Redis/Memcached来做缓存。
(5)计算外包
为了保证较高的请求吞吐率,业务逻辑代码中不适合出现特别耗时的操作(比如图像处理、群发邮件等),这个时候就可以把这些耗时的计算过程从App中外包出去,交给独立的计算模块完成。计算外包(也就是分布式计算啦)可以有2种策略:异步计算、并行计算。
异步计算 是将计算任务转移出去的一种方法,这种架构中一般会有3种角色:生产者、任务队列、消费者。App在这里充当了生产者的角色,不断地将新的计算任务插入队列中;可以用Redis、MySQL、第三方消息队列(比如RabbitMQ)等存储计算任务;消费者就是真正执行计算的了,一般会开启多个后台worker,不断地从队列中取出任务来执行。
举个例子,GitHub仓库的语言构成的计算是比较耗时的,每一次commit都执行计算是非常划不来的,所以GitHub采用了异步计算,每周以一定频率计算并更新各仓库的语言构成。
之前的异步计算只是把计算任务转移,但并没有减少总体的处理时间。怎么办?能不能把计算任务先分割为小块儿,然后一起算,算好了再汇总呢?这就是 并行计算 的原理啦!这里不得不提一下Google的Map/Reduce分布式并行计算框架。
简单地说,App这边负责拆分计算任务,然后会有多个worker对每一个计算任务都并发调用Map函数来运算,运算结果根据某参照(比如城市编号、班级号)存放在不同的结果集中。所有的Map都运算完毕后,再由worker针对每一个Map结果集都并发调用Reduce函数进行汇总计算,得出最终结果。这篇 文章 还不错,可以说明Map/Reduce的大概原理。
(6)资源复用
之前说过,既然Server可以复用与客户端的TCP连接,那么App能不能复用与各种I/O设备的连接呢?当然可以!比如MySQL、Redis等都可以用线程池的形式存放连接,减少开销。
数据库性能优化
关键:如何更快地读/写数据。
这里的数据库特指MySQL,在介绍数据库性能优化方法之前,提几个用于性能分析的MySQL命令行工具:
-
mysqlreport
:第三方MySQL状态报告工具,分析结果一目了然 -
explain
:用于分析SQL语句的执行细节(比如是否用到了索引) -
mysqlsla
:用于查询哪些SQL操作的耗时超过了预设的阈值,使用此工具之前需要在my.cnf
开启慢查询日志,即增加long_query_time=1
和log-slow-queries = /data/var/mysql_slow.log
这2行配置项
(1)索引
索引就像是一本书的目录,好的索引可以极大地提升select操作的效率,但会增加delete/update/insert的开销。如果你的数据库的读远多于写,那么索引是非常奏效的。
- 在哪个字段设置索引:经常出现在select语句的where/order by/group by后的字段,都可以考虑设置索引
- 使用组合索引:如果一条select语句的条件过滤中涉及到了多个field,那可以考虑设置组合索引,组合索引有一个最重要的原则就是 最左前缀 ,比如
where A=1 and C=2
是无法使用到A, B, C
组合索引的 - 判断一个SQL操作是否用到索引,可以使用explain工具
(2)冗余设计
数据库的表设计一般遵循所谓的第三范式(3NF),即要求非主键字段之间不能存在依赖关系。但是如果完全按照这样来做的话,SQL语句中会包含大量的join操作。在设计时我们可以保留适量的冗余,比如一个user的blogs数目,可以直接在user表中增加一个blogs_count字段,每次增加/删除blog时就相应地+/-此字段。以后查询起来就快了。
(3)读写分离
单个数据库读写扛不住?那就把读写分离开吧,使用MySQL的主从复制功能,多个从数据库保持与主数据库的同步,然后update/delete/insert全部走主数据库,select则使用负载均衡技术分摊到多个从数据库上。
(4)垂直分区 & 水平分区
读写分离了还是不行,怎么办?可以考虑把相对独立的数据表存放在不同的服务器上,然后每一个都采用读写分离技术,这就是垂直分表,可以进一步将读写压力分摊到更多的服务器上。
如果垂直分表了还不行,那就考虑将单表进一步拆分(分表),然后每n(n>=1)个表部署到独立的服务器上(水平分区)。分表和水平分区的方法大概有:哈希算法、范围分区、映射关系,具体见《构建高性能Web站点》P370页。
(5)NoSQL
如果以上的方法还不奏效,或者虽然奏效但维护成本太高,那就可以考虑抛弃关系数据库,转投NoSQL阵营了。需要注意的是,关系数据库和NoSQL都有各自的适用场景,谁也无法完全取代谁。但NoSQL实在是太多啦,如何选择呢?这里推荐一些比较不错的博文:Robbin的 文章 对NoSQL有一个概览式的分类,草屋主人的NoSQL系列文章 涉及到了各类应用场景,值得一读。
文件存储性能优化
关键:如何更快地写入和读取。
(1)存储介质
换过SSD的同学一般都会体会到操作体验的巨大提升,我所用过的Digital Ocean的主打亮点就是SSD。不过SSD的性价比和稳定性尚待考验,但未来应该是SSD的!
(2)分布式存储
了解了很多眼花缭乱的名词:RAID、HDFS、MongoDB GridFS...但完全不知道它们的关系和脉络到底如何,至于适用场景的话也没怎么看懂TAT,所以就不说了...以后如果有了这方面的实战经验,我想可以单独讨论一下。留几个链接以后再看吧: [1] 、 [2] 、 [3] 、 [4] 。
Web后端性能优化方面的知识就记录到这里,前端篇待续。
· EOF ·
相关推荐
在本项目"仿bilibili高性能后端.zip"中,开发者采用了一系列先进的技术栈来构建一个类似Bilibili的高性能后端系统。这个系统的核心组件包括Spring Boot、JSON Web Token (JWT)、FastDFS、Redis以及RocketMQ。下面将...
【web后端高级开发】 在Web开发领域,后端开发是构建网站或应用程序不可或缺的一部分,主要负责处理数据、业务逻辑以及与数据库的交互。本压缩包"web后端高级开发.zip"提供了一系列的PPT资源,旨在帮助Web新手快速...
一个基于python的web后端高性能开发框架,适用人群:对于后端感兴趣的计算机专业学生、计算机爱好者、工程师。 一个基于python的web后端高性能开发框架,适用人群:对于后端感兴趣的计算机专业学生、计算机爱好者、...
本项目旨在探讨Go语言用于构建Web后端的基础功能,对于初学者而言,这是一个很好的起点。 一、Go Web框架基础 Go语言的Web开发中,常见框架有Gin、Beego、Echo等。这些框架提供了便捷的路由设置、中间件处理和HTTP...
Nginx是一款高性能的Web服务器,它以其反向代理、负载均衡、静态文件处理和高效非...通过阅读《Nginx高性能Web服务器详解(完整版)》PDF文档,你将全面了解Nginx的各项特性和配置技巧,进一步提升你的运维和开发能力。
《Nginx高性能Web服务器》是一本深入探讨Nginx技术的权威著作,它涵盖了Nginx的基础知识、配置技巧以及优化策略。Nginx,以其高性能、轻量级和反向代理能力著称,是现代互联网架构中的关键组件。在本资料中,我们将...
随着移动互联网的普及,高可用高安全的 App 后端开发成为企业的迫切需求。以下是 PHP 开发高可用高安全 App 后端的相关知识点: 高可用 高可用的定义是指系统或应用在指定时间内保持可操作状态的能力。高可用是指...
"实验室选课系统web后端设计" 实验室选课系统web后端设计是基于Java语言和MySQL数据库开发的实验课选系统的后端设计。该系统的主要功能包括实验课选、教师考勤、成绩记录、课程资源共享等。系统采用前后端分离的...
后端接口调试是指在开发阶段,对后端服务提供的API(应用程序编程接口)进行测试,以验证其功能是否符合预期,数据传输是否准确,以及性能是否达标。这一过程对于保证软件质量、减少bug、优化系统性能至关重要。调试...
它旨在提高编程效率,简化系统编程,并在高并发环境下提供卓越的性能。"goweb:用Golang编写的Web后端"这个主题,将探讨如何使用Go语言构建强大的Web服务器应用程序。 首先,我们要理解Go语言的优势。Go语言设计时...
在web后端开发中,实现一个web端的线上答题程序是一项常见的任务,它涉及到许多关键技术点和步骤。这里,我们将深入探讨如何构建这样一个系统,主要关注后端的实现。 首先,我们需要一个用户认证系统,允许用户注册...
《Nginx高性能Web服务器详解》是一本深入探讨Nginx技术的专业书籍,由博文视点出版,电子工业出版社发行。Nginx是一款广泛应用的开源Web服务器,以其高性能、高并发处理能力以及轻量级的特性著称。本书旨在帮助读者...
在Web后端开发中,构建一个线上投票程序是一项常见的任务,它涉及到用户交互、数据处理、安全性以及服务器响应等多个方面。下面将详细讲解这个过程中的关键知识点。 首先,我们需要一个前端界面来展示投票选项和...
【Web后端课程大作业1】是一门针对Web后端技术深入学习的实践项目,旨在提升学生对现代Web应用架构的理解和应用能力。这个大作业涵盖了多个关键知识点,包括服务发现与注册、需求分析、服务器规划、软件部署策略以及...
该项目是一个全面型的Web后端设计源码,采用FastAPI、Pydantic、Tortoise-ORM、Aerich、MySQL和Redis等技术栈构建。...该设计适用于需要高性能和可靠性的Web后端开发,特别适合快速构建基于Python的现代Web应用程序。
【互联网架构设计:高性能的后端】 在互联网产品开发中,构建高性能的后端服务是至关重要的,它涉及网络硬件、逻辑计算、通信协议和数据存储等多个层面。下面我们将深入探讨这些方面,以理解如何设计出高效且稳定的...
在构建高性能Web站点时,基于LVS(Linux Virtual Server)的负载均衡技术是关键的一环。LVS是一种开源的负载均衡解决方案,它能够将网络流量有效地分发到多个服务器上,以提高系统的处理能力和可用性。本文将详细...
描述还强调这是“新的”和“绝对物有所值”,这可能意味着它包含了最新的技术或者优化,如高性能缓存机制、优化的查询算法等,从而提高了系统的效率和用户体验。 【标签】:“就是牛逼” 标签“就是牛逼”表达了...