高并发优化方案
高并发
QPS
每秒处理请求数
-
QPS 达到50
一般服务器就能应付,不需要优化 -
QPS 达到100
假设关系型数据库在0.01秒完成查询。
数据库缓存,数据库的负载均衡 -
QPS 达到800
假设使用百兆带宽,出口带宽为8m,假设每个页面10k。百兆带宽已被吃完
CDN加速,负载均衡 -
QPS 达到1000
缓存层已无法应对
做静态化 -
QPS 达到2000
文件系统的访问锁都成为灾难
做业务分离,分布式存储
优化方案
流量优化
防盗链处理
盗链是指在自己的页面上展示一些并不在自己服务器上的内容。如图片、音乐、视频、软件
防止别人通过技术手段绕过本站的资源展示页,盗用本站资源,使资源连接失效
根据referer或者签名,网站可以检测目标网页访问的来源,如果是资源文件则可以跟踪到显示它的网页地址。
nginx 模块ngx_http_referer_module用于阻挡来源非法的域名请求
nginx 指令valid_referers,全局变量$invalid_referer
//nginx 防盗链配置
location ~* \.(gif|jpg|png|bmp)$ {
valid_referers none blocked *.domain.com;
// valid_referers none blocked server_name
if ($invalid_referer) {//如果不在白名单
return 403;
#rewrite ^/ http://www.ttlsa.com/403.jpg;
}
}
// none :表示referer来源为空的情况
// blocked:referer来源不为空的情况,但是里面的值被代理或者防火墙删除了
// server_name:白名单
php curl伪造referer绕过上述防盗链
使用nginx 的 ngx_http_secure_link_module 模块进行防盗链
nginx 配置如下
location /img/ {
# 两个arg参数由请求获得
secure_link $arg_md5,$arg_expires;
# secret 为加密字符串 uri为访问的路由
secure_link_md5 secret$arg_expires$uri;
if ($secure_link = "") {
return 403;
}
if ($secure_link = "0") {
return 410;
}
}
php代码如下
//加密字符串 和nginx一致
$secret = "secret";
$uri = "/img/a.txt";
$expire = time();
//加密顺序要和nginx的配置一致
$md5 = base64_encode(md5($secret.$expire.$uri,true));
$md5 = strtr($md5, '+/', '-_');
$md5 = str_replace('=', '', $md5);
$url = "http://bd.test.com/img/a.txt?md5={$md5}&expires={$expire}";
前端优化
减少http请求
- 使用图片地图 //变多次请求为一次
- 使用css 精灵 贴图定位 //变多次请求为一次
- 合并多个css和js //变多次请求为一次
- 使用base64图片 //将图片请求省去
代码示例
$src = 'https://ss0.bdstatic.com/5aV1bjqh_Q23odCf/static/superman/img/logo/bd_logo1_31bdc765.png';
$img_info = getimagesize($src);
$base = base64_encode(file_get_contents($src)); //该base64 也可以用于css
$img_src = "data:{$img_info['mime']};base64," . $base;
exit("![]({$img_src})");
启用浏览器缓存和文件压缩
http缓存机制
缓存分类
- 200 from cache:直接从本地缓存中获取响应 //最快
- 304 not modified:协商缓存,浏览器在本地缓存没有命中的情况下,请求头中发送一定的校验数据到服务端,如果服务端没有改变,则从本地缓存中获取,返回304 //快速 不发送响应体
- 200 ok:以上两种缓存失败,直接从服务器请求回来 //最慢
相关header
优先级 pragma > cache-control > expires
- pragma:http1.0,设置为no-cache时,会禁用本地缓存
- expires:http1.0,expires对应一个时间戳,表示到期时间
- cache-control:http1.1 针对expires 时间不一致,运用时间间隔。
no-store:禁止浏览器缓存响应
no-cache:不允许直接使用本地缓存,先发起协商请求
max-age=delta-seconds:告知浏览器该响应本地缓存有效的最长期限,以秒为单位
协商缓存header
- Last-modified :通知浏览器资源的最后修改时间,时间戳
- Etag:http1.1 文件指纹标示,如果文件发送修改,则指纹会变。
- if-none-match:浏览器在本地缓存没有命中的情况下,请求头中发送一定的校验数据到服务端,如果服务端没有改变,则从本地缓存中获取,返回304
// php 模拟nginx 的304
$since = $_SERVER['HTTP_IF_MODIFIED_SINCE'];
$lefttime = 3600;
if (strtotime($since) + $lefttime > time()) {
header('HTTP/1.1 304 Not Modified');
exit;
}
header('Last-Modified: ' . gmdate('D,d M Y H:i:s', time() . ' GMT'));
echo time();
nginx配置缓存策略
add_header指令:添加状态码为2xx和3xx的响应头信息
add_header name value [always];
可以设置 pragma/expires/cache-control 可以继承
expires指令:通知浏览器过期时长
expires time;
为负值时,表示cache-control:no-cache
为正值时,表示cache-control:max-age=指定时间
location ~* \.(gif|jpg|png|bmp)$ {
expires 30d;# 表示遇到上述图片时,告诉浏览器缓存30天
expires 12h;# 缓存12小时
etag off;#关闭etag
add_header cache-control max-age=3600;#缓存3600
}
前端代码和资源压缩
- js代码压缩
一般是去掉多余空格、回车、替换长变量名、简化一些代码的写法
js代码压缩工具有在线工具、应用程序、编辑器插件;常见uglifyjs、yui composer、closure compiler - css压缩
去除空白、注释;常见css compressor - html压缩
不建议使用,有时会破坏代码结构。一般采用gzip - 图片压缩
对图片压缩是有必要的。jpegmini,imageoptim,tinypng - 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 设置请求代理服务器,该如何缓存内容
cdn加速
相关概念
什么是cdn:content dilivery network,内容分发网络。可以根据情况,将用户请求导向最近的节点。
使用cdn的优势:本地cache加速,提高站点访问速度,分担网络流量,节省带宽
- cdn工作原理:
传统访问:输入域名->解析ip->找到服务器->服务器返回
cdn:请求->智能dns解析->换回缓存服务器ip->内容返回(如命中缓存)->向源站点请求->结果返回给用户->结果缓存到缓存服务器 - cdn适用场景
站点中有大量静态资源,如css js
html和图片
大文件
直播网站 - cdn实现方式
lvs4层负载均衡
nginx 7层负载均衡和cache
反向代理
建立独立图片服务器
分担web服务器的io负载,提高性能和稳定性
能够专门对图片服务器进行优化,减少带宽成本,提高访问时间
提高可扩展,提高图片吞吐
采用独立域名,突破浏览器同一域名并发连接数限制。要处理,如何进行上传图片和图片同步
服务端优化
动态语言静态化
- 什么是动态语言静态化
将现有php等动态语言的逻辑代码生成为静态html文件,用户访问动态脚本重定向到静态html文件的过程,对于实时性要求不高的页面- 为什么要静态化
动态脚本通常会做逻辑计算和数据查询,访问量大时,服务器压力越大
访问量大时会造成cpu负载过高,数据库压力过大,
静态化可以减低逻辑处理压力,降低数据库服务器查询压力- 静态化实现方式
使用模版引擎
利用ob系列函数
ob_start 打开输出控制缓冲
ob_get_contents 返回输出缓冲区内容
ob_clean 清空输出缓冲
ob_end_flush 冲刷到缓冲区,并关闭
<?php
$cache_name = md5(__FILE__) . '.html';//生成缓存名称,通常用hash值
$cache_lefttime = 3600;//缓存过期时间
//判断缓存文件是否存在,缓存是否过期,html内容是否被修改
if (filectime(__FILE__) <= filectime($cache_name) && file_exists($cache_name) && filectime($cache_name) + $cache_lefttime > time()) {
include $cache_name;
exit;
}
ob_start();//开启缓存
?>
<p>this is ob_cache</p>
<?php
$content = ob_get_contents();//获取缓存内容
ob_end_flush();//将缓存冲到浏览器
$handle = fopen($cache_name, 'w');//打开文件
fwrite($handle, $content);//写入内容
fclose($handle);//关闭文件
动态语言并发处理
进程
进程是计算机中的程序关于某数据集合上的一次运行活动,是系统进行资源分配和调用的基本单位,是操作系统的基础。一个执行中的程序
进程的三态模型:多道程序系统中,进程在处理器上交替运行,状态不断发生变化;主要有运行、就绪、阻塞三种状态。
- 运行:当一个进程在处理机上运行时,则称该进程处于运行状态。处于此状态的进程数目小于处理器数目,对于单处理器系统处于运行的进程只能有一个,在没有其他进程执行时,通常会自动执行系统的空闲进程
- 就绪:当一个进程获得了处理机意外以外的一切资源,一旦得到处理机即可运行,则成为就绪状态。就绪状态可以按优先级来划分队列。例如,当一个进程由于时间片用完而进入就绪状态时,排入低优先级队列;当进程有io操作完成而进入就绪状态时,排入优先级高的队列
- 阻塞:也称为等待或者睡眠,一个进程正在等待某一事件发生(例如请求i o而等待io操作)而暂时停止运行,这时即使把处理机分配给进程也无法运行,故称为阻塞。
- 进程的五态模型:对于一个实际的系统,进程的状态及其转换更为复杂。包括新建态、活跃态/静止就绪、运行、活跃阻塞/静止阻塞、终止态
线程
由于用户的并发请求,为每一个请求都创建一个进程显然是不合理的。从系统资源开销的方面和效率上,引入线程的概念。
线程,有时被称为轻量级进程,是程序执行流的最小单元。线程是进程中的一个实体,是被系统独立调度和分派的基本单位,线程自己不拥有系统资源,只拥有一点在运行中必不可少的资源。线程可与同属一个进程的其他线程共享进程所拥有的全部资源。一个线程可以创建或撤销另
线程的三种状态:就绪,阻塞和运行
- 就绪 线程具备运行的所有条件,逻辑上可以运行,在等待处理机
- 阻塞 线程在等待一个事件,逻辑上不可执行
- 运行 线程占有处理机,正在运行
线程和进程的区别
- 线程是进程内的一个执行单元,进程内至少有一个线程,他们共享进程的地址空间,而进程拥有自己的地址空间。若程序只有一个线程,那就是程序本身了。
- 进程是资源分配和拥有的单元,同一个进程内的线程共享进程的资源
- 线程是处理器调度的基本单元,但进程不是
- 两者均可并发执行
- 每个独立线程有一个程序运行的入口,顺序执行序列和程序出口,但是线程不能够独立执行,必须依存在应用程序中,由应用程序提供多个线程执行控制
协程
协程是一种用户态的轻量级的线程,协程的调度完全由用户控制。协程拥有自己的寄存器上下文和栈。协程调度切换时,将寄存器上下文和栈保存到其他地方,在切回来的时候,恢复先前保存的寄存器上下文和栈,直接操作栈则基本没有内核切换的开销,可以不加锁的访问全局变量,所以上下文切换非常快。
线程和协程的区别
- 一个线程可以多个协程,一个进程也可以单独拥有多个协程
- 线程进程都是同步机制,而协程是异步
- 协程能保存上一次调用时的状态,每次过程重入时,就相当于进入上一次调用的状态。
多进程
同一时间里,同一计算机系统中如果允许两个或两个以上的进程处于允许状态,就是多进程。
多线程
线程就是把一个进程分成很多片,每一片可以是一个独立的流程
与多进程的区别是只会使用一个进程的资源,线程间可以直接通信
同步阻塞模型
最早的服务器程序都是通过多进程,多线程来解决并发io问题,一个请求创建一个进程,然后子进程进入循环同步阻塞与客户端进行交互,收发数据。
异步非阻塞
现在各种高并发异步io的服务器程序都是基于epoll实现的。
io复用异步阻塞程序使用经典reactor模型,reactor本身不处理任何数据收发,只是可以监听一个socket句柄的时间变化
php并发编程
- swoole扩展
- 消息队列
应用解耦,流量削峰,日志处理,消息通讯,kafka rabbitmq,redis - 接口的并发请求
数据库优化
数据库缓存
缓存需要考虑的问题:缓存方式的选择,缓存场景的选择,缓存数据的实时性,缓存数据的稳定性
mysql查询缓存
sql文本和查询结果的映射,sql必须完全一致(mysql内部使用hash码对应),才能使用缓存。
query_cache_type
查询缓存有0、1、2。0表示不使用查询缓存,1表示始终使用查询缓存,2表示按需使用查询缓存。
- 为1 时,也可以关闭查询缓存;select SQL_NO_CACHE * from table where condition;
- 为2 时,按需使用。select SQL_CACHE * from table where condition;
query_cache_size 默认情况下为0。表示为查询缓存预留的内存为0,0则无法使用查询缓存。可在my.cnf中设置。
清空缓存 - flush query cache //清理查询缓存中内存碎片
- reset query cache //从查询缓存中移出所有查询
- flush tables //关闭所有的表,同时将清空缓存中的内容
redis
可固化,数据结构多,
mysql查询优化
数据库服务器架构优化
数据库表结构优化
索引优化
sql优化
web服务器优化
7层负载均衡
基于url应用层信息的均衡操作
优点
- 功能强大,运行稳定,性能卓越
- 配置简单灵活
- 能够自动剔除工作不正常的后段服务器
- 上传文件使用异步模式
- 支持多种分配策略,可以权重分配,分配方式灵活
nginx负载均衡
内置策略
- 加权策略:首先将请求分配给高权重的机器,直到该机器的权值降到比其他机器低时,才开始分配请求给下一个高权重机器。
- ip hash:内置另外一个负载均衡,流程和轮询很类似,只是其中的算法和具体的策略有些变化。ip hash 算法是一种变相的轮询算法。
扩展策略:fair策略、通用hash、一致性hash
- fair 策略:根据后段服务器响应时间判断负载情况,从中选择出负载最轻的机器进行分流。
- 通用hash、一致性hash:通用hash比较简单,可以以nginx内置的变量为key进行hash,一致性hash采用了nginx内置的一致性hash环,支持memcache
http{
upstream cluster{
server srv1;
server srv2;
}
server{
listen 80;
location / {
proxy_pass http://cluster;
}
}
}
//以下为示例
upstream cluster{
#ip_hash;#根据hash,因ip一致所以拿到同一个机器
server 127.0.0.1:8080 weight=10;# 权重 轮询从大到小,顺序8081,8080,8082
server 127.0.0.1:8081 weight=13;
server 127.0.0.1:8082 weight=8;
}
server {
listen 8083;
server_name localhost;
#charset koi8-r;
#access_log /var/log/nginx/log/host.access.log main;
location / {
proxy_pass http://cluster;# 这里的cluster与上面的cluster一致
}
}
# 服务器1
server {
listen 8080;
server_name localhost;
#charset koi8-r;
#access_log /var/log/nginx/log/host.access.log main;
location / {
add_header REAL_SERVER 8080;#在响应头里查看,区别server
root /usr/share/nginx/html;
index index.html index.php index.htm;
}
}
# 服务器2
server {
listen 8081;
server_name localhost;
#charset koi8-r;
#access_log /var/log/nginx/log/host.access.log main;
location / {
add_header REAL_SERVER 8081;
root /usr/share/nginx/html;
index index.html index.php index.htm;
}
}
# 服务器3
server {
listen 8082;
server_name localhost;
#charset koi8-r;
#access_log /var/log/nginx/log/host.access.log main;
location / {
add_header REAL_SERVER 8082;
root /usr/share/nginx/html;
index index.html index.php index.htm;
}
}
4层负载均衡
通过报文中的目标地址和端口,再加上负载均衡设备设置的服务器选择方式,决定最终的内部服务器
lvs实现服务器集群有三种方式NAT、DR和TUN模式