高并发解决方案
一、高并发和大流量解决方案
高并发架构相关概念
并发:在操作系统中,是指一个时间段中有几个程序都处于已启动运行到运行完毕之间,且这几个程序都是在同一个处理机上运行,但任一个时刻点上只有一个程序在处理机上运行;在互联网时代,所讲的并发,高并发通常是指并发访问,也就是在某个时间点,有多少个访问同时到来。通常一个系统的日PV在千万以上,有可能是一个高并发的系统。有的公司完全不走技术路线,全靠机器堆,这不在讨论范围内。
QPS:每秒钟请求或者查询的数量,在互联网领域,指每秒响应请求数(指HTTP请求);并发连接数是系统同时处理的请求数量
吞吐量:单位时间内处理的请求数量(通常由QPS与并发数决定)
响应时间:从请求发出到收到响应花费的时间。例如系统处理一个HTTP请求需要100ms。
PV:综合浏览量(page view),即页面浏览量或者点击量,一个访客在24小时内访问的页面数量;同一个人浏览网站同一页面,只记作一次PV
UV:独立访客(unique visitor),即一定时间范围内相同访客多次访问网站,只计算为一个独立访客
带宽:计算带宽大小需关注两个指标,峰值流量和页面的平均大小
日网站带宽=PV/统计时间(换算到s)*平均页面大小(单位KB)*8;峰值一般是平均值的倍数,根据实际情况来定
峰值每秒请求数(QPS)=(总PV数*80%)/(6小时秒数*20%);80%的访问量集中在20%的时间
压力测试:测试能承受的最大并发,测试最大承受的QPS值
常用性能测试工具:ab,wrk,http_load,web_bench,siege,apache jmeter;ab全称是apache benchmark,apache官方推出的工具,创建多个并发访问线程,模拟多个访问者同时对某一trl地址进行访问,它的测试目标是基于url的,因此既可以用来测试apache的负载能力,也可以测试nginx,lighthttp,tomcat,IIS等其它web服务器的压力;ab的使用:模拟并发请求100次,总共请求5000次,ab -c 100 -n 5000 待测试网站;测试机器与被测试机器分开,不要对线上服务做压力测试,观察测试工具ab所在机器,以及被测试的前端机的CPU,内存,网络等都不超过最高限度的75%
高并发解决方案案例
流量优化:防盗链处理
前端优化:减少HTTP请求,合并css或js,添加异步请求,启用浏览器缓存和文件压缩,CDN加速,建立独立图片服务器,
服务端优化:页面静态化,并发处理,队列处理
数据库优化:数据库缓存,分库分表,分区操作,读写分离,负载均衡
web服务器优化:负载均衡,nginx反向代理,7,4层LVS软件
二、web资源防盗链
盗链:在自己的页面上展示一些并不在自己服务器上的内容,获得他人服务器上的资源地址,绕过别人的资源展示页面,直接在自己的页面上向最终用户提供此内容,常见的是小站盗用大站的图片,音乐,视频,软件等资源,通过盗链的方法可以减轻自己服务器的负担,因为真实的空间和流量均是来自别人的服务器
防盗链:防止别人通过一些技术手段绕过本站的资源展示页面,盗用本站的资源,让绕开本站资源展示页面的资源链接失效,可以大大减轻服务器及带宽的压力
工作原理:通过请求头中的referer或者签名,网站可以检测目标网页访问的来源网页,如果是资源文件,则可以跟踪到显示它的网页地址,一旦检测到来源不是本站即进行阻止或者返回制定的页面,通过计算签名的方式,判断请求是否合法,如果合法则显示,否则返回错误信息
实现方法:referer:nginx模块ngx_http_referer_module用于阻挡来源非法的域名请求,nginx指令valid_referers none | blocked | server_names | string...,none表示referer来源头部为空的情况,blocked表示referer来源头部不为空,但是里面的值被代理或者防火墙删除了,这些值都不以http://或者https://开头,server_names表示referer来源头部包含当前的server_names,全局变量$invalid_referer。不能彻底防范,只能提高门槛。也可以针对目录进行防盗链。
传统防盗链遇到的问题:伪造referer:可以使用加密签名解决
加密签名:使用第三方模块HttpAccessKeyModule实现Nginx防盗链。accesskey on|off 模块开关,accesskey_hashmethod md5|sha-1 签名加密方式,accesskey_arg GET参数名称,accesskey_signature 加密规则,在nginx的conf中设置
三、减少HTTP请求次数
性能黄金法则:只有10%-20%的最终用户响应时间花在接收请求的HTML文档上,剩下的80%-90%时间花在HTML文档所引用的所有组件(img,script,css,flash等)进行的HTTP请求上。
如何改善:改善响应时间的最简单途径就是减少组件的数量,并由此减少HTTP请求的数量
HTTP连接产生的开销:域名解析--TCP连接--发送请求--等待--下载资源--解析时间
减少HTTP请求的方式:
1、图片地图:允许在一个图片上关联多个URL,目标URL的选择取决于用户单击了图片上的哪个位置,以位置信息定位超链接,把HTTP请求减少为一个,可以保证设计的完整性和功能的齐全性,使用map和area标签;
<img usemap="#map" src="/map.gif?t=111"> <map name="map"> <area shape="rect" coords="0,0,30,30" href=... title=""> ... </map>
2、CSS Sprites:CSS精灵,通过使用合并图片,通过指定css的background-image和background-position来显示元素。图片地图与css精灵的响应时间基本上相同,但比使用各自独立图片的方式要快50%以上。
3、合并脚本和样式表:使用外部的js和css文件引用的方式,因为这要比直接写在页面中性能要更好一点;独立的一个js比用多个js文件组成的页面载入要快38%;把多个脚本合并为一个脚本,把多个样式表合并为一个样式表
4、图片使用base64编码减少页面请求数:采用base64的编码方式将图片直接嵌入到网页中,而不是从外部载入
四、浏览器缓存和数据压缩优化
HTTP缓存机制:如果请求成功会有三种情况:
1、 200 from cache:直接从本地缓存中获取相应,最快速,最省流量,因为根本没有向服务器进行请求;
2、 304 not modified:协商缓存,浏览器在本地没有命中的情况下请求头中发送一定的校验数据到服务端,如果服务端数据没有改变浏览器从本地缓存响应,返回304,快速,发送的数据很少,只返回一些基本的响应头信息,数据量很小,不发送实际响应体;
3、 200 OK:以上两种缓存全部失败,服务器返回完整响应,没有用到缓存,相对最慢。
缓存策略的选择:
适合缓存的内容:不变的图像,如logo,图标等,js,css静态文件,可下载的内容,媒体文件;
建议使用协商缓存:html文件,经常替换的图片,经常修改的js,css文件,js和css文件的加载可以加入文件的签名来拒绝缓存,如a.css?签名或a.签名.js;
不建议缓存的内容:用户隐私等敏感数据,经常改变的api数据接口
nginx配置缓存策略:
本地缓存配置:add_header指令:添加状态码为2xx和3xx的响应头信息,add_header name value [always];,可以设置Pragma/Expires/Cache-Control,可以继承;expires指令:通知浏览器过期时长,expires time;,为负值时表示Cache-Control: no-cache;,当为正或者0时,就表示Cache-Control: max-age=指定的时间;;当为max时,Cache-Control设置到10年;
协商缓存相关配置:Etag指令:指定签名;etag on|off;,默认是on
前端代码和资源的压缩:
让资源文件更小,加快文件在网络中的传输,让网页更快的展现,降低带宽和流量开销;压缩方式:js,css,图片,html代码的压缩,Gzip压缩。
js代码压缩:一般是去掉多余的空格和回车,替换长变量名,简化一些代码写法等,代码压缩工具很多UglifyJS(压缩,语法检查,美化代码,代码缩减,转化)、YUI Compressor(来自yahoo,只有压缩功能)、Closure Compiler(来自google,功能和UglifyJS类似,压缩的方式不一样),有在线工具tool.css-js.com,应用程序,编辑器插件。
css代码压缩:原理和js压缩原理类似,同样是去除空白符,注释并且优化一些css语义规则等,压缩工具CSS Compressor(可以选择模式)。
html代码压缩:不建议使用代码压缩,有时会破坏代码结构,可以使用Gzip压缩,当然也可以使用htmlcompressor工具,不过转换后一定要检查代码结构。
img压缩:一般图片在web系统的比重都比较大,压缩工具:tinypng,JpegMini,ImageOptim。
Gzip压缩:配置nginx服务,gzip on|off,gzip_buffers 32 4K|16 8K #缓冲(在内存中缓存几块?每块多大),gzip_comp_level [1-9] #推荐6 压缩级别(级别越高,压的越小,越浪费CPU计算资源),gzip_disable #正则匹配UA 什么样的uri不进行gzip,gzip_min_length 200 #开始压缩的最小长度,gzip_http_version 1.0|1.1 #开始压缩的http协议版本,gzip_proxied #设置请求者代理服务器,该如何缓存内容,gzip_types text/plain applocation/xml #对哪些类型的文件用压缩,gzip_vary on|off #是否传输gzip压缩标志。其他工具:自动化构建工具Grunt。
五、CDN加速
CDN:Content Delivery Network,内容分发网络,尽可能避开互联网上有可能影响数据传输速度和稳定性的瓶颈和环节,使内容传输的更快更稳定;在网络各处放置节点服务器所构成的在现有的互联网基础之上的一层智能虚拟网络;CDN系统能够实时的根据网络流量和各节点的连接,负载情况以及到用户的距离和响应时间等综合信息将用户的请求重新导向离用户最近的服务节点上。
CDN的工作原理:
- 传统访问:用户在浏览器输入域名发起请求--解析域名获取服务器IP地址--根据IP地址找到对应的服务器--服务器响应并返回数据;
- 使用CDN访问:用户发起请求--智能DNS的解析(根据IP判断地理位置,接入网类型,选择路由最短和负载最轻的服务器)--取得缓存服务器IP--把内容返回给用户(如果缓存中有)--向源站发起请求--将结果返回给用户--将结果存入缓存服务器
CDN适用场景:站点或者应用中大量静态资源的加速分发,如css,js,img和html;大文件下载;直播网站等
CDN的实现:BAT等都有提供CDN服务,可用LVS做4层负载均衡;可用nginx,Varnish,Squid,Apache TrafficServer做7层负载均衡和cache;使用squid反向代理,或者nginx等的反向代理
六、建立独立的图片服务器
独立的必要性:分担web服务器的I/O负载-将耗费资源的图片服务分离出来,提高服务器的性能和稳定性;能够专门的图片服务器进行优化-为图片服务设置有针对性的缓存方案,减少带宽成本,提高访问速度;提高网站的可扩展性-通过增加图片服务器,提高图片吞吐能力
采用独立域名:原因:同一域名下浏览器的并发连接数有限制,突破浏览器连接数的限制;由于cookie的原因,对缓存不利,大部分web cache都只缓存不带cookie的请求,导致每次的图片请求都不能命中cache
独立后的问题:如何进行图片上传和图片同步:NFS共享方式;利用FTP同步
七、动态语言静态化
将现有PHP等动态语言的逻辑代码生成为静态HTML文件,用户访问动态脚本重定向到静态HTML文件的过程。对实时性要求不高的页面比较适合。
原因:动态脚本通常会做逻辑计算和数据查询,访问量越大,服务器压力越大;访问量大时可能会造成CPU负载过高,数据库服务器压力过大;静态化可以降低逻辑处理压力,降低数据库服务器查询压力
静态化的实现方式:
1.使用模板引擎:可以使用smarty的缓存机制生成静态HTML缓存文件;
2.利用ob系列的函数:ob_start():打开输出控制缓冲,ob_ge_contents():返回输出缓冲区内容,ob_clean():清空输出缓冲区,ob_end_flush():冲刷出(送出)输出缓冲区内容并关闭缓冲,可以判断文件的inode修改时间,判断是否过期使用filectime函数
八、动态语言层的并发处理
进程&线程&协程&多线程等(具体看这里https://www.cnblogs.com/sunny0824/p/13699056.html)
九、数据库缓存层的优化
mysql等一些常见的关系型数据库的数据都存储在磁盘当中,在高并发场景下,业务应用对mysql产生的增删改查的操作造成的巨大的IO开销和查询压力,这无疑对数据库和服务器都是一种巨大的压力。
为了解决此类问题,缓存数据的概念应运而生。极大的解决数据库服务器的压力,提高应用数据的响应速度。常见的缓存形式:内存缓存,文件缓存。
缓存数据是为了让客户端很少甚至不访问数据库服务器进行数据的查询,高并发下,能最大程度的降低对数据库服务器的访问压力。默认情况下:用户请求->数据查询->连接数据库服务器并查询数据->将数据缓存起来(html,内存,json,序列化数据)->显示给客户端;用户再次请求或者新用户访问->数据查询->直接从缓存中获取数据->显示给客户端
mysql的查询缓存:query_cache_type:查询缓存类型,有0,1,2三个取值,0则不使用查询缓存,1表示始终使用查询缓存,2表示按需使用查询缓存。
方法:memcache 与 redis
1. 使用memcache缓存查询数据
memcache工作原理:是一个高性能的分布式的内存对象缓存系统,通过在内存里维护一个统一的巨大的hash表,能够用来存储各种格式的数据,包括图像,视频,文件以及数据库检索的结果等,简单的说就是将数据调用到内存,然后从内存中读取,从而大大提高读取速度。
memcache工作流程:先检查客户端的请求数据是否在memcache中,如有,直接把请求数据返回,不再对数据库进行任何操作;如果请求的数据不在memcache中,就去查数据库,把从数据库中获取的数据返回给客户端,同时把数据缓存一份到memcache中。
memcache方法:获取:get(key) 设置:set(key, val, expire) 删除:delete(key)
2. 使用redis缓存查询数据:
与memcache的区别:
- 性能相差不大,redis在2.0版本后增加了自己的VM特性,突破物理内存的限制,memcache可以修改最大可用内存,采用LRU算法;
- redis依赖客户端来实现分布式读写,memcache本身没有数据冗余机制;
- redis支持快照,AOF,依赖快照进行持久化,aof增强了可靠性的同时,对性能有所影响,memcache不支持持久化,通常做缓存,提升性能;
- memcache在并发场景下,用cas保证一致性,redis事务支持比较弱,只能保证事务中的每个操作连续执行;
- redis支持多种类的数据类型;redis用于数据量较小的高性能操作和运算上,memcache用于在动态系统中减少数据库负载,提升性能,适合做缓存,提高性能
十、mysql数据库层的优化
优化方向:数据表数据类型优化,索引优化,sql语句优化,存储引擎的优化,数据表结构设计的优化,数据库服务器架构的优化
- 数据表数据类型优化:字段使用什么样的数据类型更合适,性能更快,tinyint、smallint、bigint,考虑空间和范围的问题;char、varchar,存储字符串长度是否固定;enum,特定固定的分类可以使用enum存储,效率更快;IP地址的存储,ip2long(),使用整型存储IP地址
- 索引的优化:建立合适的索引,索引在什么场景下效率最高,索引的创建原则:不是越多越好,在合适的字段上创建合适的索引,复合索引的前缀原则,like查询%的问题,全表扫描优化,or条件索引使用情况,字符串类型索引失效的问题
- sql语句的优化:优化查询过程中的数据访问,优化长难句、特定类型的查询语句。使用limit,返回列不用*,变复杂为简单,切分查询,分解关联查询,优化count(),优化关联查询,优化子查询,优化group by和distinct,优化limit和union
- 存储引擎的优化:尽量使用innoDB存储引擎
- 数据表结构设计的优化:分区操作,通过特定的策略对数据表进行物理拆分,对用户透明,partition by;分库分表,水平拆分,垂直拆分
- 数据库架构的优化:主从复制,读写分离,双主热备,binlog日志,中继日志,主从库binlog的交换,事件传输;负载均衡,通过LVS的三种基本模式实现负载均衡,mycat数据库中间件实现负载均衡
十一、 web服务器的负载均衡
七层负载均衡的实现:
基于URL等应用层信息的负载均衡,nginx的proxy是它一个很强大的功能,实现了7层负载均衡,功能强大,性能卓越,运行稳定,配置简单灵活,能够自动剔除工作不正常的后端服务器,上传文件使用异步模式,支持多种分配策略,可以分配权重,分配方式灵活。
nginx负载均衡:内置策略:IP Hash,加权轮询;扩展策略:fair策略,通用hash,一致性hash
加权轮询:首先将请求都分给高权重的机器,直到该机器的权值降到了比其他机器低,才开始将请求分给下一个高权重的机器,当所有后端机器都down掉时,nginx会立即将所有机器的标志位清成初始状态,以避免造成所有的机器都处在timeout的状态;
IP Hash:流程和轮询很类似,只是其中的算法和具体的策略有些变化,算法是一种变相的轮询算法;
fair:根据后端服务器的响应时间判断负载情况,从中选出负载最轻的机器进行分流;
通用hash,一致性hash:通用hash比较简单,可以以nginx内置的变量为key进行hash,一致性hash采用了nginx内置的一致性hash环,支持memcache
nginx配置:
http {
upstream cluster{
#ip_hash;
server srv1 weight=1;
server srv2;
server srv3;
}
server {
listen 80;
location / {
proxy_pass http://cluster;
}
}
}
四层负载均衡的实现:
通过报文中的目标地址和端口,再加上负载均衡设备设置的服务器选择方式,决定最终选择的内部服务器。LVS实现服务器集群负载均衡有三种方式,NAT,DR和TUN。