【问题标题】:Optimising concurrent ImageMagick Requests using redis/php-resque使用 redis/php-resque 优化并发 ImageMagick 请求
【发布时间】:2015-11-22 06:37:26
【问题描述】:

我正在开发一个使用 ImageMagick 生成图像的网站。该站点每分钟将收到数百个请求,并且使用 ImageMagick 执行此操作会导致站点崩溃。

所以我们实现了 Redis 和 Php-resque 以在单独的服务器上在后台生成 ImageMagick,这样它就不会导致我们的主服务器崩溃。问题是完成图像仍然需要很长时间。由于服务器正忙于处理这些图像,用户可能希望等待最多 2-3 分钟的图像请求。

我不知道该给你什么信息,但我更多的是寻求建议。我认为如果我们可以缩短 ImageMagick 请求的初始处理时间,那么显然这将有助于加快我们可以处理的图像数量。

以下是我们使用的 ImageMagick 脚本示例:

convert -size 600x400 xc:none \( ".$path."assets/images/bases/base_image_69509021433289153_8_0.png -fill rgb\(255,15,127\) -colorize 100% \) -composite \( ".$path."assets/images/bases/eye_image_60444011438514404_8_0.png -fill rgb\(15,107,255\) -colorize 100% \) -composite \( ".$path."assets/images/markings/marking_clan_8_marking_10_1433289499.png -fill rgb\(255,79,79\) -colorize 100% \) -composite \( ".$path."assets/images/bases/shading_image_893252771433289153_8_0.png -fill rgb\(135,159,255\) -colorize 100% \) -compose Multiply -composite \( ".$path."assets/images/highlight_image_629750231433289153_8_0.png -fill rgb\(27,35,36\) -colorize 100% \) -compose Overlay -composite \( ".$path."assets/images/lineart_image_433715161433289153_8_0.png -fill rgb\(0,0,0\) -colorize 100% \) -compose Over -composite ".$path."assets/generated/queue/tempt_preview_27992_userid_0_".$filename."_file.png

我的理论是,这需要相当长的时间是由于图像着色的过程。有没有办法优化这个过程?

如果有人在处理大量 imagemagick 进程方面有经验,或者可以看到一些非常简单的方法来优化我们的请求,我将非常感激。

谢谢你:)

【问题讨论】:

  • assets/images/bases/base_image_69509021433289153_8_0.png的像素尺寸是多少?和文件大小?服务器的 CPU 和 RAM 规格是什么?您总共有多少个基本映像?

标签: php redis imagemagick resque php-resque


【解决方案1】:

您的命令实际上归结为:

convert -size 600x400 xc:none                                 \
    \( 1.png -fill rgb\(x,y,z\) -colorize 100% \) -composite  \
    \( 2.png -fill rgb\(x,y,z\) -colorize 100% \) -composite  \
    \( 3.png -fill rgb\(x,y,z\) -colorize 100% \) -composite  \
    \( 4.png -fill rgb\(x,y,z\) -colorize 100% \) -composite  \
    \( 5.png -fill rgb\(x,y,z\) -colorize 100% \) -composite  \
    \( 6.png -fill rgb\(x,y,z\) -colorize 100% \) -composite  \
    result.png

我的想法如下:

第 1 点:

在空白画布上的第一个-composite 似乎毫无意义——大概1.png 是一个具有透明度的600x400 PNG,因此您的第一行可以避免合成操作并通过更改为:节省16% 的处理时间:

convert -background none 1.png -fill ... -colorize 100% \
   \( 2.png ..
   \( 3.png ...

第 2 点

我将与您的命令等效的内容放入一个循环中,并进行了 100 次迭代,这需要 15 秒。然后,我将您对 PNG 文件的所有读取更改为对 MPC 文件或 Magick Pixel Cache 文件的读取。这将处理时间减少到不到 10 秒,即减少了 33%。 Magic Pixel Cache 只是一个预解压、预解码的文件,无需任何 CPU 工作即可直接读取到内存中。您可以在目录更改时预先创建它们并将它们与 PNG 文件一起存储。做一个你做的

convert image.png image.mpc

你会离开image.mpcimage.cache。然后,您只需将代码更改为如下所示:

convert -size 600x400 xc:none                                 \
    \( 1.mpc -fill rgb\(x,y,z\) -colorize 100% \) -composite  \
    \( 2.mpc -fill rgb\(x,y,z\) -colorize 100% \) -composite  \
    \( 3.mpc -fill rgb\(x,y,z\) -colorize 100% \) -composite  \
    \( 4.mpc -fill rgb\(x,y,z\) -colorize 100% \) -composite  \
    \( 5.mpc -fill rgb\(x,y,z\) -colorize 100% \) -composite  \
    \( 6.mpc -fill rgb\(x,y,z\) -colorize 100% \) -composite  \
    result.png

第 3 点

很遗憾,您还没有回答我的问题,但如果您的资产目录不是太大,您可以在系统启动时将其(或上面的 MPC 等价物)放到 RAM 磁盘上。

第 4 点

您绝对应该并行运行 - 这将产生最大的收益。使用 GNU Parallel 非常简单 - example here

如果您使用的是 REDIS,它实际上比这更容易。只需将 LPUSH 您的 MIME 编码图像放入 REDIS 列表中,如下所示:

#!/usr/bin/perl
################################################################################
# generator.pl <number of images> <image size in bytes>
# Mark Setchell
# Base64 encodes and sends "images" of specified size to REDIS
################################################################################
use strict;
use warnings FATAL => 'all';
use Redis;
use MIME::Base64;
use Time::HiRes qw(time);

my $Debug=0;    # set to 1 for debug messages

my $nargs = $#ARGV + 1;
if ($nargs != 2) {
    print "Usage: generator.pl <number of images> <image size in bytes>\n";
    exit 1;
}

my $nimages=$ARGV[0];
my $imsize=$ARGV[1];

# Our "image"
my $image="x"x$imsize;

printf "DEBUG($$): images: $nimages, size: $imsize\n" if $Debug;

# Connection to REDIS
my $redis = Redis->new;
my $start=time;

for(my $i=0;$i<$nimages;$i++){
   my $encoded=encode_base64($image,'');
   $redis->rpush('images'=>$encoded);
   print "DEBUG($$): Sending image $i\n" if $Debug;
}
my $elapsed=time-$start;
printf "DEBUG($$): Sent $nimages images of $imsize bytes in %.3f seconds, %d images/s\n",$elapsed,int($nimages/$elapsed);

然后运行多个工人,他们都坐在那里做 BLPOP 的工作

#!/usr/bin/perl
################################################################################
# worker.pl
# Mark Setchell
# Reads "images" from REDIS and uudecodes them as fast as possible
################################################################################
use strict;
use warnings FATAL => 'all';
use Redis;
use MIME::Base64;
use Time::HiRes qw(time);

my $Debug=0;    # set to 1 for debug messages
my $timeout=1;  # number of seconds to wait for an image
my $i=0;

# Connection to REDIS
my $redis = Redis->new;

my $start=time;

while(1){
   #my $encoded=encode_base64($image,'');
   my (undef,$encoded)=$redis->blpop('images',$timeout);
   last if !defined $encoded;
   my $image=decode_base64($encoded);
   my $l=length($image);
   $i++; 
   print "DEBUG($$): Received image:$i, $l bytes\n" if $Debug;
}

my $elapsed=time-$start-$timeout; # since we waited that long for the last one
printf "DEBUG($$): Received $i images in %.3f seconds, %d images/s\n",$elapsed,int($i/$elapsed);

如果我像上面那样运行一个生成器进程并让它生成 100,000 个 200kB 的图像,并在我的合理规格 iMac 上使用 4 个工作进程读取它们,则需要 59 秒,或者大约 1,700 个图像/秒可以通过 REDIS .

【讨论】:

    【解决方案2】:

    队列正在一次处理一个?您是否尝试过让并发作业保持并行运行,以便您一次处理多个元素?

    【讨论】:

    • 嗨;我已经尝试了多个工作人员(我用 1,3、5 进行了测试)但是我们所在的服务器 - 我们发现只有 1 个工作人员会更好(服务器人员告诉我们这是因为服务器更擅长处理请求或者其他的东西)。我们有另一台服务器,使用 5 名工作人员比使用 1 名工作人员稍好一些,但等待时间仍然很长。
    • 您尝试过 Worker Queue 吗?像豆茎?还是 RabittMQ?
    猜你喜欢
    • 2018-08-01
    • 1970-01-01
    • 2013-10-02
    • 1970-01-01
    • 1970-01-01
    • 2016-09-01
    • 2021-05-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多