Nginx之LNMP、LNNMP、LNNNMP架构实现及缓存技术
2014-10-06 20:49
603 查看
1.前言
1.1Nginx简介
Nginx是一款由俄罗斯程序员IgorSysoev所开发轻量级的网页服务器、反向代理服务器以及电子邮件(IMAP/POP3)代理服务器。可以在目前所有主流的操作系统上运行。
Nginx采用模块化设计架构,易扩展;使用多线程处理客户请求,减少了进程上下文切换的开销;使用epoll或者kqueue事件驱动模型,提高了并发处理性能。
1.2Tengine介绍
Tengine是由淘宝网发起的Web服务器项目。它在Nginx的基础上,针对大访问量网站的需求,添加了很多高级功能和特性。
Tengine的性能和稳定性已经在大型的网站如淘宝网,天猫商城等得到了很好的检验。它的最终目标是打造一个高效、稳定、安全、易用的Web平台。
特性
继承Nginx-1.4.7的所有特性,100%兼容Nginx的配置;
动态模块加载(DSO)支持。加入一个模块不再需要重新编译整个Tengine;
支持SPDYv3协议,自动检测同一端口的SPDY请求和HTTP请求;
流式上传到HTTP后端服务器或FastCGI服务器,大量减少机器的I/O压力;
更加强大的负载均衡能力,包括一致性hash模块、会话保持模块,还可以对后端的服务器进行主动健康检查,根据服务器状态自动上线下线;
输入过滤器机制支持。通过使用这种机制Web应用防火墙的编写更为方便;
支持设置proxy、memcached、fastcgi、scgi、uwsgi在后端失败时的重试次数
动态脚本语言Lua支持。扩展功能非常高效简单;
支持管道(pipe)和syslog(本地和远端)形式的日志以及日志抽样;
支持按指定关键字(域名,url等)收集Tengine运行状态;
组合多个CSS、JavaScript文件的访问请求变成一个请求;
自动去除空白字符和注释从而减小页面的体积
自动根据CPU数目设置进程个数和绑定CPU亲缘性;
监控系统的负载和资源占用从而对系统进行保护;
显示对运维人员更友好的出错信息,便于定位出错机器;
更强大的防攻击(访问速度限制)模块;
更方便的命令行参数,如列出编译的模块列表、支持的指令等;
可以根据访问文件类型设置过期时间;
今天的实验将使用Tengine替代Nginx来搭建LNMP、LNNMP和LNNNMP。这三种架构后端都使用同样的PHP-FPM、MariaDB,这些服务器搭建参看以前的博文。
PHP-FPM,使用FastCGI协议和Nginx的WEB服务器通信。
MariadB,提供数据库服务器。
2.环境准备
2.1PHP-FPM
提供测试页面index.php
2.2Tengine安装
编译安装前修改版本信息,让客户端无法看到真实的服务器信息和版本号。
导出环境变量
3.LNMP
3.1规划
Nginx是WEB服务器。
3.2提供一个测试用index.html
3.3nginx配置文件
3.4SysV风格脚本
3.5运行nginx
3.6测试
访问http://192.168.23.80/index.php,这个请求会通过FastCGI发送到后端的PHP-FPM服务器处理,然后php程序被执行,去连接后端的数据库,最后将执行结果返回Nginx的WEB服务器,由它返回给客户端浏览器。
从响应首部可以看到上游服务器的一些信息,X-upS是有意为之,以示区别。
4.LNNMP
Nginx的作用是反向代理服务器、WEB服务器。这是一个多级的架构。
4.1规划
4.2WEB2服务器
IP地址为192.168.23.81,监听TCP80端口。
Tengine的安装配置请参照LNMP的步骤。
配置文件如下:
创建页面根目录,并提供一个测试index.html,同时为了演示动静分离效果,提供样式表文件给WEB1服务器的index.php。
4.3代理服务器
安装请参照LNMP中的步骤。
IP地址:
对外:172.16.23.80:80/16对内:172.16.23.100:80/24
配置文件
工作在代理服务器模式
注意监听8080端口的sorryserver的rewrite的写法。
提供本地8080端口BackupWEb服务器的维护页面
4.4测试
(1)静态资源的调度
可以看出对于静态资源的访问被调度到了不同的服务器上。
(2)动态网页的响应
可以从响应首部看到,客户端请求经过代理服务器被调度到了WEB1来响应。
(3)SorryServer
关闭WEB1和WEB2后,静态服务器组的backup服务器启用,通过代理服务器本地8080端口响应客户端请求,返回维护页面。
通过各种URL测试,包括动静资源的访问,所有客户端请求都被重写为对本地8080端口的sorryserver的maintenance.html页面的请求。
4.5综述
从以上的测试,可以看出,upstream中定义实现了动静分离的效果,对于动静资源的访问,应用不同的策略调度到了不同的上游服务器来响应。
当所有WEB服务停止响应的时候,还可以启用备用服务器提供临时服务。这样会有更好的用户体验。
5.LNNNMP
Nginx是反向代理、缓存、WEB服务器。
5.1前提
所有内网的服务器之间一定要时间同步,否则实验可能不能达到预期。
本实验在内网搭建了一台ntp服务器,所有主机启动ntpd,指向这一台ntp服务器。
5.2规划
在代理服务器和静态资源WEB服务器之间增加缓存服务器,缓存WEB1和WEB2的静态资源。
5.3代理服务器
修改配置文件
upstreamstatic{
server192.168.23.88;
server127.0.0.1:8080backup;
}
5.4缓存服务器
准备cache目录
#mkdir/data/nginx/webcache
#chown-Rnginx:nginx/data/nginx/webcache
缓存服务器配置
nginx配置
说明:
该缓存服务器只接收代理服务器发来的对静态资源的请求,都交给缓存服务器,缓存服务器调度到某一台WEB服务器响应客户端请求。如果失败就转向backup服务器返回维护页面。
proxy_cache_path用来设置缓存的存储路径为/data/nginx/webcache,缓存使用2级目录。
proxy_cache_valid可以针对不同的响应状态码实现不同的缓存时长。为了方便测试将200设置为1分钟,正常情况下可以设置长一点,例如1天。
proxy_next_upstream指的是出现问题的情况下,就使用下一个上游服务器。
proxy_cache_use_stale指的是如果上游服务器出现某些问题,是否使用过期的缓存响应客户端,默认为off。可以考虑对于某些错误的时候开启,因为即使出现了问题,也可以将过期的缓存返回客户端,这样用户的体验比看到什么服务器错误好。
增加响应首部X-Cache,其内容填充为缓存状态,这是为了便于客户端观察。
5.5测试
5.5.1静态资源测试
访问172.16.23.80/index.html。
第一次测试
缓存没有命中,因为缓存中没有数据。E-tag为54138849-78。
隔了一分钟,再次访问,缓存服务器返回状态为EXPIRED即过期。这是因为对于状态码200的缓存过期时长为1分钟。但是客户端在If-None-Match中提交了E-tag即54138849-78。缓存服务器收到了这个请求后,会首先判断缓存是否过期,过期则访问上有服务器,文件并未改变,则刷新缓存文件的新鲜度,返回给客户端。
两个重要的条件请求首部。引自《HTTP权威指南》
If-None-Match如果提供的实体标记与当前文档的标记不相符,就获取文档。
If-Modified-Since除非在某个指定的日期之后资源被修改过,否则就限制这个请求。
连续刷新一次,这次在过期时间内重新刷新页面。这一次缓存命中了,直接返回缓存的内容给客户端。
再看缓存服务器上缓存的文件
#ls-R*
4:
16
4/16:
ccea787b591b976d3e42861cf565d164
c:
33
c/33:
71c50b7fc36aaac8ea9ebc87ae37a33c
http://static/index.html取MD5值为71c50b7fc36aaac8ea9ebc87ae37a33chttp://static/favicon.ico取MD5值为ccea787b591b976d3e42861cf565d164
来看看71c50b7fc36aaac8ea9ebc87ae37a33c的内容
目录命名对MD5值倒取字符。即c/33
存储的是WEB1返回的结果,同时保留了ETag等首部信息。注意key是URL。
目录命名对MD5值倒取字符。即4/16
这个文件是取网站的favicon图标,因为找不到,所以返回到了backup的页面。
为什么不是404呢?
proxy_next_upstream设定为404错误,找下一个上游服务器,最后只能找到sorryserver了。
上游服务器WEB1和WEB2全部关闭,再次访问172.16.23.80/index.html。
缓存失效,去上游服务器获取页面,被调度到了sorryserver,返回的页面变化了,因此缓存服务器重新缓存页面后返回客户端。
连续刷新,缓存命中,直接将缓存内容返回客户端。
再次启用WEB2,访问172.16.23.80/index.html。
缓存失效,缓存服务器比较上游服务器,页面内容变化了,因此重新请求页面,缓存后返回客户端。
再次请求,缓存命中。
5.5.2动态页面测试
从前面的规划和配置中可知,动态内容没有经过缓存服务器,那么先访问172.16.23.80/index.php看看结果。
接上例,WEB1没有启用,因此对php访问被调度到了sorryserver。这是静态页面,返回ETag和Last-Modified首部。
再次刷新,返回依然如此,因为没有缓存。
启用WEB1的服务,返回了动态执行的结果。条件请求首部提交了也没有用处。
5.5.3动态页面缓存
修改代理服务器的配置
upstreamdynamic{
server192.168.23.88:8080;
server127.0.0.1:8080backup;
}
修改缓存服务器配置
重新装载代理服务器和缓存服务器的配置,然后测试。
这一次用户请求被调度到了缓存服务器,然后经由其调度到了WEB1,WEB1响应,缓存服务器缓存后返回给客户端。
1分钟后,再次请求,缓存已经过期,依然是WEB1响应,缓存服务器缓存后,返回客户端。
快速刷新页面,缓存命中,从缓存服务器直接返回客户端。
上图是缓存服务器缓存文件,可以看出动态页面执行的结果缓存了。
由此可见,动态内容也被缓存,并且缓存可以命中。
5.5.4动态内容缓存策略
如果页面是不变的,缓存整个页面是有意义的。其实这样动态页面的意义就不存在了,不如直接动态页面静态化更好。
如果页面中有部分内容是不断变化的,缓存整个页面是有问题的。ESI(Edgesideinclue)页面片段缓存技术,是由oracle提出的技术规格。这个结合Varnish使用会有较好的效果。其原理也是将网页静态内容部分缓存,ESI标记出来的内容从后端服务器请求后,和当前缓存内容合并后发给客户端。
6.总结
本文从LNMP演化到LNNNMP,使用Tengine(Nginx)充当多级架构中的不同角色,从而能窥探到整个网站架构的基本轮廓和思路,大型网站也是从这个模型再根据业务需要逐步完善的。
最后,通过对对静态、动态内容的缓存内容分析,从较为深入的层次理解缓存机制。动静资源可以实现不同的缓存机制,生产环境中要根据不同资源类型,合理配置缓存策略,减轻带宽压力,提高响应能力,有效提升用户体验。
本文出自“终南山下”博客,转载请与作者联系!
1.1Nginx简介
Nginx是一款由俄罗斯程序员IgorSysoev所开发轻量级的网页服务器、反向代理服务器以及电子邮件(IMAP/POP3)代理服务器。可以在目前所有主流的操作系统上运行。
Nginx采用模块化设计架构,易扩展;使用多线程处理客户请求,减少了进程上下文切换的开销;使用epoll或者kqueue事件驱动模型,提高了并发处理性能。
1.2Tengine介绍
Tengine是由淘宝网发起的Web服务器项目。它在Nginx的基础上,针对大访问量网站的需求,添加了很多高级功能和特性。
Tengine的性能和稳定性已经在大型的网站如淘宝网,天猫商城等得到了很好的检验。它的最终目标是打造一个高效、稳定、安全、易用的Web平台。
特性
继承Nginx-1.4.7的所有特性,100%兼容Nginx的配置;
动态模块加载(DSO)支持。加入一个模块不再需要重新编译整个Tengine;
支持SPDYv3协议,自动检测同一端口的SPDY请求和HTTP请求;
流式上传到HTTP后端服务器或FastCGI服务器,大量减少机器的I/O压力;
更加强大的负载均衡能力,包括一致性hash模块、会话保持模块,还可以对后端的服务器进行主动健康检查,根据服务器状态自动上线下线;
输入过滤器机制支持。通过使用这种机制Web应用防火墙的编写更为方便;
支持设置proxy、memcached、fastcgi、scgi、uwsgi在后端失败时的重试次数
动态脚本语言Lua支持。扩展功能非常高效简单;
支持管道(pipe)和syslog(本地和远端)形式的日志以及日志抽样;
支持按指定关键字(域名,url等)收集Tengine运行状态;
组合多个CSS、JavaScript文件的访问请求变成一个请求;
自动去除空白字符和注释从而减小页面的体积
自动根据CPU数目设置进程个数和绑定CPU亲缘性;
监控系统的负载和资源占用从而对系统进行保护;
显示对运维人员更友好的出错信息,便于定位出错机器;
更强大的防攻击(访问速度限制)模块;
更方便的命令行参数,如列出编译的模块列表、支持的指令等;
可以根据访问文件类型设置过期时间;
今天的实验将使用Tengine替代Nginx来搭建LNMP、LNNMP和LNNNMP。这三种架构后端都使用同样的PHP-FPM、MariaDB,这些服务器搭建参看以前的博文。
PHP-FPM,使用FastCGI协议和Nginx的WEB服务器通信。
MariadB,提供数据库服务器。
2.环境准备
2.1PHP-FPM
提供测试页面index.php
#mkdir/web/nginx
#vim/web/nginx/index.php
<html>
<head>
<title>dynamic</title>
</head>
<body>
<tableclass="tb_show">
<tr>
<td>ClientIPis</td>
<tdclass="content_show"><?phpechogetenv('REMOTE_ADDR').':'.getenv('REMOTE_PORT')?></td>
</tr>
<tr>
<td>ServerIPis</td>
<tdclass="content_show"><?phpechogetenv('SERVER_ADDR').':'.getenv('SERVER_PORT')?></td>
</tr>
<tr>
<tdcolspan="2">
<?php
mysql_connect("192.168.23.121","wp","wp")or
die("Couldnotconnect:".mysql_error());
printf("MySQLserverversion:%s\n",mysql_get_server_info());
?>
</td>
</tr>
</table>
</body>
</html>
2.2Tengine安装
#yum-yinstallgccopenssl-develpcre-devel
#groupadd-rnginx
#useradd-r-gnginx-s/bin/nologinnginx
编译安装前修改版本信息,让客户端无法看到真实的服务器信息和版本号。
#tarxftengine-2.0.3.tar.gz
#cdtengine-2.0.3
#sed-i\
-e's/\(#define[[:space:]]\+TENGINE_VER[[:space:]]\+\).*/\1"MeXP\/1.0.1"/'\
-e's/\(#define[[:space:]]\+NGINX_VER[[:space:]]\+\).*/\1"MeXP\/1.0.0"/'\
./src/core/nginx.h
#./configure--prefix=/usr/local/nginx\
--error-log-path=/var/log/nginx/error.log\
--http-log-path=/var/log/nginx/access.log\
--pid-path=/var/run/nginx/nginx.pid\
--lock-path=/var/lock/nginx.lock\
--user=nginx--group=nginx\
--with-http_ssl_module\
--with-http_flv_module\
--with-http_stub_status_module\
--with-http_gzip_static_module\
--http-client-body-temp-path=/usr/local/nginx/client\
--http-proxy-temp-path=/usr/local/nginx/proxy\
--http-fastcgi-temp-path=/usr/local/nginx/fcgi\
--http-uwsgi-temp-path=/usr/local/nginx/uwsgi\
--http-scgi-temp-path=/usr/local/nginx/scgi\
--with-pcre
#make&&makeinstall
#/usr/local/nginx/sbin/nginx-v
Tengineversion:MeXP/1.0.1(MeXP/1.0.0)
导出环境变量
#vim/etc/profile.d/nginx.sh
exportPATH=/usr/local/nginx/sbin:$PATH
#source/etc/profile.d/nginx.sh
3.LNMP
3.1规划
Nginx是WEB服务器。
3.2提供一个测试用index.html
#vim/usr/local/nginx/html/index.html
<html>
<head>
<title>dynamic</title>
</head>
<body>
<h1align="left">ThisisastaticpageofWEB1</h1>
</body>
</html>
3.3nginx配置文件
usernginx;
worker_processesauto;
#pid/var/run/nginx.pid;
error_log/var/log/nginx/error.log;
events{
useepoll;
worker_connections1024;
}
http{
includemime.types;
default_typeapplication/octet-stream;
log_formatmain'$remote_addr-$remote_user[$time_local]"$request"'
'$status$body_bytes_sent"$http_referer"'
'"$http_user_agent""$http_x_forwarded_for"';
access_log/var/log/nginx/access.logmain;
sendfileon;
#tcp_nopushon;
#keepalive_timeout0;
keepalive_timeout5;
#gzipon;
server{
listen80;
server_nameWEB1;
add_headerX-upSWEB1-$server_addr:$server_port;
#charsetkoi8-r;
#access_loglogs/host.access.logmain;
location/{
roothtml;
indexindex.htmlindex.htm;
}
#error_page404/404.html;
#redirectservererrorpagestothestaticpage/50x.html
#
error_page500502503504/50x.html;
location=/50x.html{
roothtml;
}
#passthePHPscriptstoFastCGIserverlisteningon127.0.0.1:9000
#
location~\.php${
#roothtml;
fastcgi_pass192.168.23.85:9000;
fastcgi_indexindex.php;
fastcgi_paramSCRIPT_FILENAME/web/nginx$fastcgi_script_name;
fastcgi_paramQUERY_STRING$query_string;
includefastcgi_params;
}
#denyaccessto.htaccessfiles,ifApache'sdocumentroot
#concurswithnginx'sone
#
#location~/\.ht{
#denyall;
#}
}
}
3.4SysV风格脚本
#!/bin/bash
#nginxStartupscriptfortheNginxHTTPServer
#version0.1.0
#chkconfig:-8515
#description:Nginxisahigh-performancewebandproxyserver.
#processname:nginx
#pidfile:/var/run/nginx.pid
#config:/usr/local/nginx/conf/nginx.conf
prog="nginx"
RETVAL=0
nginxd=/usr/local/nginx/sbin/nginx
nginx_config=/usr/local/nginx/conf/nginx.conf
nginx_pid=/var/run/nginx.pid
nginx_lock=/var/lock/subsys/nginx
#Sourcefunctionlibrary.
./etc/rc.d/init.d/functions
#Sourcenetworkingconfiguration.
./etc/sysconfig/network
#Checkthatnetworkingisup.
[${NETWORKING}="no"]&&exit0
[-x$nginxd]||exit0
#Startnginxdaemonsfunctions.
start()
{
if[-e$nginx_pid];then
echo"nginxalreadyrunning...."
exit1
fi
echo-n$"Starting$prog:"
daemon$nginxd-c${nginx_config}
RETVAL=$?
echo
[$RETVAL=0]&&touch$nginx_lock
return$RETVAL
}
#Stopnginxdaemonsfunctions.
stop()
{
echo-n$"Stopping$prog:"
killproc$nginxd
RETVAL=$?
echo
[$RETVAL=0]&&rm-f$nginx_lock$nginx_pid
}
reload(){
echo-n$"Reloading$prog:"
#kill-HUP`cat${nginx_pid}`
killproc$nginxd-HUP
RETVAL=$?
echo
}
case"$1"in
start)
start
;;
stop)
stop
;;
reload)
reload
;;
restart)
stop
start
;;
status)
status$prog
RETVAL=$?
;;
chkconf)
$nginxd-t
;;
*)
echo$"Usage:$prog{start|stop|restart|reload|status|chkconf|help}"
exit1
esac
exit$RETVAL
3.5运行nginx
#vim/etc/init.d/nginx
#chmod+x/etc/init.d/nginx
#chkconfig--addnginx
#chkconfig--listnginx
nginx0:off1:off2:off3:off4:off5:off6:off
#servicenginxstart
Startingnginx:[OK]
3.6测试
访问http://192.168.23.80/index.php,这个请求会通过FastCGI发送到后端的PHP-FPM服务器处理,然后php程序被执行,去连接后端的数据库,最后将执行结果返回Nginx的WEB服务器,由它返回给客户端浏览器。
从响应首部可以看到上游服务器的一些信息,X-upS是有意为之,以示区别。
4.LNNMP
Nginx的作用是反向代理服务器、WEB服务器。这是一个多级的架构。
4.1规划
4.2WEB2服务器
IP地址为192.168.23.81,监听TCP80端口。
Tengine的安装配置请参照LNMP的步骤。
配置文件如下:
usernginx;
worker_processesauto;
#pid/var/run/nginx.pid;
error_log/var/log/nginx/error.log;
events{
useepoll;
worker_connections1024;
}
http{
includemime.types;
default_typeapplication/octet-stream;
log_formatmain'$remote_addr-$remote_user[$time_local]"$request"'
'$status$body_bytes_sent"$http_referer"'
'"$http_user_agent""$http_x_forwarded_for"';
access_log/var/log/nginx/access.logmain;
sendfileon;
#tcp_nopushon;
#keepalive_timeout0;
keepalive_timeout5;
#gzipon;
server{
listen8080;
server_nameWEB2;
add_headerX-upSWEB2-$server_addr:$server_port;
#charsetkoi8-r;
#access_loglogs/host.access.logmain;
location/{
root/web/nginx/static;
indexindex.htmlindex.htm;
}
#error_page404/404.html;
#redirectservererrorpagestothestaticpage/50x.html
#
error_page500502503504/50x.html;
location=/50x.html{
roothtml;
}
}
}
创建页面根目录,并提供一个测试index.html,同时为了演示动静分离效果,提供样式表文件给WEB1服务器的index.php。
#mkdir-pv/web/nginx/static
#vim/web/nginx/static/index.html
<html>
<head>
<title>static</title>
</head>
<body>
<h1align="center">OK!Thisisastaticpage</h1>
</body>
</html>
#servicenginxstart
Startingnginx:[OK]
4.3代理服务器
安装请参照LNMP中的步骤。
IP地址:
对外:172.16.23.80:80/16对内:172.16.23.100:80/24
配置文件
工作在代理服务器模式
usernginx;
worker_processesauto;
#pid/var/run/nginx.pid;
error_log/var/log/nginx/error.log;
events{
useepoll;
worker_connections1024;
}
http{
includemime.types;
default_typeapplication/octet-stream;
log_formatmain'$remote_addr-$remote_user[$time_local]"$request"'
'$status$body_bytes_sent"$http_referer"'
'"$http_user_agent""$http_x_forwarded_for"';
access_log/var/log/nginx/access.logmain;
sendfileon;
#tcp_nopushon;
#keepalive_timeout0;
keepalive_timeout5;
#gzipon;
upstreamstatic{
server192.168.23.80;
server192.168.23.81:8080weight=2;
server127.0.0.1:8080backup;
}
upstreamdynamic{
server192.168.23.80;
server127.0.0.1:8080backup;
}
server{
listen80;
server_namelocalhost;
add_headerX-ProxyProxy-$server_addr:$server_port;
#charsetkoi8-r;
#access_loglogs/host.access.logmain;
location~\.php${
indexindex.php;
proxy_passhttp://dynamic;
}
location~\.(gif|jpg|jpeg|png|bmp|swf)${
expires30d;
}
location~\.(js|css)${
expires1h;
}
location/{
proxy_passhttp://static;
}
#error_page404/404.html;
#redirectservererrorpagestothestaticpage/50x.html
#
error_page500502503504/50x.html;
location=/50x.html{
roothtml;
}
}
server{
listen8080;
server_namesorry;
add_headerX-SorrySorryServer-$server_addr:$server_port;
location/{
roothtml;
rewrite.*/maintenance.htmlbreak;
}
}
}
注意监听8080端口的sorryserver的rewrite的写法。
提供本地8080端口BackupWEb服务器的维护页面
#vim/usr/local/nginx/html/maintenance.html
<html>
<head>
<title>Sorry</title>
</head>
<body>
<h1align="center">Downformaintenance</h1>
</body>
</html>
4.4测试
(1)静态资源的调度
可以看出对于静态资源的访问被调度到了不同的服务器上。
(2)动态网页的响应
可以从响应首部看到,客户端请求经过代理服务器被调度到了WEB1来响应。
(3)SorryServer
关闭WEB1和WEB2后,静态服务器组的backup服务器启用,通过代理服务器本地8080端口响应客户端请求,返回维护页面。
通过各种URL测试,包括动静资源的访问,所有客户端请求都被重写为对本地8080端口的sorryserver的maintenance.html页面的请求。
4.5综述
从以上的测试,可以看出,upstream中定义实现了动静分离的效果,对于动静资源的访问,应用不同的策略调度到了不同的上游服务器来响应。
当所有WEB服务停止响应的时候,还可以启用备用服务器提供临时服务。这样会有更好的用户体验。
5.LNNNMP
Nginx是反向代理、缓存、WEB服务器。
5.1前提
所有内网的服务器之间一定要时间同步,否则实验可能不能达到预期。
本实验在内网搭建了一台ntp服务器,所有主机启动ntpd,指向这一台ntp服务器。
5.2规划
在代理服务器和静态资源WEB服务器之间增加缓存服务器,缓存WEB1和WEB2的静态资源。
5.3代理服务器
修改配置文件
upstreamstatic{
server192.168.23.88;
server127.0.0.1:8080backup;
}
5.4缓存服务器
准备cache目录
#mkdir/data/nginx/webcache
#chown-Rnginx:nginx/data/nginx/webcache
缓存服务器配置
nginx配置
usernginx;
worker_processesauto;
#pid/var/run/nginx.pid;
error_log/var/log/nginx/error.log;
events{
useepoll;
worker_connections1024;
}
http{
includemime.types;
default_typeapplication/octet-stream;
log_formatmain'$remote_addr-$remote_user[$time_local]"$request"'
'$status$body_bytes_sent"$http_referer"'
'"$http_user_agent""$http_x_forwarded_for"';
access_log/var/log/nginx/access.logmain;
sendfileon;
#tcp_nopushon;
#keepalive_timeout0;
keepalive_timeout5;
#gzipon;
proxy_cache_path/data/nginx/webcachelevels=1:2
keys_zone=webcache:100minactive=24hmax_size=1g;
upstreamstatic{
server192.168.23.80;
server192.168.23.81:8080;
server192.168.23.100:8080backup;
}
server{
listen80;
server_nameCache;
add_headerX-Cache$upstream_cache_status;
location/{
proxy_next_upstreamerrortimeoutinvalid_header
http_500http_503http_404;
proxy_passhttp://static;
proxy_cachewebcache;
proxy_cache_valid2001m;
proxy_cache_valid30130210m;
proxy_cache_validany1m;
proxy_cache_use_staleoff;
}
}
}
说明:
该缓存服务器只接收代理服务器发来的对静态资源的请求,都交给缓存服务器,缓存服务器调度到某一台WEB服务器响应客户端请求。如果失败就转向backup服务器返回维护页面。
proxy_cache_path用来设置缓存的存储路径为/data/nginx/webcache,缓存使用2级目录。
proxy_cache_valid可以针对不同的响应状态码实现不同的缓存时长。为了方便测试将200设置为1分钟,正常情况下可以设置长一点,例如1天。
proxy_next_upstream指的是出现问题的情况下,就使用下一个上游服务器。
proxy_cache_use_stale指的是如果上游服务器出现某些问题,是否使用过期的缓存响应客户端,默认为off。可以考虑对于某些错误的时候开启,因为即使出现了问题,也可以将过期的缓存返回客户端,这样用户的体验比看到什么服务器错误好。
增加响应首部X-Cache,其内容填充为缓存状态,这是为了便于客户端观察。
5.5测试
5.5.1静态资源测试
访问172.16.23.80/index.html。
第一次测试
缓存没有命中,因为缓存中没有数据。E-tag为54138849-78。
隔了一分钟,再次访问,缓存服务器返回状态为EXPIRED即过期。这是因为对于状态码200的缓存过期时长为1分钟。但是客户端在If-None-Match中提交了E-tag即54138849-78。缓存服务器收到了这个请求后,会首先判断缓存是否过期,过期则访问上有服务器,文件并未改变,则刷新缓存文件的新鲜度,返回给客户端。
两个重要的条件请求首部。引自《HTTP权威指南》
If-None-Match如果提供的实体标记与当前文档的标记不相符,就获取文档。
If-Modified-Since除非在某个指定的日期之后资源被修改过,否则就限制这个请求。
连续刷新一次,这次在过期时间内重新刷新页面。这一次缓存命中了,直接返回缓存的内容给客户端。
再看缓存服务器上缓存的文件
#ls-R*
4:
16
4/16:
ccea787b591b976d3e42861cf565d164
c:
33
c/33:
71c50b7fc36aaac8ea9ebc87ae37a33c
来看看71c50b7fc36aaac8ea9ebc87ae37a33c的内容
目录命名对MD5值倒取字符。即c/33
存储的是WEB1返回的结果,同时保留了ETag等首部信息。注意key是URL。
目录命名对MD5值倒取字符。即4/16
这个文件是取网站的favicon图标,因为找不到,所以返回到了backup的页面。
为什么不是404呢?
proxy_next_upstream设定为404错误,找下一个上游服务器,最后只能找到sorryserver了。
上游服务器WEB1和WEB2全部关闭,再次访问172.16.23.80/index.html。
缓存失效,去上游服务器获取页面,被调度到了sorryserver,返回的页面变化了,因此缓存服务器重新缓存页面后返回客户端。
连续刷新,缓存命中,直接将缓存内容返回客户端。
再次启用WEB2,访问172.16.23.80/index.html。
缓存失效,缓存服务器比较上游服务器,页面内容变化了,因此重新请求页面,缓存后返回客户端。
再次请求,缓存命中。
5.5.2动态页面测试
从前面的规划和配置中可知,动态内容没有经过缓存服务器,那么先访问172.16.23.80/index.php看看结果。
接上例,WEB1没有启用,因此对php访问被调度到了sorryserver。这是静态页面,返回ETag和Last-Modified首部。
再次刷新,返回依然如此,因为没有缓存。
启用WEB1的服务,返回了动态执行的结果。条件请求首部提交了也没有用处。
5.5.3动态页面缓存
修改代理服务器的配置
upstreamdynamic{
server192.168.23.88:8080;
server127.0.0.1:8080backup;
}
修改缓存服务器配置
usernginx;
worker_processesauto;
#pid/var/run/nginx.pid;
error_log/var/log/nginx/error.log;
events{
useepoll;
worker_connections1024;
}
http{
includemime.types;
default_typeapplication/octet-stream;
log_formatmain'$remote_addr-$remote_user[$time_local]"$request"'
'$status$body_bytes_sent"$http_referer"'
'"$http_user_agent""$http_x_forwarded_for"';
access_log/var/log/nginx/access.logmain;
sendfileon;
#tcp_nopushon;
#keepalive_timeout0;
keepalive_timeout5;
#gzipon;
proxy_cache_path/data/nginx/webcachelevels=1:2
keys_zone=webcache:100minactive=24hmax_size=1g;
proxy_cache_path/data/nginx/phpcachelevels=1:2
keys_zone=phpcache:100minactive=24hmax_size=1g;
upstreamstatic{
server192.168.23.80;
server192.168.23.81:8080;
server192.168.23.100:8080backup;
}
upstreamdynamic{
server192.168.23.80;
server192.168.23.100:8080backup;
}
server{
listen80;
server_nameCache;
add_headerX-Cache$upstream_cache_status;
location/{
proxy_next_upstreamerrortimeoutinvalid_header
http_500http_503http_404;
proxy_passhttp://static;
proxy_cachewebcache;
proxy_cache_valid2001m;
proxy_cache_valid30130210m;
proxy_cache_validany1m;
proxy_cache_use_staleoff;
}
}
server{
listen8080;
server_nameCache;
add_headerX-Cache$upstream_cache_status;
location/{
proxy_passhttp://dynamic;
proxy_cachephpcache;
proxy_cache_valid2001m;
proxy_cache_valid30130210m;
proxy_cache_validany1m;
proxy_cache_use_staleerrortimeoutinvalid_header
http_500http_503http_404;
}
}
}
重新装载代理服务器和缓存服务器的配置,然后测试。
这一次用户请求被调度到了缓存服务器,然后经由其调度到了WEB1,WEB1响应,缓存服务器缓存后返回给客户端。
1分钟后,再次请求,缓存已经过期,依然是WEB1响应,缓存服务器缓存后,返回客户端。
快速刷新页面,缓存命中,从缓存服务器直接返回客户端。
上图是缓存服务器缓存文件,可以看出动态页面执行的结果缓存了。
由此可见,动态内容也被缓存,并且缓存可以命中。
5.5.4动态内容缓存策略
如果页面是不变的,缓存整个页面是有意义的。其实这样动态页面的意义就不存在了,不如直接动态页面静态化更好。
如果页面中有部分内容是不断变化的,缓存整个页面是有问题的。ESI(Edgesideinclue)页面片段缓存技术,是由oracle提出的技术规格。这个结合Varnish使用会有较好的效果。其原理也是将网页静态内容部分缓存,ESI标记出来的内容从后端服务器请求后,和当前缓存内容合并后发给客户端。
6.总结
本文从LNMP演化到LNNNMP,使用Tengine(Nginx)充当多级架构中的不同角色,从而能窥探到整个网站架构的基本轮廓和思路,大型网站也是从这个模型再根据业务需要逐步完善的。
最后,通过对对静态、动态内容的缓存内容分析,从较为深入的层次理解缓存机制。动静资源可以实现不同的缓存机制,生产环境中要根据不同资源类型,合理配置缓存策略,减轻带宽压力,提高响应能力,有效提升用户体验。
本文出自“终南山下”博客,转载请与作者联系!
相关文章推荐
- 使用Nginx实现HTTP动态负载均衡—《亿级流量网站架构核心技术》
- LNMP架构,phpf服务器和memcached缓存配置,openresty+memcached实现缓存
- LNMP基于FastCGI实现Nginx,PHP,MySQL的架构分离
- (转)nginx+redis实现接入层高性能缓存技术
- redis服务器环境下mysql实现lnmp架构缓存
- 业务架构平台的技术实现环境
- 业务架构平台的技术实现环境
- 业务架构平台的技术实现环境
- 业务架构平台的技术实现环境
- 缓存技术详谈和代码实现
- Spring技术内幕——深入解析Spring架构与设计原理(一)IOC实现原理
- 业务架构平台的技术实现环境
- Step1数据系统技术(3.使用浏览器Cache和http状态码304实现的客户端缓存)
- 基于VC6实现 GDI+下的双缓存技术
- 杂谈:一个小型WebMap项目的架构和技术实现(GmapAPI+jQuery+XML)
- VC无闪烁刷屏技术的实现(双缓存)
- 实现ASP缓存技术
- 业务架构平台的技术实现环境
- 利用.NET Remoting基础架构中的真实代理/透明代理技术实现了不针对具体类型、具体方法的通用方法调用拦截机制
- 双缓存技术在Symbian中的实现