目的:

减少图片存储数量,节省存储空间(现
mogilefs
存储了近
8
千万张图片,其中近一半为缩略图)。

存储系统中只存储原图,缩略图和其它尺寸的图根据
url
地址中的参数自动生成。

如:原图

缩略图:

在原图
url
后面加上尺寸信息,当只有一个参数时,宽固定,高按比例调节

当有两个参数时,分别为宽,高的尺寸。

要求

1
,能够快速处理

    2
,能够根据
url
中的尺寸参数进行相应处理

系统的处理流程:
Client
  |
Squid
  |
Nginx
  |gearmam
Python
使用到的技术:

Squid nginx php gearman python

Nginx rewrite
的写法:

InBlock.gif    upstream p_w_picpath_resize {
InBlock.gif                                server 192.168.1.X;
InBlock.gif                                }
########
location
中对
url
进行判断,符合规则的
url
进行重写,代理到后端去执行,因为想地址重写后地址栏中
url
不变,因此使用了
proxy_pass

  
InBlock.gif
if ($request_uri    ~*
"^/([a-z0-9./_]+)-([0-9]+),([0-9]+)$") {
InBlock.gif         rewrite
"^/([a-z0-9./_]+)-([0-9]+),([0-9]+)$" /index.php?file=$1&x=$2&y=$3
break;
InBlock.gif         proxy_pass http:
//p_w_picpath_resize;
InBlock.gif                                                }
InBlock.gif    
if ($request_uri    ~*
"^/([a-z0-9./_]+)-([0-9]+)") {
InBlock.gif         rewrite
"^/([a-z0-9./_]+)-([0-9]+)" /index.php?file=$1&x=$2            
break;
InBlock.gif         proxy_pass http:
//p_w_picpath_resize;
InBlock.gif                                                }
PHP
程序功能及代码:

PHP
接收
url
的参数,通过
gearman
发送给后端
python
处理,之后把处理的结果以图片形式展示出来

代码如下:

InBlock.gif<?php
InBlock.gifheader('Content-type: p_w_picpath/jpeg');
InBlock.gif                $img_file=$_GET['file'];
InBlock.gif                $url=
"http://cn.gcimg.net/".$img_file;
InBlock.gif                $sizex=$_GET['x'];
InBlock.gif                $sizey=isset($_GET['y'])?$_GET['y']:"-1";
InBlock.gif                $data=$url.",".$sizex.",".$sizey;
InBlock.gif                $client= new GearmanClient();
InBlock.gif                $client->addServer("192.168.1.x ",4730);//job 服务器地址
InBlock.gif                $p_w_picpath=$client->do('liguxk',$data);
InBlock.gif                //输出图片
InBlock.gif                echo $p_w_picpath;
InBlock.gif?>

 

Python
图片处理脚本

InBlock.gif#!/usr/bin/env python
InBlock.gif'''
InBlock.gifresize p_w_picpath size from gearman
InBlock.gifwrite by lgq @liguxk
InBlock.gif'''
InBlock.gifimport gearman
InBlock.gifimport urllib
InBlock.giffrom PIL import Image
InBlock.giffrom cStringIO import StringIO
InBlock.gif
InBlock.gif
class CustomGearmanWorker(gearman.GearmanWorker):
InBlock.gif        def on_job_execute(self, current_job):
InBlock.gif                
return super(CustomGearmanWorker, self).on_job_execute(current_job)
InBlock.gif
InBlock.gifdef resizeimg(gearman_worker,job):
InBlock.gif        img_url,size_x,size_y=job.data.split(',')
InBlock.gif        size_x=
int(size_x)
InBlock.gif        size_y=
int(size_y)
InBlock.gif        sock=urllib.urlopen(img_url)
InBlock.gif        old_img_data=sock.read()
InBlock.gif        sock.close()
InBlock.gif        old_img=Image.open(StringIO(old_img_data))
InBlock.gif        old_x,old_y=old_img.size
InBlock.gif        
if old_img.mode !='RGBA':
InBlock.gif                img_format='JPEG'
InBlock.gif        
else:
InBlock.gif                img_format='PNG'
InBlock.gif        new_img=StringIO()
InBlock.gif        
if size_y == -1:
InBlock.gif                imgswap=old_img.resize((size_x,(old_y*size_x)/old_x),Image.ANTIALIAS)
InBlock.gif        
else:
InBlock.gif                imgswap=old_img.resize((size_x,size_y),Image.ANTIALIAS)
InBlock.gif        imgswap.save(new_img,img_format,quality=60)
InBlock.gif        
return new_img.getvalue()
InBlock.gifdef main():
InBlock.gif        new_worker=CustomGearmanWorker(['192.168.1.x:4730','192.168.1.x:4730'])
InBlock.gif        new_worker.register_task('liguxk',resizeimg)
InBlock.gif        new_worker.work()
InBlock.gif
InBlock.gif
if __name__=='__main__':
InBlock.gif        
while [1]:
InBlock.gif                main()

 

squid
命中率:

此系统中最前端为
squid
缓存,命中率在
80%
(见下图),因此透过
squid,
需要后端调度的本身就不多,这其中需要生成的图片又不多,而
worker
可以开多个,因此可以满足在线生成的需求。

压力测试报告:
左侧的为小图存储在mogilefs中的情况,右侧为使用实时处理的测试数据。
差别大的一个重要原因是因为通过处理的图片比原来的缩略图大小小了很多

 

 

李国强

2011-12-29