Оптимизация параллельных запросов ImageMagick с использованием redis / php-resque

Я работаю над сайтом, который использует 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 или может найти несколько простых способов оптимизировать наши запросы.

Спасибо 🙂

2

Решение

Ваша команда на самом деле сводится к следующему:

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 это PNG 600×400 с прозрачностью, поэтому ваша первая строка может избежать операции компоновки и сэкономить 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 — это просто предварительно распакованный, предварительно декодированный файл, который может быть считан непосредственно в память без каких-либо усилий процессора. Вы можете предварительно создавать их всякий раз, когда ваш каталог изменяется, и хранить их вместе с файлами PNG. Чтобы сделать тот, который вы делаете

convert image.png image.mpc

и ты выйдешь image.mpc а также image.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 — пример здесь.

Если вы используете 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 изображений по 200 КБ каждое и считывает их с 4 рабочими процессами на моем подходящем iMac, это занимает 59 секунд, или около 1700 изображений / с могут проходить через REDIS.

2

Другие решения

Очередь обрабатывается по одному? Вы пытались выполнять параллельные задания, которые будут работать параллельно, чтобы вы работали более чем с одним элементом одновременно, если это так?

0

По вопросам рекламы [email protected]