小谈php处理网站大数据大流量与高并发

并发:

在操作系统中,是指一个时间段中有几个程序都处于已启动运行到运行完毕之间,且这几个程序都是在同一个处理机上运行,但任意一个时刻上只有一个程序在处理机上运行。

我们说的高并发是什么

上面的定义明显不是我们通常所言的并发,在互联网时代,所讲的并发、高并发,通常是指并发访问。也就是在某个时间点,有多少个访问同时到来通常如果一个系统的日PV在千万以上,有可能是一个高并发的系统,但是有的公司完全不走技术路线,全靠机器堆,这不在我们的讨论范围。

高并发的问题,我们具体该关心什么
  • QPS:每秒钟请求或者查询的数量,在互联网领域,指每秒响应请求数(指HTTP请求)
  • 吞吐量:单位时间内处理的请求数量(通常由QPS与并发数决定)
  • 响应时间:从请求发出到收到响应花费的时间,例如系统处理一个HTTP请求需要100ms,这个100ms就是系统的响应时间
  • PV:综合浏览量(Page View),即页面浏览量或者点击量,一个访客在24小时内访问的页面数量,同一个人浏览你的网站同一页面,只记作一次PV
  • UV:独立访问(UniQue Visitor),即一定时间范围内相同访客多次访问网站,只计算为1个独立访客
  • 带宽:计算带宽大小需关注两个指标,峰值流量和页面的平均大小

日网站带宽=PV/统计时间(换算到秒)平均页面大小(单位KB)8

峰值一般是平均值的倍数,根据实际情况来定

QPS不等于并发连接数

QPS是每秒HTTP请求数量,并发连接数是系统同时处理的请求数量

(总PV数80%)/(6小时秒数20%)=峰值每秒请求数(QPS)

这里假设80%的访问量集中在20%的时间

QPS达到极限,各种情况如何处理?

随着QPS的增长,每个阶段需要根据实际情况来进行优化,优化的方案也与硬件条件、网络带宽息息相关。

  • QPS达到50

    可以称之为小型网站,一般的服务器就可以应付

  • QPS达到100

    假设关系型数据库的每次请求在0.01秒完成,假设单页面只有一个SQL查询,那么100QPS意味这1秒钟完成100次请求,但是此时我们并不能保证数据库查询能完成100次。
    方案:数据库缓存层、数据库的负载均衡

  • QPS达到800

    假设我们使用百兆带宽,意味着网站出口的实际带宽是8M左右;
    假设每个页面只有10k,在这个并发条件下,百兆带宽已经吃完。
    方案:CDN加速、负载均衡

  • QPS达到1000

    假设使用Memcache缓存数据库查询数据,每个页面对Memcache的请求远大于直接对DB的请求
    Memcache的悲观并发数在2W左右,但有可能在之前内网带宽已经吃光,表现出不稳定
    方案:静态HTML缓存

  • QPS达到2000

    这个级别下,文件系统访问锁都成为灾难
    方案:做业务分离,分布式存储

1-1. WEB资源防盗链

什么是防盗链

盗链:指在自己的页面上展示一些并不在自己服务器上的内容。明白了盗链的意思,相信你就已经知道什么是防盗链了。

防盗链的工作原理

通过referer或者签名,网站可以检测目标网页访问的来源页,一旦检测到来源页不是本站即进行阻止或者返回指定页面。

防盗链的实现方法

方案1:Referer

nginx 模块ngx_http_referer_module用于阻挡来源非法的域名请求。

location ~ .*\.(gif|jpg|png|swf|bmp|jpeg)$
{
    valid_referers none blocked jiatengfei.com
    if ($invalid_referer)
    {
        #return 403;
        rewrite ^/ http://jiatengfei.com/403.jpg;
    }
}

缺点:请求头是来自于客户端的,是可伪造的,暂不在本文讨论范围内。

方案2:加密签名
使用第三方HttpAccessKeyModule模块实现NGINX防盗链。

  • accesskey on|off 模块开关
  • accesskey_hashmethod md5/sha-1签名加密方式
  • accesskey_arg url中的GET参数名称
  • accesskeysignature 加密规则

修改nginx的conf文件:

location ~ .*\.(gif|jpg|png|swf|bmp|jpeg)$
{
    accesskey on;
    accesskey_hashmethod md5;
    accesskey_arg "key";
    accesskey_signature "mypass$remote_addr";
}

示例:

<?php
$key= md5("mypass".$_SERVER['REMOTE_ADDR']);
//$key生成的算法要跟配置文件的设置保持一致

echo '<img src="./logo.png">';    //403
echo '<img src="./logo.png?key='.$key.'">';    //正常显示
?>

缺点:要在每个资源后面添加签名

1-2. 减少HTTP请求

只有10%-20%的最终用户响应时间花在接收请求的HTML文档上,剩下的80%-90%时间花在HTML文档所引用的所有组件(图片,script,css,flash等)进行的HTTP请求上。因此,改善响应时间的最简单途径就是减少组件的数量,并由此减少HTTP请求的数量。

  • 图片地图

把多张图片整合到一张图片中,减少HTTP请求数;

  • CSS Sprites

CSS精灵,通过使用合并图片,通过指定CSS的background-image和background-position来显示元素;

  • 合并JS脚本和CSS样式表

适当地把多个JS脚本合并为一个脚本,把多个CSS样式表合并为一个样式表;

  • 图片使用base64编码减少页面的请求数

data:url模式可以在页面中渲染图片但无需额外的HTTP请求

  • 使用外部js和css文件引用方式
    在用户不带缓存访问页面的时候,内联所有的js和css的效率更快,原因是外置js和css带来额外的http请求开销,1个http请求相对于3个http请求要更快一些。

    其实,使用外部JS和CSS文件会产生更快的访问速度,这是由于外部JS和CSS文件能被浏览器缓存,当下次再请求相同的JS和CSS时,浏览器将不会再发出HTTP请求,而是使用缓存的JS和CSS文件,减少了HTTP请求数。

1-3. 浏览器缓存和压缩优化

HTTP缓存机制

1.200 from cache: 直接从本地缓存中获取响应,最快速,最省流量,因为根本没有向服务器发送请求

2.304 not modify:协商缓存,浏览器在本地没有命中情况下请求头发送一定的校验数据到服务端,如果服务端数据没有改变浏览器从本地缓存响应,返回304,快速,发送的数据小,只返回一些基本的响应头信息,数据量很小,不发生实际响应体

3.200 OK:以上两种缓存全都失败,服务器返回完整响应,没有用到缓存,相对最慢。

location ~ .*\.(gif|jpg|png|swf|bmp|jpeg)$
{
    expires 30d;  //缓存30天
}

gzip压缩

location ~ .*\.(gif|jpg|png|swf|bmp|jpeg)$
{
    gzip on|off //是否开启
    gzip_buffers 32 4K | 16 8K //缓冲
    gzip_comp_level [1-9] 推荐6 压缩级别(级别越高,压的越小,越浪费CPU资源)
    gzip_disable //什么样的uri不进行gzip压缩
    gzip_min_length 200 //开始压缩的最小长度
    gzip_http_version //开始压缩的http协议版本 
    gzip_types text/plain application/xml //对哪些类型的文件用
    gzip_proxied//设置请求者代理服务器,该如何缓存内容
    gzip_vary on|off //是否传输gzip压缩标志
}

1-4. CDN加速

CDN的全称是Content Delivery Network,即内容分发网络。利用部署大量网络节点,通过服务器缓存加速,让用户可以就近同运营商网络,提高用户访问网站的响应速度,也可以支持更大规模的访问量。

传统访问流程:

用户在浏览器输入域名发起请求 --> 解析域名获取服务器IP地址 --> 根据IP地址找到对应的服务器 --> 服务器响应并返回数据

CDN访问流程:

用户发起请求 --> 智能DNS解析(根据IP判断地理位置、接入网络类型、选择路由最短和负载最轻的服务器) --> 取得缓存服务器IP --> 把内容返回给用户(如果缓存中有) --> 向源站发起请求 --> 将结果返回给用户 --> 将结果存入缓存服务器

CDN加速原理

首先,将内容缓存(将网站的内容存到内存或者本地文件,使用squid做为web服务)

Squid:Squid是一个代理缓存服务器,支持FTP、http、http等网络协议,它和一般的代理缓存软件的不同之处在于它使用一个单独的、非模块化的、I/O驱动的进程来处理所有客户端的请求。

然后,当用户访问网站内容的时候,通过调度系统将用户的请求路由或者引导到离用户接入网络最近或者访问效果的缓存服务器上,有该缓存服务器为用户提供内容服务;相对于直接访问源站,这种方式缩短了用户和内容之间的网络距离,从而达到加速的效果。

CDN适用场景
  • 站点或应用中有大量静态和更新频率低的资源,例如:css、js、html、图片
  • 大文件下载
  • 数据流量大的产品:如直播网站
CDN实现
  • 服务器商提供的CDN服务
  • 可用LVS做4层负载均衡
  • 可用Nginx、Varnish、Squid、Apache TrafficServer做7层负载均衡和cache

1-5. 独立的图片服务器

对于Web服务器来说,不管是Apache、IIS还是其他容器,图片是最消耗资源的,于是我们有必要将图片与页面进行分离,这是基本上大型网站都会采用的策略,他们都有独立的、甚至很多台的图片服务器。这样的架构可以降低提供页面访问请求的服务器系统压力,并且可以保证系统不会因为图片问题而崩溃。

在应用服务器和图片服务器上,可以进行不同的配置优化,比如apache在配置ContentType的时候可以尽量少支持、尽可能少的LoadModule,保证更高的系统消耗和执行效率。

独立的图片服务器分担Web服务器的I/O负载,将耗资源的图片服务分离出来,提高服务器的性能和稳定性。

1-6. 动态语言静态化

什么是动态语言静态化

将现有PHP等动态语言的逻辑代码生成为静态HTML文件,用户访问动态脚本重定向到静态HTML文件的过程。

为什么要静态化
  1. 动态脚本通常会做逻辑计算和数据查询,访问量越大,服务器压力越大
  2. 访问量大时可能会造成CPU负载过高,数据库服务器压力过大
  3. 静态化可以降低逻辑处理压力,降低数据库服务器查询压力
静态化的实现方式
第一种:使用模板引擎

( 1 ) 可以使用smarty的缓存机制生成静态html缓存文件

$smarty->cache_dir = $ROOT."/cache"; //缓存目录
$smarty->caching = true; //是否开启缓存
$smarty->cache_lifetime = '3600'; //缓存时间
$smarty->display(string template [, string cache_id [,string compile_id]]);

备注:cache_id表示为缓存id; compile_id表示为编译文件id。

(2) 清除smarty缓存

$smarty->clear_all_cache(); //清除所有缓存
$smarty->clear_cache('file.html'); //清除指定的缓存
$smarty->clear_cache('article.html',$art_id); //清除同一模板下的指定缓存号的缓存
第二种: 利用ob系列的函数

相关函数

  • ob_start(); // 打开输出控制缓冲
  • ob_get_contents(); // 返回输出缓冲区内容
  • ob_clean(); // 清空输出缓冲区
  • ob_end_flush(); // 冲刷出(送出)输出缓冲区内容并关闭缓冲

示例:

<?php

$cache_name = md5(__FILE__).'.html';  // 定义缓存文件名
$cache_lifetime = 3600;  // 定义时间

// 限制条件符合就加载缓存文件
if(filemtime(__FILE__) <= filemtime($cache_name) && file_exists($cache_name) &&
 filectime($cache_name) + $cache_lifetime > time())
{
    include $cache_name;
    exit;
}

ob_start();
?>
    =========这部分是要缓存的内容========
<?php

$content = ob_get_contents();
ob_end_flush();
$handle = fopen($cache_name,'w');
fwrite($handle,$content);
fclose($handle);

?>

1-7. 数据库缓存

MYSQL等一些常见的关系型数据库的数据都存储在磁盘当中,在高并发场景下,业务应用对MYSQL产生的增、删、改、查的操作造成巨大的I/O开销和查询压力,这无疑对数据库和服务器都是一种巨大的压力,为了解决此类问题,缓存数据的概念应运而生。

缓存形式:文件缓存、内容缓存(推荐)

用户请求-->数据查询-->连接数据库服务器并查询数据-->将数据缓存起来(HTML、内存、JSON、序列化数据)-->显示给客户端

用户再次请求或者新用户访问-->数据查询-->直接从缓存中获取数据-->显示给客户端

Redis 与 Memcache 数据库缓存

1-8. 负载均衡

1-8-1.七层负载均衡的实现

基于URL等应用信息的负载均衡

Nginx的proxy是它一个很强大的功能,实现了7层负载均衡

Nginx实现的优点
  • 功能强大,性能卓越,运行稳定
  • 配置简单灵活
  • 能够自动剔除工作不正常的后端服务器
  • 上传文件使用异步模式
  • 支持多种分配策略,可以分配权重,分配方式灵活

Nginx负载均衡拥有两种策略:内置策略、扩展策略

内置策略:IP Hash、加权轮询

扩展策略:fair策略、通用hash、一致性hash

加权轮询策略

首先将请求都分给高权重的机器,直到该机器的权值降到了比其他机器低,才开始将请求分给下一个高权重的机器;

当所有后端机器都down掉时,Nginx会立即将所有机器的标志位清成初始状态,以避免造成所有的机器都处于timeout的状态。

IP Hash策略

Nginx内置的另一个负载均衡的策略,流程和轮询很类似,只是其中的算法和具体的策略有些变化;IP Hash算法是一种变相的轮询算法。

fair策略

根据后端服务器的响应时间判断负载情况,从中选出负载最轻的机器进行分流。nginx本身不支持fair,需要下载nginx的upstrea_fair模块。

通用Hash、一致性Hash策略

通用hash比较简单,可以以Nginx内置的变量为key进行hash,一致性hash采用了Nginx内置的一致性hash环,支持memcache。

Nginx可以配置代理多台服务器,当一台服务器宕机之后,仍能保持系统可用。来看一个最简单的Nginx负载均衡配置:

http {
    upstream cluster {
        // ip_hash;
        server srv1;
        server srv2;
        server srv3;
    }
    server {
        listen 80;
        location / {
            proxy_pass http://cluster;
        }
    }
}

通过上述配置,Nginx会作为HTTP反向代理,把访问本机的HTTP请求,均分到后端集群的3台服务器上。

upstream按照轮询(默认)方式进行负载,每个请求按时间顺序逐一分配到不同的后端服务器,如果后端服务器down掉,能自动剔除。虽然这种方式简便、成本低廉。但缺点是:可靠性低和负载分配不均衡。适用于图片服务器集群和纯静态页面服务器集群。

1-8-2.四层负载均衡的实现

通过报文中的目标地址和端口,再加上载均衡设备设置的服务器选择方式,决定最终选择的内部服务器

LVS实现服务器集群负载均衡有三种方式:NAT、DR和TUN

小结:
  1. 先从流量做优化:防盗链、
  2. 前端优化:缓存、数据压缩、CDN加速、独立图片服务器、
  3. 服务器:动态静态化
  4. 数据层:数据缓存、MySQL优化
  5. 最后,Web服务器的负载均衡
4 人推荐

声明:本文原创发布于加藤非博客,转载请注明出处:加藤非博客 jiatengfei.com 。如有侵权,请联系本站删除。

加藤非博客
请先登录再发表评论
  • 最新评论

  • 总共0条评论