Nginx + GridFS 实现的缩略图处理机制 « weberliu.com

来源:互联网 发布:金税三期重装后没数据 编辑:程序博客网 时间:2024/05/04 19:28

场景

在 B2C 系统中,由于页面上大量的使用商品的缩略图,因此如何来处理和存储缩略图也就显得尤为重要了。以前在做 Ecshop 的时候出于用户服务器环境的限制,我们是在图片上传的时候来根据系统设置来生成缩略图。这样带来的问题是:用户更换模板以后有可能调整图片的大小,而导致之前生成的缩略图不可用,因此在 Ecshop 中提供了重新生成缩略图的功能。

在新的B2C系统开发之初,我们就对缩略图的生成机制进行了定义,要求是:

  • 缩略图尺寸由模板来决定,也就是通过请求的url地址来生成相应尺寸的缩略图
  • 动态生成缩略图并缓存缩略图,缓存不存在的时候自动重新生成

难点

  • 动态生成缩略图有可能会使 Web 服务器需要同时处理大量的缩略图请求,而导致 CPU 负载过高。即便有CDN缓存、本地文件缓存也不能完全排除这种可能性。
  • URL 地址的问题,最开始我们考虑采用 图片id_width_height.jpg 这样的形式,但是这么做的风险是,如果有人大量的刷不同宽度和高度的数值就可能会造成撑爆缓存。Amazone 采用的是规则的定义来作为图片路径,这样一定程度的解决了被刷的可能性,但是对于我们来说这种方式也存在一定的问题,因为他的规则中只有大中小等几个规则的定义,并没有具体的宽度和高度,很难适应各种各样的运营经理和产品经理提出来的需求。基于这些考虑,我们最好考虑文件的路径还是采用id_width_height.jpg这种形式的命名,但是给用户看到的地址采用了 TinyURL 的方式来处理,这样既能避免恶意的刷尺寸同时也可以灵活的满足各种需求

方案 1

在最初的解决方案中,我们采用了和手机之家类似的方式来处理:当有缩略图的请求的时候,图片控制器来检查是否存在缓存,如果不存在则生成新的缩略图并写入缓存。

经过了一段时间的运行之后,我们发现这种方式处理的图片在没有CDN的前提下,系统的处理时间相对过长,页面上经常会出现图片一个个的“蹦”出来的情况,效果不是很理想。

方案 2

在一次浏览 Nginx Wiki的时候无意中发现了一个插件:GridFs。下面是关于这个插件的描述:

nginx-gridfs is an Nginx module to serve content directly from MongoDB’s GridFS.

非常的简单,就是允许Nginx直接访问 MongoDB 的 GridFS。这也就触发了我一个新的想法:如果将缩略图的缓存文件放到GridFS中,直接通过 Nginx 来访问是不是能改善现有的状况呢?

于是我做了一个简单的测试:

  • 缓存写入Memcache,PHP 读取Memcache并返回给用户;
  • 直接通过Nginx-GridFS插件读取GridFS中的缓存文件。

在用ab简单的压力测试之后发现效果是惊人的。使用GridFS系统的响应时间是10ms左右,而第一种方式则比第二种相差了10倍左右(具体数字记不太清楚了)。

难点

虽然直接访问 GridFS 的效率是如此的优秀,但是还面临着一个问题,在没有缓存的时候如何来触发缩略图的生成?前面已经提到过系统的要求就是要动态的生成缩略图,因此不可能采用主动缓存的方式。而通过Nginx直接访问GridFS这种方式由于跳过了PHP,我们没有办法来触发缩略图的生成。

这个问题困扰了我好几天。。。。

404

在又一遍仔细阅读Nginx配置参数的时候发现了 error_page 这个参数,他可以将一个错误信息指定到另一个URL。当GridFS中缓存文件不存在的时候 Nginx 同样也是返回一个 404 的错误,而我们可以通过 error_page 将404的错误重新定向到 PHP 处理程序,交给PHP来生成缩略图并缓存到GridFS中,这样整个的流程就完整了:

Nginx配置

要实现这一功能,首先要安装 nginx-gridfs 插件,具体的安装方法还是到官方去看吧:https://github.com/mdirolf/nginx-gridfs。这里就不多说了

其次是要修改vhost里面的server容器,将图片的请求单独进行处理,例如:

  server  {    listen          80;     server_name     statics.*;    root            /www/statics;     location  /thumb/    {         gridfs        files root_collection=thumbnail field=filename type=string;      mongo         192.168.0.11:27017;      error_page    404 =201 /thumb.php?f=$request_filename;      expires       30d;    }    ... ...  }

注意 error_page 这一行中的 =201,它的作用是将404的Status Code改变为201,当然您可以写成 =200,我们这里用201的目的是为了通过 HTTP Status Code来判断是否为缓存中读到的文件


原文地址:http://zoomq.qiniudn.com/ZQScrapBook/ZqFLOSS/data/20110718145649/

0 0