Nginx图片下载不完整的处理过程


背景

昨天同事进行了nginx的迁移
然后晚上发现图片展示不全. 
自己其实之前遇到过类似的问题
但是因为熬夜比较久,脑子已经不转了.
所以花了接近半小时才理清楚.
感觉一些事情不记录一下, 无法加深印象.

问题现象

客户的nginx从之前的CentOS迁移到了新机房的银河麒麟v10SP3
基本功能可用,但是比较大的图片就显示不出来了. 
因为当时下午在补觉, 所以上会时间比较晚. 
上会之后看了下问题现场, 因为同期进行了网络切割
所以大家都在讨论网络层的问题. 
自己也一直没插上话.
等到网络问题讨论完, 我这边能插上话后.
让同事帮忙看了下 error log
里面发现了 permission denied的提示
然后很明显就可以定位到时Nginx的权限相关的问题 
跟同事说 可以修改用户 从 nobody 到 root 
或者是可以 修改一下 proxy_temp 的目录权限
都可以解决这个问题. 

同事为了简单, 修改了下 proxy_temp 的目录权限后问题解决. 

其实最初自己遇到过这个问题
也是因为安全加固之后的一些小的兼容性的问题
但是自己当时忘记整理了(因为解决的太畅快了)
以至于这次出问题耗费了半个多小时. 
感觉必须记录一下将原理搞清楚才可以.

关于nginx的proxy_temp

语法:proxy_buffering on|off
默认值:proxy_buffering on
上下文:http,server,location

作用:该指令开启从后端被代理服务器的响应body缓冲。
    如果proxy_buffering开启,nginx假定被代理的后端服务器会以最快速度响应,
    并把内容保存在由指令 proxy_buffer_size 和 proxy_buffers 指定的缓冲区里边.
    如果响应body无法放在内存里边,那么部分内容会被写到磁盘上。
    如果proxy_buffering被关闭了,那么响应body会按照获取body的多少立刻同步传送到客户端。
    nginx不尝试计算被代理服务器整个响应body的大小,nginx能从服务器接受的最大数据,是由指令 proxy_buffer_size指定的。
    对于基于长轮询(long-polling)的Comet 应用来说,关闭 proxy_buffering 是重要的,
    不然异步响应将被缓存导致Comet无法工作。
    但是无论proxy_buffering是否开启,proxy_buffer_size都是生效的

语法:proxy_buffers  数量  size
默认值:proxy_buffers 256 8k
上下文:http,server,location

作用:设置存储被代理服务器响应的body所占用的buffer个数和每个buffer大小。
    具体的意思是说,开辟256个长度为8k大小的read_buf用来存储body,
    当然不是连接建立初始化时就开辟256个,而是当当前buf不够存响应body时才会新申请一个,最多申请256个buf。

语法:proxy_buffer_size size
默认值:proxy_buffer_size 4k/8k
上下文:http,server,location

作用:Nginx使用该大小申请read_buf,即大小指定了 upstream header 最大长度,
如果响应头超过了这个长度,Nginx会报upstream sent too big header错误,然后client收到的是502。

语法:proxy_temp_path  path [level1 level2 level3]
默认值:proxy_temp_path proxy_temp
上下文:http,server,location
作用:定义proxy的临时文件存在目录以及目录的层级。

proxy_max_temp_file_size
语法:proxy_max_temp_file_size size;
默认值:proxy_max_temp_file_size 1024m;
上下文:http, server, location

关于解决问题的方法

同事用curl的方式高的
发现会在第100KB 进行截断
所以很明显应该是proxy_buffer size的设置.

其实解决问题的思路应该主要有三个:
1. 扩大proxy_buffer的大小. 保证能够在内存里面放的开
优点,性能最好, 缺点不能无限大, 并且会消耗过多的内存. 

2. 修改proxy_temp_path 的目录权限.
可以修改属主, 也可以修改目录权限, 保证nginx的worker进程可以有读写权限.

3. 修改nginx的运行用户.
如果不清楚具体目录,可以修改worker的用户给一个高一点权限的用户
这样也可以解决.

关于性能

一般认为 nginx 作为 proxy 的时候 对磁盘的读写压力不大.
但是在这种情况下, 一个是buffer size 一个是max_body_size
其实还是需要中转到磁盘内的. 

改大buffer区域肯定能够提供一定的性能. 
linux下采用 sendfile 这种COW的方式减少CPU的内存COPY来提高性能
还可以通过gzip 的方式进行 js,css,bmp 等文件格式的压缩
减少网络消耗

其实还可以增加一下内存文件系统. 比如 /dev 后者是 /tmp等
将临时文件放到内存文件系统的目录下.
并且增加特定权限 能够避免磁盘的拖累, 提高nginx的速度

当然前提是内存需要尽量大一点.
吃饱了的马才能使千里马.