[转]Nginx负载均衡原理初解

时间:2024-05-20 18:03:32

什么是负载均衡

我们知道单台服务器的性能是有上限的,当流量很大时,就需要使用多台服务器来共同提供服务,这就是所谓的集群。

负载均衡服务器,就是用来把经过它的流量,按照某种方法,分配到集群中的各台服务器上。这样一来不仅可以承担

更大的流量、降低服务的延迟,还可以避免单点故障造成服务不可用。一般的反向代理服务器,都具备负载均衡的功能。

负载均衡功能可以由硬件来提供,比如以前的F5设备。也可以由软件来提供,LVS可以提供四层的负载均衡(利用IP和端口),

Haproxy和Nginx可以提供七层的负载均衡(利用应用层信息)。

来看一个最简单的Nginx负载均衡配置。

  1. http {
  2. upstream cluster {
  3. server srv1;
  4. server srv2;
  5. server srv3;
  6. }
  7. server {
  8. listen 80;
  9. location / {
  10. proxy_pass http://cluster;
  11. }
  12. }
  13. }

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

此时使用的HTTP反向代理模块是ngx_http_proxy_module。

一般在upstream配置块中要指明使用的负载均衡算法,比如hash、ip_hash、least_conn。

这里没有指定,所以使用了默认的HTTP负载均衡算法 - 加权轮询。

负载均衡流程图

在描述负载均衡模块的具体实现前,先来看下它的大致流程:

[转]Nginx负载均衡原理初解

负载均衡模块 

Nginx目前提供的负载均衡模块:

ngx_http_upstream_round_robin,加权轮询,可均分请求,是默认的HTTP负载均衡算法,集成在框架中。

ngx_http_upstream_ip_hash_module,IP哈希,可保持会话。

ngx_http_upstream_least_conn_module,最少连接数,可均分连接。

ngx_http_upstream_hash_module,一致性哈希,可减少缓存数据的失效。

以上负载均衡模块的实现,基本上都遵循一套相似的流程。

1. 指令的解析函数

比如least_conn、ip_hash、hash指令的解析函数。

这些函数在解析配置文件时调用,主要用于:

检查指令参数的合法性

指定peer.init_upstream函数指针的值,此函数用于初始化upstream块。

2. 初始化upstream块

在执行完指令的解析函数后,紧接着会调用所有HTTP模块的init main conf函数。

在执行ngx_http_upstream_module的init main conf函数时,会调用所有upstream块的初始化函数,

即在第一步中指定的peer.init_upstream,主要用于:

创建和初始化后端集群,保存该upstream块的数据

指定peer.init,此函数用于初始化请求的负载均衡数据

来看下ngx_http_upstream_module。

  1. ngx_http_module_t ngx_http_upstream_module_ctx = {
  2. ...
  3. ngx_http_upstream_init_main_conf, /* init main configuration */
  4. ...
  5. };
  1. static char *ngx_http_upstream_init_main_conf(ngx_conf_t *cf, void *conf)
  2. {
  3. ...
  4. /* 数组的元素类型是ngx_http_upstream_srv_conf_t */
  5. for (i = 0; i < umcf->upstreams.nelts; i++) {
  6. /* 如果没有指定upstream块的初始化函数,默认使用round robin的 */
  7. init = uscfp[i]->peer.init_upstream ? uscfp[i]->peer.init_upstream :
  8. ngx_http_upstream_init_round_robin;
  9. if (init(cf, uscfp[i] != NGX_OK) {
  10. return NGX_CONF_ERROR;
  11. }
  12. }
  13. ...
  14. }

3. 初始化请求的负载均衡数据块

当收到一个请求后,一般使用的反向代理模块(upstream模块)为ngx_http_proxy_module,

其NGX_HTTP_CONTENT_PHASE阶段的处理函数为ngx_http_proxy_handler,在初始化upstream机制的

函数ngx_http_upstream_init_request中,调用在第二步中指定的peer.init,主要用于:

创建和初始化该请求的负载均衡数据块

指定r->upstream->peer.get,用于从集群中选取一台后端服务器(这是我们最为关心的)

指定r->upstream->peer.free,当不用该后端时,进行数据的更新(不管成功或失败都调用)

请求的负载均衡数据块中,一般会有一个成员指向对应upstream块的数据,除此之外还会有自己独有的成员。

"The peer initialization function is called once per request. 
It sets up a data structure that the module will use as it tries to find an appropriate
backend server to service that request; this structure is persistent across backend re-tries,
so it's a convenient place to keep track of the number of connection failures, or a computed
hash value. By convention, this struct is called ngx_http_upstream_<module_name>_peer_data_t."

4. 选取一台后端服务器

一般upstream块中会有多台后端,那么对于本次请求,要选定哪一台后端呢?

这时候第三步中r->upstream->peer.get指向的函数就派上用场了:

采用特定的算法,比如加权轮询或一致性哈希,从集群中选出一台后端,处理本次请求。

选定后端的地址保存在pc->sockaddr,pc为主动连接。

函数的返回值:

NGX_DONE:选定一个后端,和该后端的连接已经建立。之后会直接发送请求。

NGX_OK:选定一个后端,和该后端的连接尚未建立。之后会和后端建立连接。

NGX_BUSY:所有的后端(包括备份集群)都不可用。之后会给客户端发送502(Bad Gateway)。

5. 释放一台后端服务器

当不再使用一台后端时,需要进行收尾处理,比如统计失败的次数。

这时候会调用第三步中r->upstream->peer.free指向的函数。

函数参数state的取值:

0,请求被成功处理

NGX_PEER_FAILED,连接失败

NGX_PEER_NEXT,连接失败,或者连接成功但后端未能成功处理请求

一个请求允许尝试的后端数为pc->tries,在第三步中指定。当state为后两个值时:

如果pc->tries不为0,需要重新选取一个后端,继续尝试,此后会重复调用r->upstream->peer.get。

如果pc->tries为0,便不再尝试,给客户端返回502错误码(Bad Gateway)。

在upstream模块的回调

负载均衡模块的功能是从后端集群中选取一台后端服务器,而具体的反向代理功能是由upstream模块实现的,

比如和后端服务器建立连接、向后端服务器发送请求、处理后端服务器的响应等。

我们来看下负载均衡模块提供的几个钩子函数,是在upstream模块的什么地方回调的。

Nginx的HTTP反向代理模块为ngx_http_proxy_module,其NGX_HTTP_CONTENT_PHASE阶段的处理函数为

ngx_http_proxy_handler,每个请求的upstream机制是从这里开始的。

  1. ngx_http_proxy_handler
  2. ngx_http_upstream_create /* 创建请求的upstream实例 */
  3. ngx_http_upstream_init /* 启动upstream机制 */
  4. ngx_htp_upstream_init_request /* 负载均衡模块的入口 */
  5. uscf->peer.init(r, uscf) /* 第三步,初始化请求的负载均衡数据块 */
  6. ...
  7. ngx_http_upstream_connect /* 可能会被ngx_http_upstream_next重复调用 */
  8. ngx_event_connect_peer(&u->peer); /* 连接后端 */
  9. pc->get(pc, pc->data); /* 第四步,从集群中选取一台后端 */
  10. ...
  11. /* 和后端建连成功后 */
  12. c = u->peer.connection;
  13. c->data = r;
  14. c->write->handler = ngx_http_upstream_handler; /* 注册的连接的读事件处理函数 */
  15. c->read->handler = ngx_http_upstream_handler; /* 注册的连接的写事件处理函数 */
  16. u->write_event_handler = ngx_http_upstream_send_request_handler; /* 写事件的真正处理函数 */
  17. u->read_event_handler = ngx_http_upstream_process_header; /* 读事件的真正处理函数 */

选定后端之后,在和后端通信的过程中如果发生了错误,会调用ngx_http_upstream_next来继续尝试其它的后端。

  1. ngx_http_upstream_next
  2. if (u->peer.sockaddr) {
  3. if (ft_type == NGX_HTTP_UPSTREAM_FT_HTTP_403 ||
  4. ft_type == NGX_HTTP_UPSTREAM_FT_HTTP_404)
  5. state = NGX_PEER_NEXT;
  6. else
  7. state = NGX_PEER_FAILED;
  8. /* 第五步,释放后端服务器 */
  9. u->peer.free(&u->peer, u->peer.data, state);
  10. u->peer.sockaddr = NULL;
  11. }

Reference

[1]. http://www.evanmiller.org/nginx-modules-guide.html#proxying

[2]. http://tengine.taobao.org/book/chapter_05.html#id5

---------------------

下载nginx源码包,编译nginx需要指定pcre,zlib,openssl,到官网上下载源代码包:
http://www.zlib.NET/
http://www.openssl.org/
http://www.pcre.org/

将这三个包下载放到/opt目录,tar -xzvf *.gz解压,然后也将nginx-0.6.32的包解压到/opt目录下,进入nginx目录,执行:
#./configure --sbin-path=/usr/local/sbin --with-http_ssl_module --with-pcre=../pcre-7.7 --with-zlib=../zlib-1.2.3 --with-openssl=../openssl-0.9.8g
#make && make install

如果在./configure时出现在错误,可能是没有安装gcc g+编译器的缘故。单独安装这两个编译器比较麻烦,ubuntu提供了build-essential来安装gcc和g++编译器

apt-get install build-essential

采用aptitude安装的时候,系统会将所有依赖的包都一并装上,但是使 用源码编译的时候就没这么智能了,我们需要找到需要的依赖包,然后手工安装,幸运的是,这并不复杂,也不多,例如pcre, ssl 和zlib,安装方法比较简单:

sudo aptitude install libpcre3 libpcre3-dev libpcrecpp0 libssl-dev zlib1g-dev

三、安装Nginx

ok,准备工作做完了,是时候开始下载、 安装Nginx了。

1、下载源代码

到Nignx官网去下载源代码

2、 解压

tar -zxvf nginx-0.6.31.tar.gz
...
cd nginx-0.6.31/

3、选择需要的编译选项
Nginx的编译选项还是不少 的,基本上都是定制位置和模块,分别是:
1)--sbin-path=/usr/local/sbin
从源码编译Nginx会安装在/usr/local/nginx目录下(你可以使用参数--prefix=<path>指定自己需要的位置),然后会将bin文件放在/usr/local/nginx/sbin/nginx,虽然比较清晰,但是和我们一般的习惯不同

(作为程序员的我们更习惯在/usr/local /sbin下寻找bin文件);

2)--with-http_ssl_module
主要是提供https访问支持的。

5、 Compile

./configure --sbin-path=/usr/local/sbin --with-http_ssl_module

编 译的时间不会很长,完成的时候你会在屏幕上看到类似下面的信息:

...
nginx path prefix: "/usr/local/nginx"
nginx binary file: "/usr/local/sbin"
nginx configuration file: "/usr/local/nginx/conf/nginx.conf"
...

留 意上面的提示,涉及到nginx的几个重要文件的路径信息。

6、Make && make install
差不多快完成了,现在make一下。

make

sudo make install

在使用make install 命令时可能会提示当前执行的命令权限不够,sudo su命令然后输入密码提升下权限就ok

7、chmod(可选)
还需要给bin文件有执行权限,也比较简单:

chmod +x /usr/local/sbin/nginx

8、Nginx重要文件安装目录

默认nginx安装的目录在/usr/local/nginx下,包括:
/usr/local/nginx/sbin #nginx启动文件
/usr/local/nginx/conf #配置文件
/usr/local/nginx/html #默认网页文件
/usr/local/nginx/logs #日志文件

四、使用

安装完成后,试着使用下。
1、启动

sudo /usr/local/sbin/nginx

然后把自己的浏览器导航到http://IP就可 以看到默认的欢迎界面了

Welcome to nginx!

If you see this page, the nginx web server is successfully installed andworking. Further configuration is required.

For online documentation and support please refer tonginx.org.
Commercial support is available atnginx.com.

Thank you for using nginx.

2、停止

   sudo /usr/local/sbin/nginx-s stop


六、附录:Compile-time options

(原文参考:http://wiki.codemongers.com/NginxInstallOptions )

configure 脚本确定系统所具有一些特性,特别是 nginx 用来处理连接的方法。然后,它创建 Makefile 文件。

configure 支持下面的选项:

1)目录属性

--prefix=<path> - Nginx安装路径。如果没有指定,默认为 /usr/local/nginx。

--sbin-path=<path> - Nginx可执行文件安装路径。只能安装时指定,如果没有指定,默认为<prefix>/sbin/nginx。

--conf-path=<path> - 在没有给定-c选项下默认的nginx.conf的路径。如果没有指定,默认为<prefix>/conf/nginx.conf。

--pid-path=<path> - 在nginx.conf中没有指定pid指令的情况下,默认的nginx.pid的路径。如果没有指定,默认为 <prefix>/logs/nginx.pid。

--lock-path=<path> - nginx.lock文件的路径,默认为<prefix>/logs/nginx.lock

--error-log-path=<path> - 在nginx.conf中没有指定error_log指令的情况下,默认的错误日志的路径。如果没有指定,默认为 <prefix>/logs/error.log。

--http-log-path=<path> - 在nginx.conf中没有指定access_log指令的情况下,默认的访问日志的路径。如果没有指定,默认为 <prefix>/logs/access.log。

--user=<user> - 在nginx.conf中没有指定user指令的情况下,默认的nginx使用的用户。如果没有指定,默认为 nobody。

--group=<group> - 在nginx.conf中没有指定user指令的情况下,默认的nginx使用的组。如果没有指定,默认为 nobody。

--builddir=DIR - 指定编译的目录

2)模块

--with-rtsig_module - 启用 rtsig 模块

--with-select_module --without-select_module -允许或不允许开启SELECT模式,如果 configure 没有找到更合适的模式,比如:kqueue(sun os),epoll (Linux kenel 2.6+), rtsig(实时信号)或者/dev/poll(一种类似select的模式,底层实现与SELECT基本相 同,都是采用轮训方法) SELECT模式将是默认安装模式

--with-poll_module --without-poll_module - Whether or not to enable the poll module. This module is enabled by default if a more suitable method such as kqueue, epoll, rtsig or /dev/poll is not discovered by configure.

--with-http_ssl_module -开启HTTP SSL模块,使NGINX可以支持HTTPS请求。这个模块需要已经安装了OPENSSL,在DEBIAN上是libssl

--with-http_realip_module - 启用 ngx_http_realip_module

--with-http_addition_module - 启用 ngx_http_addition_module

--with-http_sub_module - 启用 ngx_http_sub_module

--with-http_dav_module - 启用 ngx_http_dav_module

--with-http_flv_module - 启用 ngx_http_flv_module

--with-http_stub_status_module - 启用 "server status" 页

--without-http_charset_module - 禁用 ngx_http_charset_module

--without-http_gzip_module - 禁用 ngx_http_gzip_module. 如果启用,需要 zlib 。

--without-http_ssi_module - 禁用 ngx_http_ssi_module

--without-http_userid_module - 禁用 ngx_http_userid_module

--without-http_access_module - 禁用 ngx_http_access_module

--without-http_auth_basic_module - 禁用 ngx_http_auth_basic_module

--without-http_autoindex_module - 禁用 ngx_http_autoindex_module

--without-http_geo_module - 禁用 ngx_http_geo_module

--without-http_map_module - 禁用 ngx_http_map_module

--without-http_referer_module - 禁用 ngx_http_referer_module

--without-http_rewrite_module - 禁用 ngx_http_rewrite_module. 如果启用需要 PCRE 。

--without-http_proxy_module - 禁用 ngx_http_proxy_module

--without-http_fastcgi_module - 禁用 ngx_http_fastcgi_module

--without-http_memcached_module - 禁用 ngx_http_memcached_module

--without-http_limit_zone_module - 禁用 ngx_http_limit_zone_module

--without-http_empty_gif_module - 禁用 ngx_http_empty_gif_module

--without-http_browser_module - 禁用 ngx_http_browser_module

--without-http_upstream_ip_hash_module - 禁用 ngx_http_upstream_ip_hash_module

--with-http_perl_module - 启用 ngx_http_perl_module

--with-perl_modules_path=PATH - 指定 perl 模块的路径

--with-perl=PATH - 指定 perl 执行文件的路径

--http-log-path=PATH - Set path to the http access log

--http-client-body-temp-path=PATH - Set path to the http client request body temporary files

--http-proxy-temp-path=PATH - Set path to the http proxy temporary files

--http-fastcgi-temp-path=PATH - Set path to the http fastcgi temporary files

--without-http - 禁用 HTTP server

--with-mail - 启用 IMAP4/POP3/SMTP 代理模块

--with-mail_ssl_module - 启用 ngx_mail_ssl_module

--with-cc=PATH - 指定 C 编译器的路径

--with-cpp=PATH - 指定 C 预处理器的路径

--with-cc-opt=OPTIONS - Additional parameters which will be added to the variable CFLAGS. With the use of the system library PCRE in FreeBSD, it is necessary to indicate --with-cc-opt="-I /usr/local/include". If we are using select() and it is necessary to increase the number of file descriptors, then this also can be assigned here: --with-cc-opt="-D FD_SETSIZE=2048".

--with-ld-opt=OPTIONS - Additional parameters passed to the linker. With the use of the system library PCRE in FreeBSD, it is necessary to indicate --with-ld-opt="-L /usr/local/lib".

--with-cpu-opt=CPU - 为特定的 CPU 编译,有效的值包括:pentium, pentiumpro, pentium3, pentium4, athlon, opteron, amd64, sparc32, sparc64, ppc64

--without-pcre - 禁止 PCRE 库的使用。同时也会禁止 HTTP rewrite 模块。在 "location" 配置指令中的正则表达式也需要 PCRE 。

--with-pcre=DIR - 指定 PCRE 库的源代码的路径。

--with-pcre-opt=OPTIONS - Set additional options for PCRE building.

--with-md5=DIR - Set path to md5 library sources.

--with-md5-opt=OPTIONS - Set additional options for md5 building.

--with-md5-asm - Use md5 assembler sources.

--with-sha1=DIR - Set path to sha1 library sources.

--with-sha1-opt=OPTIONS - Set additional options for sha1 building.

--with-sha1-asm - Use sha1 assembler sources.

--with-zlib=DIR - Set path to zlib library sources.

--with-zlib-opt=OPTIONS - Set additional options for zlib building.

--with-zlib-asm=CPU - Use zlib assembler sources optimized for specified CPU, valid values are: pentium, pentiumpro

--with-openssl=DIR - Set path to OpenSSL library sources

--with-openssl-opt=OPTIONS - Set additional options for OpenSSL building

--with-debug - 启用调试日志

--add-module=PATH - Add in a third-party module found in directory PAT

---------------------

前言:

由于本人工作原因,涉及到网络直播领域,其中视频的回放下载,涉及到了一些视频下载方面的技术。针对于一个完整视频的下载,目前市面上的主流做法是,先将整个视频流切片,存储到文件服务器中,在用户需要观看回放视频时。通过一个视频回源服务器,去文件服务器中逐个请求切片,返回给用户播放。
今天着重探讨的是关于回源服务器缓存的配置以及合理的缓存策略。

通过给回源服务器配置缓存的案例,详细讲解一整套缓存配置机制,并且可沿用到其他任何缓存配置场景中。

今天的讲解分为四点:
  • 回源服务器的工作是啥
  • 为啥需要给回源服务器加缓存
  • 如何配置缓存
  • 如何针对业务场景配置完备的缓存机制

回源服务器的工作:

回源服务器在下面叙述中简称:源站
如图所示,在文件下载的过程中,横跨在cdn与文件服务器之间,作为下载枢纽。

[转]Nginx负载均衡原理初解

源站架构:源站是nginx+PHP的webserver架构,如图所示:

[转]Nginx负载均衡原理初解

但如果源站只是简单的收到请求,然后下载资源,再返回,势必会存在以下几点不够优化的问题:
1、cdn可能存在多次回源现象
2、源站对同一资源的多次下载,存在网络流量带宽浪费,以及不必要的耗时。
所以为了优化这些问题,需要给源站做一层缓存。缓存策略采用nginx自带的proxy_cache模块。

proxy_cache原理:

proxy_cache模块的工作原理如图所示:
[转]Nginx负载均衡原理初解

如何配置proxy_cache模块

在nginx.conf文件中添加如下代码:
  1. http{
  2. ......
  3. proxy_cache_path/data/nginx/tmp-test levels=1:2 keys_zone=tmp-test:100m inactive=7d max_size=1000g;
  4. }
代码说明:

proxy_cache_path 缓存文件路径

levels 设置缓存文件目录层次;levels=1:2 表示两级目录

keys_zone 设置缓存名字和共享内存大小

inactive 在指定时间内没人访问则被删除

max_size 最大缓存空间,如果缓存空间满,默认覆盖掉缓存时间最长的资源。

当配置好之后,重启nginx,如果不报错,则配置的proxy_cache会生效

查看  proxy_cache_path /data/nginx/目录,
会发现生成了tmp-test文件夹。

如何使用proxy_cache

在你对应的nginx vhost server配置文件中添加如下代码:
  1. location /tmp-test/ {
  2. proxy_cache tmp-test;
  3. proxy_cache_valid  200 206 304 301 302 10d;
  4. proxy_cache_key $uri;
  5. proxy_set_header Host $host:$server_port;
  6. proxy_set_header X-Real-IP $remote_addr;
  7. proxy_set_header   X-Forwarded-For $proxy_add_x_forwarded_for;
  8. proxy_passhttp://127.0.0.1:8081/media_store.php/tmp-test/;
  9. }
配置项介绍:
Proxy_cache tmp-test 使用名为tmp-test的对应缓存配置

proxy_cache_valid  200 206 304 301 302 10d; 对httpcode为200…的缓存10天

proxy_cache_key $uri  定义缓存唯一key,通过唯一key来进行hash存取

proxy_set_header  自定义http header头,用于发送给后端真实服务器。

proxy_pass  指代理后转发的路径,注意是否需要最后的/
到这里,最基本的proxy_cache功能就配置成功了。当uri成功匹配到该location,则proxy_cache就会生效。

添加proxy_cache之后,请求过程的变化:

1、第一次访问:

[转]Nginx负载均衡原理初解
第一次访问,proxy_cache并没有找到对应的缓存文件(未命中缓存MISS),所以当第一次请求完成的同时,proxy_cache会保持缓存:
2、保存缓存,如图所示:
[转]Nginx负载均衡原理初解

3、同一个url第二次访问,当同一个文件再次到达源站,proxy_cache就会找到其对应的缓存文件(命中缓存HIT)直接返回给请求端,无需再执行php程序,如图所示:

[转]Nginx负载均衡原理初解

提出疑问:

到此,就完成了最基本的proxy_cache配置和访问过程介绍,但是最基本的配置,往往无法满足我们的业务需求,我们往往会提出以下几点疑问和需求:
  1. 需要主动清理缓存文件
  2. 写入路径为一块磁盘,如果磁盘打满该怎么解决?
  3. 如何让源站支持断点续传,以及断点续传的缓存策略
  4. 如果请求端 range 请求(分片下载)一个大资源,同样的uri,如何区别请求?
  5. 还需要告诉请求端,资源的过期时间
  6. 日志统计,如何配置命中与不命中字段,如何做统计?
面对以上疑问,我们一个一个解决。

问题一:主动清理缓存

采用:nginx  proxy_cache_purge 模块 ,该模块与proxy_cache成对出现,功能正好相反。
设计方法:在nginx中,另启一个server,当需要清理响应资源的缓存时,在本机访问这个server。
例如:
访问 127.0.0.1:8083/tmp-test/TL39ef7ea6d8e8d48e87a30c43b8f75e30.txt 即可清理该资源的缓存文件。
配置方法:
  1. location /tmp-test/ {
  2. allow 127.0.0.1; //只允许本机访问
  3. deny all; //禁止其他所有ip
  4. proxy_cache_purge tmp-test $uri;  //清理缓存
  5. }
proxy_cache_purge:缓存清理模块
tmp-test:指定的key_zone
$uri:指定的生成key的参数
proxy_cache_purge缓存清理过程,如图所示:

[转]Nginx负载均衡原理初解

问题二:缓存文件强磁盘打满该怎么办?

由于写入路径为一个单一目录,只能写入一块磁盘。一块磁盘很快就会被打满,解决该问题有如下两种方法:
1、将多块磁盘做磁盘阵列? 缺点是:减小了实际的存储空间。
2、巧妙得运用proxy_cache_path的目录结构,由于levels=1:2,这导致缓存文件的目录结构为两层,每层目录名,都是由hash函数生成。如图所示:

[转]Nginx负载均衡原理初解

总共含有16*16*16=4096个文件目录。对该一级目录进行软连接,分别将0-f软连接到你所需要的指定磁盘目录上,如图所示:
[转]Nginx负载均衡原理初解
通过软链的方法,实现:将不同盘下的目录作为真正存放数据的路径,解决了多盘利用,单盘被打满的问题。

问题三:支持range(断点续传)

添加上缓存代理之后,客户端发起的range请求将会失效,如下图所示:

[转]Nginx负载均衡原理初解

导致range参数无法传递到下一级的原因如下:
当缓存代理转发http请求到后端服务器时,http header会改变,header中的部分参数,会被取消掉。其中range参数被取消,导致,后端nginx服务器没有收到range参数,最终导致这个分片下载不成功。所以需要对代理转发的header进行配置。
例如:
  1. location /tmp-test/ {
  2. proxy_cache tmp-test;
  3. proxy_cache_valid  200 206 304 301 302 10d;
  4. proxy_cache_key $uri;
  5. <span style="color:#ff0000;">proxy_set_header Range $http_range;</span>
  6. proxy_pass http://127.0.0.1:8081/media_store.php/tmp-test/;
  7. }
红色部分的含义:将http请求中的range值($http_range)放到代理转发的http请求头中作为参数range的值。

问题四,当支持range加载后,proxy_cache_key,则需要重新配置:

如果请求端 Range请求(分片下载)一个大资源,同样的uri,proxy cache如何识别资源对应的key。
由于nginx配置为:proxy_cache_key $uri,用uri作为key
所以当请求为普通请求和range请求时,都是同样的uri作为key。proxy_cache将有可能导致错误返回。如下图所示:

[转]Nginx负载均衡原理初解

解决方法如下:
修改proxy_cache_key ,配置proxy_cache_key $http_range$uri;
这样就能解决:key唯一性。可以避免不管是正常请求还是不同的range请求,第一次获取的内容和之后获取的缓存内容都不会出现异常。

问题五:如何配置-返回过期时间

需要通过返回过期时间来指定请求端,哪些资源需要缓存,哪些资源不缓存,
参数 正常请求 range请求
返回过期时间 返回 不返回
为了防止请求端将分片资源当做完整资源缓存起来,我们需要对正常请求,返回过期时间;对range请求, 不返回过期时间。
解决该问题,通过对nginx配置即可解决:
  1. location /media_store.php {
  2. fastcgi_pass   127.0.0.1:9000;
  3. fastcgi_index  media_store.php;
  4. fastcgi_param  SCRIPT_FILENAME  $document_root/$fastcgi_script_name;
  5. include        fastcgi_params;
  6. if ( $http_range = ''){
  7. expires 2592000s;
  8. }
  9. }
在proxy_pass代理之后的location中加入对$http_range的判断,expires 表示过期时间。 2592000s指缓存过期时间。

问题七:缓存命中情况如何在http头中体现,以及在nginx日志中查看

解决方法:
利用nginx $upstream_cache_status变量:该变量代表缓存命中的状态,
如果命中,为HIT;如果未命中,为MISS
在返回nginx server配置中添加:
add_header  Nginx-Cache "$upstream_cache_status";
在nginxlog中添加:
log_format       combinedio  …$upstream_cache_status;
http返回head截图:

[转]Nginx负载均衡原理初解

nginx log日志截图:
[转]Nginx负载均衡原理初解

总结:

整个一套完备的缓存策略就介绍到此,这套方案中不仅实现了基本的缓存配置,还解决了实际场景应用中会遇到的,磁盘扩展,缓存清理,断点续传,缓存过期时间,缓存命中提示等问题,只要将这套方案灵活运用,不管是再复杂的场景,基本都能满足需求。以上都是我在工作中爬过的坑,不断完善总结出的结果,希望对读者能有帮助。