高并发下 php 输出图像对性能开销大不大?

2016-12-30 19:41:46 +08:00
 alwayshere

目前服务器是这样的: cpu 太差,是 ATOM 的 cpu ,带宽是 100M 独享,硬盘太小,而且是机械硬盘,内存 4G 。 服务器需要裁剪图像(平均 4M 大小)输出到浏览器端,因为硬盘太小,如果把每个图片都裁剪成各种分辨率存到硬盘的话,硬盘装不下,我目前想到两种方案:

1.客户端浏览器 canvas 裁剪:

输出原始图像到客户端,用 js 配合 canvas 最终生成 base64 图像。

优点:不费硬盘、不费内存

缺点:带宽估计消耗很大,用户等待时间较长,即使一个 100x100 的图像都要等很长,搜索引擎抓取不到这张图像

2.php 动态输出:

php 在客户端进行裁剪,rewrite伪装成静态地址如www。abc。com/images/little-cat-480x800.jpg:

//php 裁剪原始图像得到$content;

header('Content-Type:image/jpeg');

//输出裁剪后的图片内容$content;

这样做的好处:不费硬盘、不费带宽

缺点:费内存和 cpu ,在高并发下不知道表现如何?

项目实际经验太少,求教 V 站高手解疑一下,哪种更好?或者有更好的方案?谢谢

3075 次点击
所在节点    程序员
11 条回复
duola
2016-12-30 19:47:57 +08:00
这是硬伤,寄硬盘跟 CPU 吧,这是最好的解决方案,反正再在硬盘和 CPU 也不贵。
incompatible
2016-12-30 19:48:55 +08:00
用七牛之类的云存储,可以以很低的成本解决你的问题。
alwayshere
2016-12-30 19:49:32 +08:00
@incompatible 自己算了下,还是自己搭建图片服务器更实惠点
alwayshere
2016-12-30 19:53:16 +08:00
@duola 目前硬盘只有 1T ,裁剪成各种分辨率再拿其他服务器来装,花销挺大的
gouchaoer
2016-12-30 20:12:19 +08:00
php 有 glide 专门裁剪图片,其实图片如果反复访问的话第一次裁剪才耗 cpu ,第 n 次访问直接 nginx 读文件输出就完了
pubby
2016-12-30 21:29:38 +08:00
2006 年我也是用你的方案,存的是原图, php 动态裁剪,前面加 squid 缓存
2009 年改成 fastcgi
2014 年用 go 重写,改成分布式处理
z5864703
2016-12-30 22:25:10 +08:00
动态裁剪+缓存。这样不用所有图都进行裁剪存储,然后缓存可以设置有效期。过期自动删除缓存,这样磁盘利用率就可以提高很多了
mko0okmko0
2016-12-31 00:58:23 +08:00
如果是 JPG 就可以用贱招.
首先你只要存原始尺寸图片.但一定要是 JPG 渐进式格式.存档品质不要超过 85 比较好.这个 php 本身就有支援.
然后重点.输出的时候用串流输出. 意思就是 php 读取图片用串流(BYTE)的.不要解读图片
然后 php 控制输出也是串流的.但输出多少要自己控制.
例如你只要 4 分之 1 的大小=长宽减半的大小.
你就先取得整个档案的大小.计算 4 分之一的档案长度.
然后串流输出的时候.到达 4 分之 1 的输出后.直接截断.关闭串流.
用户端需要稍微用 html 控制一下.直接指定显示的长宽.
因为实际上你输出的还是原始尺寸.
而对方收到的图片清晰度却没有达到全部.但一定超过 4 分之一的需求.
所以看不出有问题.但计算消耗只有第一次的存档.输出频宽消耗也只有 4 分之 1.
验证方法是随便产出一些 渐进式品质 75~85 的 JPG 图片.直接用档案切割软体或是 16 进制的编辑软体截断档案.
然后将截断的前面用看图软体打开.
用 4 分之一大小看基本上没问题.但用原始尺寸看就可以看到破损.
有研究 JPEG 格式就知道.渐进式的话.他有一个一个的段落.每个段落都会让图片慢慢清晰.而且后面的不存在也没关系.只是解码会终止.但可以正常显示前面.
如果有搭配 imagemagick 或类似的软体存档.可以给他更多参数.

例如黑白灰(YUV 的亮度 Y)正确性比彩色(UV 部分)重要.下参数让 Y 全部在前然后才是 UV.
他就会变成 YYYYUUUUVVVV(第一层.最模糊) YYYYUUUUVVVV (第 2 层某一块.是第 1 层的局部补正) YYYYUUUUVVVV (第 2 层某一块.是第一层的局部补正) YYYYUUUUVVVV (第 3 层某一块.是第 2 层的局部补正) YYYYUUUUVVVV (第 3 层某一块.是第 2 层的局部补正) .....直到最后一层.

如过颜色比较重要那就下参数让 YUV 混合.就变成 YUVYUVYUVYUV(第 1 层) YUVYUVYUVYUVYUVYUVYUVYUV(第 2 层)....以此类推.

每一层有几块是编码器决定的.而且还分算数二进算法或是金字塔算法.所以不好计算你要的尺寸要切到哪一层.干脆直接截断.
金字塔算法是预设的因为效率更高但较复杂难预测. 算数二进比较简单.可预测但档案较大.

以上就是不换设备但有高解析度到低解析度需求的解决方案.
缺点是用户很喜欢你的图片存下来看原图他会翻白眼.因为截断的瑕疵就会露出来.
这里的补充解法是. 图片旁边写 JS 下载语法.
参考范例(繁)
http://www.ladesign.tw/paper/info/jquery_html2canvas_download
他是按照标签产图.你就选图片的那个标签名称就可以.他会产生"用户看见"的大小的图片.
等于是用户浏览器自己重新编码一个图片了.大小比用户看见的小一些(我也不懂原因哈哈哈).
但是不会有破图瑕疵.

以上是我的自建图床解.
mko0okmko0
2016-12-31 01:06:05 +08:00
补充 JPEG 格式
https://zh.wikipedia.org/zh-cn/JPEG
YUV
https://zh.wikipedia.org/zh-cn/YUV

建议参数是
Q75~85.
YUV420(颜色解析度较低.预设值) 或是 YUV444(颜色解析度跟 Y 一样)
float(慢速编码但品质优). 如果 CPU 不够力用 int(预设的整数编码)
YYYYUUUUVVVV.(每层的黑白灰优先).
fhefh
2016-12-31 10:51:10 +08:00
nice 先 mark
lecher
2016-12-31 21:36:30 +08:00
如果没有高并发的压力,可以先不考虑这个问题,图像处理极其耗费 CPU 资源,纯靠堆机器解决,基本上做不了高并发方案。

我遇到过的,调用网站验证码的功能,没有考虑到并发量的问题,在任何嵌入验证码需求的页面都直接把验证码生成接口嵌入在 IMG 标签中。导致无登录状态的用户在访问任何页面都请求一次验证码的接口,单台服务器 8 核 CPU 的情况下,日均 PV 到 400w 就在高峰期频繁出现 100%CPU 的情况了,后来改动了调用方式,只有确认用户主动点击登录之类的活动触发了验证码才会填充 URL 到 IMG 标签中。

这只是一个 CPU 消耗比较小,在 100ms 内就能处理完的图像处理请求,如果是大图像裁剪,性能会更差,如果真的有访问量比较大的峰值,建议买硬盘扩容,硬盘的成本是最低的,可以靠堆硬盘解决的问题,不要心疼钱。

这是一个专为移动设备优化的页面(即为了让你能够在 Google 搜索结果里秒开这个页面),如果你希望参与 V2EX 社区的讨论,你可以继续到 V2EX 上打开本讨论主题的完整版本。

https://www.v2ex.com/t/331326

V2EX 是创意工作者们的社区,是一个分享自己正在做的有趣事物、交流想法,可以遇见新朋友甚至新机会的地方。

V2EX is a community of developers, designers and creative people.

© 2021 V2EX