Содержание
Как программно удалить фон изображения, сделав его прозрачным в ruby?
У меня есть куча изображений продуктов, и я хотел бы удалить фон каждого из них программно в ruby. Вот несколько примеров изображений , которые я разместил на imgur.
Я приведу здесь пример, чтобы вы могли видеть, но я не хотел публиковать кучу изображений. Это самый простой из них. Он имеет только белый фон, но некоторые продукты имеют более сложный фон. Я знаю, что делать что-то подобное на этом изображении , вероятно, не будет работать, поэтому я хотел бы понять это и изящно потерпеть неудачу, фактически не удаляя фон и просто уведомляя меня каким-то образом.
Я использую ruby на rails 3 и carrierwave в качестве обработчика загрузки.
Возможно ли это вообще, или я действительно смогу удалить только белый фон?
ruby
ruby-on-rails-3
image-processing
imagemagick
rmagick
Поделиться
Источник
hadees
13 марта 2012 в 19:56
4 ответа
5
Для удаления белого фона из изображений ниже приведен сценарий bash с использованием imagemagick :
#!/bin/bash
# pass the image path, image name and threshold(used as a fuzz factor) to the bash script
IMGPATH=$1
IMGNAME=$2
THRESHOLD=$3
# start real
convert ${IMGPATH}${IMGNAME} \( +clone -fx 'p{0,0}' \) -compose Difference -composite -modulate 100,0 +matte ${IMGPATH}${IMGNAME}_difference.png
# remove the black, replace with transparency
convert ${IMGPATH}${IMGNAME}_difference.png -bordercolor white -border 1x1 -matte -fill none -fuzz 7% -draw 'matte 1,1 floodfill' -shave 1x1 ${IMGPATH}${IMGNAME}_removed_black.png
composite -compose Dst_Over -tile pattern:checkerboard ${IMGPATH}${IMGNAME}_removed_black.png ${IMGPATH}${IMGNAME}_removed_black_check.png
# create the matte
convert ${IMGPATH}${IMGNAME}_removed_black.png -channel matte -separate +matte ${IMGPATH}${IMGNAME}_matte.png
# negate the colors
convert ${IMGPATH}${IMGNAME}_matte.png -negate -blur 0x1 ${IMGPATH}${IMGNAME}_matte-negated.png
# eroding matte(to remove remaining white border pixels from clipped foreground)
convert ${IMGPATH}${IMGNAME}_matte.png -morphology Erode Diamond ${IMGPATH}${IMGNAME}_erode_matte.png
# you are going for: white interior, black exterior
composite -compose CopyOpacity ${IMGPATH}${IMGNAME}_erode_matte.png ${IMGPATH}${IMGNAME} ${IMGPATH}${IMGNAME}_finished.png
#remove white border pixels
convert ${IMGPATH}${IMGNAME}_finished.png -bordercolor white -border 1x1 -matte -fill none -fuzz ${THRESHOLD}% -draw 'matte 1,1 floodfill' -shave 1x1 ${IMGPATH}${IMGNAME}_final.png
#deleting extra files
rm ${IMGPATH}${IMGNAME}_difference.png
rm ${IMGPATH}${IMGNAME}_removed_black.png
rm ${IMGPATH}${IMGNAME}_removed_black_check.png
rm ${IMGPATH}${IMGNAME}_matte.png
rm ${IMGPATH}${IMGNAME}_matte-negated.png
rm ${IMGPATH}${IMGNAME}_finished.png
Я столкнулся с проблемой удаления белых пикселей границы из результирующего изображения. Размывание двоичной маски и бритье оставшихся пикселей решает эту проблему.
Источник : преобразование белого в прозрачный
Поделиться
jahackbeth
12 мая 2014 в 19:30
2
Взгляните на http://www.imagemagick.org/использования/маскировки/#bg_remove
Вы, вероятно, можете довольно хорошо работать с однотонными фонами, но нетвердые фоны (например, на изображении, с которым вы связались) довольно сложны, и я сомневаюсь, что вы сможете придумать единый метод, который будет работать для всех изображений.
Поделиться
GreyBeardedGeek
13 марта 2012 в 20:17
1
ImageMagick-наиболее вероятное решение здесь (возможно, с мини-магией gem для rails), но, как вы предполагаете, маловероятно, что в некоторых случаях у вас будет хороший результат. Простое превращение всех белых пикселей в прозрачные, скорее всего, оставит неровные края, которые будут вполне заметны на небелом фоне. Но есть удивительное количество опций, которые дают вам много такого контроля, который вы могли бы иметь в PhotoShop или другом-вы создадите маску, которая находит контур формы в пределах определенного допуска оттенка, цвета или тому подобного, чем изменение всех соответствующих пикселей на прозрачный. Я думаю, вам также придется перейти на формат PNG, так как я не верю, что JPEG поддерживает Альфа-прозрачность.
CarrierWave — это правильный инструмент для такого рода обработки-вы можете использовать его возможности «versions» для хранения исходного файла и сделать несколько автоматических попыток с использованием различных параметров (которые сохранят разные файлы, не касаясь оригинала).
http://www.imagemagick.org/использования/маскировки/#two_background может дать вам старт.
Поделиться
Tom Harrison
13 марта 2012 в 20:21
-1
Предполагая, что вы имеете в виду пакетную обработку кучи несвязанных файлов. Насколько я понимаю, вы не можете этого сделать, так как удаление фона (путем вытягивания матового) — это сильно ограниченная проблема и обязательно требует ввода данных пользователем. Постоянный цвет фона, такой как синий или зеленый экран, позволит вам довольно хорошо работать с бесплатными программами, такими как gimp, но ничто не может сделать большую работу, если вы не хотите читать, например, научные журналы bleeding edge.
Матирование Пуассона
http://research.microsoft.com/apps/pubs/default.aspx?id=69117
OR
Замкнутое решение для естественного матирования изображений
www.wisdom.weizmann.ac.il/~levina/papers/Matting-Levin-Lischinski-Weiss-CVPR06.pdf
Более поздний имеет код matlab, предусмотренный для академических целей. Но все это серьезные дробилки чисел и может использовать все ваши системные ресурсы на больших файлах, как можно убедиться из пробной версии приложения pullmatt от http://PixelFeather.com/download . Что, кстати, лучше всего подходит для решения проблемы естественного изображения любого проприетарного программного обеспечения, включая photoshop.
Поделиться
user2337920
01 мая 2013 в 00:57
Похожие вопросы:
Сделайте белый фон изображения прозрачным в css
У меня есть два изображения, одно из которых-маленький значок, наложенный на первое изображение. Мой значок имеет белый фон, поэтому, когда значок помещается поверх другого изображения, мы получаем…
Удалить фон изображения на веб-сайте?
Как сделать фон на изображении прозрачным? Большинство моих изображений имеют белый фон. Когда я использую их на своем сайте с черным цветом фона моего тела, это выглядит неловко. Я безуспешно…
Сделайте фон изображения прозрачным или белым программно
Если вы создаете приложение, в котором хотите сделать фон изображения прозрачным или белым, есть ли способ сделать это? Например : В образе,…
Удалите белый фон с изображения и сделайте его прозрачным
Мы пытаемся сделать следующее в Mathematica — RMagick удалить белый фон из изображения и сделать его прозрачным . Но с реальными фотографиями это выглядит паршиво (например, ореол вокруг…
RMagick удалить белый фон с картинки и сделать его прозрачным
Мне нужно удалить белый фон с этого изображения и сделать фон прозрачным. Так что это просто черная галочка на прозрачном фоне, экспортируемая как png. например, поворот В Есть идеи?
Удалите угол изображения или сделайте его прозрачным
Я пытаюсь удалить угол изображения или сделать его прозрачным, чтобы фон за этим углом был виден. Есть ли какой-нибудь способ сделать это с Jquery или CSS?
Сделайте фон прозрачным на изображении.(Пример изображения прилагается)
Как я могу удалить белый цвет фона(белый в этом случае) из изображения?. Или хотите сделать фон изображения прозрачным. Это два образа. Изображение 1: текст на белом фоне Изображение 2-это:…
Как программно удалить фон изображения, сделав его прозрачным в android
Как программно удалить фон изображения, сделав его прозрачным в android. Это прямо вперед, чтобы удалить фон фотографии/изображения в android? Каков же подход. Есть ли у нас уже существующие api? В…
Как удалить черный фон с изображения и сделать его прозрачным с помощью opencv?
Я использую метод PerspectiveTransform для преобразования изображения в заданный прямоугольник. Метод warpPerspective работает нормально, но вывод содержит черный фон, и я хочу удалить черный цвет и…
Как удалить белый фон изображения и сделать его прозрачным?
Я пытаюсь программно удалить белый фон нескольких изображений и сделать его прозрачным. Формат изображения прост, он имеет белый фон и один объект, расположенный в основном посередине. Я хочу…
Запасной фон-чехол 2-в-1, 2B1530-09/18 зеленый хромакей и рубиновый, 150*300 см
Дополнительные фоны в виде двустороннего чехла 2B1530-09/18 предназначены для использования вместе с пружинными каркасами комплектов KIT или отражателя с габаритами 150 х 200 см. Таким образом, вы можете сэкономить средства и пространство студии, имея богатую коллекцию солидных цветных фонов из качественной ткани.
С чехлом 2B1530-09/18 мы имеем два цвета, и первый из них — зеленый хромакей. Слово «хромакей» (СК, фотошоп) означает лишь одно: при дальнейшем редактировании слоев фотографии или видео данный фон удаляется легче, чем все остальные. Эта легкость объясняется очень просто: яркий зеленый цвет максимально удален от оттенков человеческой кожи. Такими же свойствами обладает и синий цвет (именно с синего началась история удаления фона для его замены в знаменитом фильме «Багдадский вор»).
Но в последние годы зеленый хромакей популярнее из-за высокой чувствительности к нему цифровых камер нового поколения. Зеленые изображения меньше «шумят», чище и, как следствие, легче поддаются так называемому «композитингу».
В использовании зеленого хромакея 2B1530-09/18 есть свои нюансы. Очень важно располагать объект съемки подальше от зеленого экрана, в противном случае вы рискуете получить лица с зеленоватым оттенком за счет переотражения, которое появляется вследствие близкого расположения объекта к фону.
Второй нюанс – достаточное и равномерное освещение фона хромакей с минимальным количеством теней и пересветов. Это тоже поможет в дальнейшем редактировании. Исключением является тень от объекта: ее лучше оставить, чтобы использовать в комбинировании слоев во время их обработки.
Рубиновый цвет – вторая сторона чехла 2B1530-09/18. Он является одним из оттенков яркого красного цвета. Его главное свойство – доминирование на фоне любого другого цвета. Поэтому его использование должно быть продуманным и тщательно выполненным.
Характеристики:
- габариты – 154 х 295 см;
- вес – 1,1 кг;
- ткань фона – хлопок;
- плотность нити – 21s.
Коллекция тканевых фонов на пружине включает в себя также и запасные чехлы самых разнообразных расцветок.
Отражатели также могут использоваться как каркас для запасных чехлов.
Вам могут понадобиться модификаторы света и цветовые гаджеты для специального освещения фонов, поэтому взгляните на софтбоксы, рефлекторы и цветные фильтры
Желаем легкого композитинга и новых «рубиновых» портретов лучших красавиц вашего города.
отзывы, фото и характеристики на Aredi.ru
Мы доставляем посылки в г. Калининград и отправляем по всей России
- 1
Товар доставляется от продавца до нашего склада в Польше. Трекинг-номер не
предоставляется. - 2
После того как товар пришел к нам на склад, мы организовываем доставку в г. Калининград.
- 3
Заказ отправляется курьерской службой EMS или Почтой России. Уведомление с трек-номером вы
получите по смс и на электронный адрес.
!
Ориентировочную стоимость доставки по России менеджер выставит после
оформления заказа.
Гарантии и возврат
Гарантии
Мы работаем по договору оферты, который является юридической гарантией того, что мы выполним
свои обязательства.
Возврат товара
Если товар не подошел вам, или не соответсвует описанию, вы можете вернуть его, оплатив
стоимость обратной пересылки.
- У вас остаются все квитанции об оплате, которые являются подтверждением заключения сделки.
- Мы выкупаем товар только с проверенных сайтов и у проверенных продавцов, которые полностью отвечают за доставку товара.
- Мы даем реальные трекинг-номера пересылки товара по России и предоставляем все необходимые документы по запросу.
- 5 лет успешной работы и тысячи довольных клиентов.
Как программно удалить фон изображения, делая его прозрачным в рубине?
For removing white backgrounds from images, following is a bash script using imagemagick :
#!/bin/bash
# pass the image path, image name and threshold(used as a fuzz factor) to the bash script
IMGPATH=$1
IMGNAME=$2
THRESHOLD=$3
# start real
convert ${IMGPATH}${IMGNAME} \( +clone -fx 'p{0,0}' \) -compose Difference -composite -modulate 100,0 +matte ${IMGPATH}${IMGNAME}_difference.png
# remove the black, replace with transparency
convert ${IMGPATH}${IMGNAME}_difference.png -bordercolor white -border 1x1 -matte -fill none -fuzz 7% -draw 'matte 1,1 floodfill' -shave 1x1 ${IMGPATH}${IMGNAME}_removed_black.png
composite -compose Dst_Over -tile pattern:checkerboard ${IMGPATH}${IMGNAME}_removed_black.png ${IMGPATH}${IMGNAME}_removed_black_check.png
# create the matte
convert ${IMGPATH}${IMGNAME}_removed_black.png -channel matte -separate +matte ${IMGPATH}${IMGNAME}_matte.png
# negate the colors
convert ${IMGPATH}${IMGNAME}_matte.png -negate -blur 0x1 ${IMGPATH}${IMGNAME}_matte-negated.png
# eroding matte(to remove remaining white border pixels from clipped foreground)
convert ${IMGPATH}${IMGNAME}_matte.png -morphology Erode Diamond ${IMGPATH}${IMGNAME}_erode_matte.png
# you are going for: white interior, black exterior
composite -compose CopyOpacity ${IMGPATH}${IMGNAME}_erode_matte.png ${IMGPATH}${IMGNAME} ${IMGPATH}${IMGNAME}_finished.png
#remove white border pixels
convert ${IMGPATH}${IMGNAME}_finished.png -bordercolor white -border 1x1 -matte -fill none -fuzz ${THRESHOLD}% -draw 'matte 1,1 floodfill' -shave 1x1 ${IMGPATH}${IMGNAME}_final.png
#deleting extra files
rm ${IMGPATH}${IMGNAME}_difference.png
rm ${IMGPATH}${IMGNAME}_removed_black.png
rm ${IMGPATH}${IMGNAME}_removed_black_check.png
rm ${IMGPATH}${IMGNAME}_matte.png
rm ${IMGPATH}${IMGNAME}_matte-negated.png
rm ${IMGPATH}${IMGNAME}_finished.png
I was facing a problem removing white border pixels from the resulting image. Eroding the binary mask and shaving the remaining pixels solves the problem.
Source : convert white to transparent
В Казани пышно отметили победу «Рубина» в чемпионате России по футболу — Российская газета
Накануне игроки и тренеры «Рубина» получили заслуженные золотые медали за победу в первенстве России.
Награды казанцам вручили президент Татарстана Минтимер Шаймиев, исполняющий обязанности президента РФС Никита Симонян и президент Премьер-лиги Сергей Прядкин.
На праздничной церемонии Шаймиев сделал несколько громких заявлений.
Во-первых, президент Татарстана настаивает на том, что команду нужно сохранить в полном составе. Во-вторых, «Рубину» дан наказ — постараться выиграть третье «золото» подряд. В третьих, спонсорам придется раскошелиться, чтобы команда ни в чем не нуждалась.
«Я обращаюсь к женам футболистов, подругам, — в частности, отметил Шаймиев. — С вашими детьми я смогу договориться, они меня любят. (Улыбается.) Так вот: оставайтесь в Казани! У кого есть сложности с языком, с устройством детей в детские сады, школы, — решим все проблемы. Давайте вместе сохраним коллектив, в котором все поддерживают друг друга!»
Залог успеха «Рубина» Шаймиев видит в тренерской работе Курбана Бердыева, которого за две победы в чемпионатах скромно назвал гением. Но тут же задумался: «Выиграет третий титул, даже не знаю, как потом его называть», — резюмировал президент под громкий хохот футболистов.
Разумеется, много внимания было уделено Алехандро Домингесу, который в этом году стал лучшим футболистом России. С такой оценкой таланта аргентинца полностью согласился заслуженный мастер спорта СССР Никита Симонян:
— Футболисты — большие актеры. Домингес — солист всего ансамбля. Но при этом без режиссерской руки ничего бы у него не получилось. Мне кажется, именно главный тренер Курбан Бердыев сумел раскрыть сильные стороны Домингеса. А вот Дику Адвокату (футболист перешел в «Рубин» из «Зенита») этого сделать не удалось.
Российская газета: Никита Павлович, как футбольная общественность России восприняла вторую победу «Рубина»?
Никита Симонян: Восемь очков отрыва от серебряного призера — это, я вам скажу, дорогого стоит. Не знаю ни одного человека, который сомневался бы в справедливости такого исхода чемпионата.
РГ: Сумеет ли «Рубин» выйти в серию плей-офф Лиги чемпионов?
Симонян: Осталась одна игра — в Италии с «Интером». Чтобы ни говорили про команду с Апеннин, сильнейшей командой мира является «Барселона». «Рубин» сумел ее победить. Ничто не мешает ему сделать то же самое и в Милане.
Алехандро Домингес на церемонии вел себя очень скромно и старался не отходить от жены и ребенка. Только получив золотую медаль, аргентинец позволил всем желающим сфотографироваться с собой, а потом ответил на вопросы корреспондента «РГ».
Российская газета: Алехандро, президент республики Шаймиев заявил на церемонии о желании сохранить чемпионский состав…
Алехандро Домингес: Я поражен таким трепетным отношением к футболу со стороны первых лиц. Подобного я не встречал. К слову, тот вариант, что я останусь в «Рубине», не исключен. Переговоры о продолжении карьеры в клубе уже начались. На меня также вышли команды из Италии, Испании. Определенная ясность наступит после матча с «Интером».
РГ: Вы два раза выигрывали чемпионат России. Какой титул для вас является более значимым, завоеванный с «Зенитом» в 2007 году или с «Рубином» в 2009-м?
Домингес: Вы же видите, как я радуюсь этой победе с «Рубином»! Но если серьезно, не хотелось бы делить эти победы.
Еще одним свободным агентом является полузащитник Александр Рязанцев, который на церемонию вручения пришел вместе с мамой. Спортсмен не скрывает, что и на него есть покупатели, но кто это — широкой публике до поры до времени лучше не знать. Впрочем, президент «Рубина» Александр Гусев заявил, что переговоры со всеми игроками уже идут в полном объеме, и в ближайшее время будущее «Рубина» станет ясным и понятным.
чемпионат мира-2010
Сегодня в 20.00 по московскому времени в Кейптауне пройдет жеребьевка финальной стадии чемпионата мира 2010 года. Трансляцию можно будет посмотреть в записи на телеканале «Спорт» в 21.45.
Накануне 32 участника турнира были распределены по четырем корзинам.
Корзина 1: ЮАР, Германия, Бразилия, Италия, Испания, Англия, Аргентина, Голландия.
Корзина 2: Франция, Португалия, Словения, Швейцария, Греция, Сербия, Дания, Словакия.
Корзина 3: Кот-д Ивуар, Гана, Камерун, Нигерия, Алжир, Парагвай, Чили, Уругвай.
Корзина 4: Япония, Южная Корея, КНДР, Австралия, Новая Зеландия, США, Мексика, Гондурас.
Сенсационным стало непопадание сборной Франции в первую корзину, вместе с другими сильнейшими. По словам чиновников ФИФА, это было сделано в качестве наказания французам за скандал в стыковом матче против ирландцев.
Напомним, что в той игре Тьерри Анри подыграл себе рукой в штрафной площади, после чего его партнер забил решающий гол. В результате сборная Франция пробилась на чемпионат мира 2010 года в ЮАР, а Ирландия — нет. Любопытно, что с таким решением ФИФА согласился и легендарный игрок сборной Франции, а ныне глава УЕФА Мишель Платини.
В остальном Международная федерация футбола при распределении команд по корзинам в первую очередь учитывала выступление сборных за последние два года.
Исполком ФИФА не принял никаких изменений, касающихся принципов судейства, хотя на этом активно настаивал президент ассоциации Йозеф Блаттер. Судить поединки чемпионата мира по-прежнему будут три арбитра, а не пять. Не будет в ЮАР и видеоповторов спорных моментов.
Вместе с тем президент ФИФА пообещал, что Международная федерация непременно вернется к обсуждению этих вопросов, но после первенства.
Подготовил Павел Петровский
Learning by building, система фоновой обработки на Ruby
В сегодняшнем посте мы собираемся реализовать простую систему фоновой обработки для развлечения! Мы можем кое-что узнать по пути, заглянув во внутреннее устройство популярных систем фоновой обработки, таких как Sidekiq. Продукт этой забавы ни в коем случае не предназначен для производственного использования.
Предположим, у нас есть задача в нашем приложении, которая загружает один или несколько веб-сайтов и извлекает их заголовки. Поскольку мы не имеем никакого влияния на производительность этих веб-сайтов, мы хотели бы выполнять задачу вне нашего основного потока (или текущего запроса — если мы создаем веб-приложение), но в фоновом режиме.
Инкапсуляция задачи
Прежде чем мы перейдем к фоновой обработке, давайте создадим объект службы для выполнения поставленной задачи. Мы будем использовать OpenURI и Nokogiri для извлечения содержимого тега title.
1 2 3 4 5 6 7 8 9 10 11 12 | требуется 'open-uri' требовать "нокогири" класс TitleExtractorService вызов def (url) document = Nokogiri :: HTML (открыть (URL)) title = document.css ('html> head> title'). первый.содержание помещает title.gsub (/ [[: space:]] + /, '') .strip спасать помещает "Невозможно найти заголовок для # {url}" конец конец |
При вызове службы печатается заголовок указанного URL.
1 2 | TitleExtractorService.new.call ('https://appsignal.com') # AppSignal: мониторинг производительности приложений для Ruby on Rails и Elixir |
Это работает, как и ожидалось, но давайте посмотрим, сможем ли мы немного улучшить синтаксис, чтобы он выглядел и напоминал другие системы фоновой обработки.Создав модуль Magique :: Worker
, мы можем добавить синтаксический сахар к объекту службы.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 | модуль Magique модуль Worker def self.included (база) base.extend (ClassMethods) конец модуль ClassMethods def perform_now (* аргументы) new.perform (* аргументы) конец конец def выполнить (*) поднять NotImplementedError конец конец конец |
Модуль добавляет метод perform
к рабочему экземпляру и метод perform_now
к рабочему классу, чтобы сделать вызов немного лучше.
Давайте включим модуль в наш сервисный объект. Пока мы занимаемся этим, давайте также переименуем его в TitleExtractorWorker
и изменим метод call
на perform
.
1 2 3 4 5 6 7 8 9 10 11 | класс TitleExtractorWorker включить Magique :: Worker def выполнить (url) document = Nokogiri :: HTML (открыть (URL)) title = document.css ('HTML> заголовок> заголовок'). first.content помещает title.gsub (/ [[: space:]] + /, '').полоска спасать помещает "Невозможно найти заголовок для # {url}" конец конец |
Вызов по-прежнему имеет тот же результат, но немного понятнее, что происходит.
1 2 | TitleExtractorWorker.perform_now ('https://appsignal.com') # AppSignal: мониторинг производительности приложений для Ruby on Rails и Elixir |
Реализация асинхронной обработки
Теперь, когда у нас работает извлечение заголовков, мы можем получить все заголовки из прошлых статей Ruby Magic.Для этого предположим, что у нас есть константа RUBYMAGIC
со списком всех URL-адресов прошлых статей.
1 2 3 4 5 6 7 8 9 | RUBYMAGIC.each do | url | TitleExtractorWorker.perform_now (url) конец # Распутывание классов, экземпляров и метаклассов в Ruby | Блог AppSignal # Привязки и лексическая область видимости в Ruby | Блог AppSignal # Создание расширения Ruby C с нуля | Блог AppSignal # Замыкания в Ruby: блоки, процедуры и лямбды | Блог AppSignal #... |
Мы получаем заголовки прошлых статей, но чтобы извлечь их все, требуется время. Это потому, что мы ждем завершения каждого запроса, прежде чем перейти к следующему.
Давайте улучшим это, добавив в наш рабочий модуль метод perform_async
. Чтобы ускорить процесс, он создает новый поток для каждого URL-адреса.
1 2 3 4 5 6 7 8 9 | модуль Magique модуль Worker модуль ClassMethods def perform_async (* аргументы) Нить.новый {new.perform (* args)} конец конец конец конец |
После изменения вызова на TitleExtractorWorker.perform_async (url)
, мы получаем все заголовки почти сразу. Однако это также означает, что мы одновременно открываем более 20 подключений к блогу Ruby Magic. ( Извините за возню с вашим блогом, ребята! 😅)
Если вы следуете своей собственной реализации и тестируете ее вне длительного процесса (например, веб-сервера), не забудьте добавить что-то вроде цикла {sleep 1}
в конец вашего скрипта чтобы убедиться, что процесс не завершится немедленно.
Постановка задач в очередь
При подходе к созданию нового потока для каждого вызова мы в конечном итоге достигнем пределов ресурсов (как на нашей стороне, так и на веб-сайтах, к которым мы обращаемся). Поскольку мы хотим быть хорошими гражданами, давайте изменим реализацию на асинхронную, но не похожую на атаку отказа в обслуживании.
Обычный способ решить эту проблему — использовать шаблон производитель / потребитель. Один или несколько производителей помещают задачи в очередь, в то время как один или несколько потребителей берут задачи из очереди и обрабатывают их.
Очередь — это в основном список элементов. Теоретически простой массив подойдет. Однако, поскольку мы имеем дело с параллелизмом, нам нужно убедиться, что только один производитель или потребитель может получить доступ к очереди одновременно. Если мы не будем осторожны с этим, все закончится хаосом — как два человека, пытающиеся протиснуться через дверь одновременно.
Эта проблема известна как проблема производитель-потребитель, и ее можно решить несколькими способами. К счастью, это очень распространенная проблема, и Ruby поставляется с правильной реализацией Queue
, которую мы можем использовать, не беспокоясь о синхронизации потоков.
Чтобы использовать его, давайте убедимся, что и производители, и потребители могут получить доступ к очереди. Мы делаем это, добавляя метод класса к нашему модулю Magique
и назначая ему экземпляр Queue
.
1 2 3 4 5 6 7 8 9 10 11 | модуль Magique def self.backend @backend конец def self.backend = (бэкэнд) @backend = бэкэнд конец конец Magique.backend = Queue.new |
Затем мы изменяем нашу реализацию perform_async
, чтобы поместить задачу в очередь вместо создания собственного нового потока.Задача представлена в виде хэша, включающего ссылку на рабочий класс, а также аргументы, переданные методу perform_async
.
1 2 3 4 5 6 7 8 9 | модуль Magique модуль Worker модуль ClassMethods def perform_async (* аргументы) Magique.backend.push (рабочий: сам, аргументы: аргументы) конец конец конец конец |
На этом мы закончили со стороной продюсера.Теперь давайте посмотрим на потребителя.
Каждый потребитель — это отдельный поток, который берет задачи из очереди и выполняет их. Вместо того, чтобы останавливаться после одной задачи, как поток, потребитель затем берет другую задачу из очереди и выполняет ее, и так далее. Вот базовая реализация потребителя под названием Magique :: Processor
. Каждый процессор создает новый поток, который зацикливается бесконечно. На каждой итерации он пытается получить новую задачу из очереди, создает новый экземпляр рабочего класса и вызывает свой метод perform
с заданными аргументами.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 | модуль Magique класс Процессор def self.start (параллелизм = 1) concurrency.times {| n | новый ("Процессор № {n}")} конец def инициализировать (имя) thread = Thread.new делать петля делать payload = Magique.backend.pop worker_class = полезная нагрузка [: worker] worker_class.new.perform (* полезная нагрузка [: аргументы]) конец конец thread.name = имя конец конец конец |
В дополнение к циклу обработки мы добавляем удобный метод под названием Magique :: Processor.начало
. Это позволяет нам запускать сразу несколько процессоров. Хотя на самом деле в названии потока нет необходимости, это позволит нам увидеть, действительно ли все работает так, как ожидалось.
Давайте настроим вывод нашего TitleExtractorWorker
, включив в него имя текущего потока.
1 | помещает "[# {Thread.current.name}] # {title.gsub (/ [[: space:]] + /, '') .strip}" |
Чтобы протестировать нашу настройку фоновой обработки, нам сначала нужно развернуть набор процессоров перед постановкой наших задач в очередь.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 | Magique.backend = Queue.new Magique :: Processor.start (5) RUBYMAGIC.each do | url | TitleExtractorWorker.perform_async (url) конец # [Processor 3] Привязки и лексическая область видимости в Ruby | Блог AppSignal # [Процессор 4] Создание расширения Ruby C с нуля | Блог AppSignal # [Процессор 1] Распознавание классов, экземпляров и метаклассов в Ruby | Блог AppSignal # [Процессор 0] Скрытые жемчужины Ruby, StringScanner | Блог AppSignal # [Процессор 2] Волокна и счетчики в Ruby: выворачиваем блоки наизнанку | Блог AppSignal # [Processor 4] Closures in Ruby: Blocks, Procs and Lambdas | Блог AppSignal #... |
При запуске мы все равно получаем заголовки всех статей. Хотя это не так быстро, как использование отдельного потока для каждой задачи, это все же быстрее, чем первоначальная реализация, в которой не было фоновой обработки. Благодаря добавленным именам процессоров мы также можем подтвердить, что все процессоры работают через очередь. Настраивая количество одновременно работающих процессоров, можно найти баланс между скоростью обработки и существующими ограничениями ресурсов.
Расширение до нескольких процессов и машин
На данный момент текущая реализация нашей системы фоновой обработки работает достаточно хорошо. Однако он по-прежнему ограничен одним и тем же процессом. Задачи, требовательные к ресурсам, по-прежнему будут влиять на производительность всего процесса. В качестве последнего шага давайте рассмотрим распределение рабочей нагрузки между несколькими процессами и, возможно, даже несколькими машинами.
Очередь — единственное соединение между производителями и потребителями. Прямо сейчас он использует реализацию в памяти.Давайте черпаем вдохновение из Sidekiq и реализуем очередь с помощью Redis.
Redis поддерживает списки, которые позволяют нам отправлять и извлекать задачи. Кроме того, гем Redis Ruby является потокобезопасным, а команды Redis для изменения списков атомарны. Эти свойства позволяют использовать его для нашей системы асинхронной фоновой обработки без проблем с синхронизацией.
Давайте создадим очередь с поддержкой Redis, которая реализует методы push
и shift
точно так же, как Queue
, которые мы использовали ранее.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 год 22 23 | требуется json требовать 'redis' модуль Magique модуль Backend класс Redis def initialize (соединение = :: Redis.new) @connection = соединение конец def push (работа) @ connection.lpush ('магия: очередь', JSON.dump (задание)) конец def shift _queue, job = @ connection.brpop ('магия: очередь') payload = JSON.parse (задание, symbolize_names: true) полезная нагрузка [: worker] = Объект.const_get (полезная нагрузка [: работник]) полезная нагрузка конец конец конец конец |
Поскольку Redis ничего не знает об объектах Ruby, мы должны сериализовать наши задачи в JSON перед сохранением их в базе данных с помощью команды lpush
, которая добавляет элемент в начало списка.
Чтобы получить задачу из очереди, мы используем команду brpop
, которая получает последний элемент из списка. Если список пуст, он будет заблокирован до тех пор, пока не станет доступен новый элемент.Это хороший способ приостановить наши процессоры, когда нет доступных задач. Наконец, после получения задачи из Redis мы должны найти настоящий класс Ruby на основе имени рабочего, используя Object.const_get
.
В качестве последнего шага давайте разделим все на несколько процессов. Что касается производителя, единственное, что нам нужно сделать, это изменить серверную часть на нашу недавно реализованную очередь Redis.
1 2 3 4 5 6 7 | №... Magique.backend = Magique :: Backend :: Redis.new RUBYMAGIC.each do | url | TitleExtractorWorker.perform_async (url) конец |
С потребительской точки зрения мы можем обойтись несколькими строчками вроде этого:
1 2 3 4 5 6 | № ... Magique.backend = Magique :: Backend :: Redis.new Magique :: Processor.start (5) цикл {сон 1} |
При выполнении процесс-потребитель будет ждать поступления новой работы в очередь.Как только мы запускаем процесс производителя, который помещает задачи в очередь, мы видим, что они немедленно обрабатываются.
Наслаждайтесь ответственно и не используйте это в производстве
Хотя мы держали его подальше от реальных настроек, которые вы могли бы использовать в производстве (так что не надо!), Мы предприняли несколько шагов для создания фонового процессора. Мы начали с того, что заставили процесс работать как фоновую службу. Затем мы сделали его асинхронным и использовали Queue
для решения проблемы производителя-потребителя. Затем мы расширили процесс до нескольких процессов или машин, используя Redis, а не реализацию в памяти.
Как упоминалось ранее, это упрощенная реализация системы фоновой обработки. Многие вещи упущены и не рассматриваются в явной форме. К ним относятся (но не ограничиваются этим) обработка ошибок, множественные очереди, планирование, объединение соединений и обработка сигналов.
Тем не менее, нам было весело писать это, и мы надеемся, что вам понравилось заглянуть под капот системы фоновой обработки. Возможно, вы даже забрали кое-что.
Приглашенный писатель Бенедикт Дайке (Benedikt Deicke) — инженер-программист и технический директор Userlist.io. Попутно он пишет книгу о создании приложений SaaS на Ruby on Rails. Вы можете связаться с Бенедиктом через Twitter.
Основы активной работы — Руководства по Ruby on Rails
1 Что такое активная работа?
Active Job — это структура для объявления заданий и их выполнения в различных
бэкэндов очередей. Эти задания могут быть разными: от регулярных до запланированных.
уборки, выставления счетов, рассылки. Все, что можно нарезать
на небольшие единицы работы и на самом деле выполняются параллельно.
2 Цель активного задания
Главное — убедиться, что все приложения Rails будут иметь инфраструктуру заданий
на месте. Затем мы можем добавить фреймворки и другие жемчужины,
не беспокоясь о различиях API между различными исполнителями заданий, такими как
Отложенная работа и спасение. Выбор серверной части очереди становится более оперативным
тогда беспокойство. И вы сможете переключаться между ними, не перезаписывая
ваши рабочие места.
Rails по умолчанию поставляется с реализацией асинхронной организации очереди, которая
выполняет задания с пулом потоков внутри процесса.Задания будут выполняться асинхронно, но любые
задания в очереди будут отброшены при перезапуске.
3 Создание задания
В этом разделе приводится пошаговое руководство по созданию задания и постановке его в очередь.
3.1 Создание задания
Active Job предоставляет генератор Rails для создания заданий. Следующее создаст
вакансия в приложении / вакансии
(с прикрепленным тестом под тест / вакансии
):
$ bin / rails генерирует задание guest_cleanup
вызвать test_unit
создайте test / jobs / guest_cleanup_job_test.rb
создать приложение / jobs / guest_cleanup_job.rb
bin / rails генерирует задание guest_cleanup
Копировать
Вы также можете создать задание, которое будет выполняться в определенной очереди:
$ bin / rails генерирует задание guest_cleanup --queue срочно
bin / rails сгенерировать задание guest_cleanup —queue срочно
Копировать
Если вы не хотите использовать генератор, вы можете создать свой собственный файл внутри
app / jobs
, просто убедитесь, что он наследуется от ApplicationJob
.
Вот как выглядит работа:
класс GuestCleanupJob
class GuestCleanupJob выполнить
с любым количеством аргументов.
3.2 Поставить задание в очередь
Поставить задание в очередь следующим образом:
# Поставить в очередь задание, которое будет выполнено, как только система очередей будет
# свободный.
ГостиCleanupJob.perform_later guest
# Поставить задание в очередь, которое будет выполнено, как только система очередей будет
# свободный.
ГостиCleanupJob.perform_later guest
Копировать
# Поставить задание на выполнение завтра в полдень.
GuestCleanupJob.set (wait_until: Date.tomorrow.noon) .perform_later (гость)
# Поставить задание на завтра в полдень.GuestCleanupJob.set (wait_until: Date.tomorrow.noon) .perform_later (гость)
Копировать
# Поставить задание в очередь через 1 неделю.
GuestCleanupJob.set (ожидание: 1. неделя) .perform_later (гость)
# Поставить задание в очередь через 1 неделю.
GuestCleanupJob.set (ожидание: 1. неделя) .perform_later (гость)
Копировать
# `perform_now` и` perform_later` будут вызывать `Perform` под капотом, поэтому
# вы можете передать столько аргументов, сколько определено в последнем.
GuestCleanupJob.perform_later (гость1, гость2, фильтр: 'some_filter')
# `perform_now` и` perform_later` будут вызывать `Perform` под капотом, поэтому
# вы можете передать столько аргументов, сколько определено в последнем.GuestCleanupJob.perform_later (гость1, гость2, фильтр: 'some_filter')
Копировать
Вот и все!
4 Выполнение заданий
Для постановки в очередь и выполнения заданий в производстве вам необходимо настроить серверную часть очереди,
то есть вам нужно выбрать стороннюю библиотеку очередей, которую Rails должен использовать.
Сам Rails предоставляет только внутрипроцессную систему очередей, которая хранит задания только в ОЗУ.
В случае сбоя процесса или перезагрузки машины все невыполненные задания будут потеряны.
асинхронный сервер по умолчанию.Это может быть хорошо для небольших приложений или некритичных задач, но большинство
производственным приложениям нужно будет выбрать постоянный бэкэнд.
4.1 Бэкэнды
Active Job имеет встроенные адаптеры для нескольких бэкэндов очередей (Sidekiq,
Resque, Delayed Job и др.). Чтобы получить актуальный список адаптеров
см. документацию по API для ActiveJob :: QueueAdapters.
4.2 Настройка серверной части
Вы можете легко настроить серверную часть очереди:
# config / application.rb
модуль YourApp
класс Application
# config / application.rb
модуль YourApp
класс Application
класс GuestCleanupJob
class GuestCleanupJob Поскольку задания выполняются параллельно вашему приложению Rails, большинство библиотек очередей Вот неполный список документации: Большинство адаптеров поддерживают несколько очередей. С Active Job вы можете запланировать class GuestCleanupJob # config / application.rb # app / jobs / guest_cleanup_job.rb class GuestCleanupJob # config / application.rb # app / jobs / guest_cleanup_job.rb MyJob.set (очередь:: другая_очередь) .perform_later (запись) Для управления очередью с уровня задания вы можете передать блок класс ProcessVideoJob ProcessVideoJob.perform_later (Video.last) Убедитесь, что серверная часть очереди "слушает" имя вашей очереди. Для некоторых Активное задание предоставляет перехватчики для запуска логики в течение жизненного цикла задания.Нравиться class GuestCleanupJob класс ApplicationJob Одна из самых распространенных задач в современном веб-приложении - отправка электронной почты за пределы # Если вы хотите отправить электронное письмо, используйте #deliver_now # Если вы хотите отправить электронное письмо через Active Job, используйте #deliver_later Использование асинхронной очереди из задачи Rake (например, для Каждое задание использует I18n.locale =: eo UserMailer.welcome (@user) .deliver_later # Электронная почта будет переведена на эсперанто. ActiveJob по умолчанию поддерживает следующие типы аргументов: Активное задание поддерживает GlobalID для параметров. Это дает возможность передавать вживую класс TrashableCleanupJob класс TrashableCleanupJob Список поддерживаемых типов аргументов можно расширить. Вам просто нужно определить свой собственный сериализатор: класс MoneySerializer # Преобразует сериализованное значение в правильный объект. и добавьте этот сериализатор в список: Rails.application.config.active_job.custom_serializers << MoneySerializer
Копировать Активное задание обеспечивает способ перехвата исключений, возникающих во время выполнения class GuestCleanupJob Неудачное задание не будет повторяться, если не настроено иное. Также можно повторить или отменить задание, если во время выполнения возникло исключение. класс RemoteServiceJob GlobalID позволяет сериализовать полные объекты Active Record, переданные на Если пройденная запись удаляется после постановки задания в очередь, но до Подробные инструкции о том, как проверить свои вакансии, можно найти в Вам предлагается помочь улучшить качество этого руководства. Пожалуйста, внесите свой вклад, если вы заметите какие-либо опечатки или фактические ошибки. Вы также можете найти неполный контент или устаревшие вещи. Если по какой-либо причине вы заметили, что нужно исправить, но не можете исправить это самостоятельно, пожалуйста, И, наконец, что не менее важно, любое обсуждение Ruby on Rails. Вы разрабатываете приложение Ruby, в котором пользователь может зарегистрироваться, и после отправки формы пользователь должен получить электронное письмо.Вы пришлете его немедленно? Если это так, пользователю придется подождать, пока приложение подключится к серверу электронной почты и отправит фактическое электронное письмо. Это плохо для пользователей. Чтобы решить эту проблему, электронную почту следует отправлять в фоновом режиме. Таким образом, его можно поставить в очередь для отправки позже, и пользователь быстро увидит страницу подтверждения. Намного лучше! Это выполняется системой очередей, которая работает в фоновом режиме, ожидая выполнения новых заданий. Это термин, используемый для обозначения задач.В нашем случае электронное письмо - это работа, которую нужно выполнить. Есть несколько популярных систем. Некоторым нужна база данных, например Delayed :: Job, в то время как другие предпочитают Redis, например Resque и Sidekiq. Некоторые общие задания, которые следует выполнять в фоновом режиме: Этой системе нужна база данных, потому что она использует таблицу для управления заданиями.«Задержанная» часть в его названии происходит от способа постановки задания в очередь: с использованием метода С Delayed :: Job его можно поставить в очередь следующим образом: Это всего лишь один метод посередине, очень удобный. Однако класс также можно адаптировать так, чтобы метод всегда обрабатывался Delayed :: Job асинхронно. Это достигается с помощью помощника Delayed :: Job даже может назначать приоритеты, запускаться в заданное время и помещаться в несколько очередей. Например, чтобы запустить задание за 5 минут с приоритетом 2, поставьте его в очередь следующим образом: Или используя функцию И затем вызов метода как раньше: Resque основан на Delayed :: Job, но использует Redis вместо базы данных. Он также предоставляет приложение Sinatra для мониторинга заданий, чтобы вы могли видеть, какие из них выполняются и их очереди. Чтобы сделать класс совместимым с Resque, чтобы его можно было запускать в фоновом режиме, необходимо реализовать новый метод: Метод Чтобы запланировать запуск задания в указанное время, Resque требуется гем Resque не поддерживает числовые приоритеты как Delayed :: Job, но основывается на порядке определения очередей. Таким образом, первые имеют более высокий приоритет. Это определяется при запуске процесса: Resque будет проверять «высокую» очередь и выполнять свои задания.Когда эта очередь пуста, она начнет проверку «низкой» очереди. Затем класс должен определить используемую очередь: Sidekiq также нуждается в Redis для работы, но его главное отличие состоит в том, что он использует потоки, поэтому несколько заданий могут выполняться параллельно с использованием одного и того же процесса Ruby. Кроме того, он использует тот же формат сообщений, что и Resque, поэтому миграция должна быть простой. Зная использование потоков, становится ясно, что его основным намерением было создание самой быстрой системы обработки заданий для Ruby.Это в несколько раз быстрее, чем Resque и Delayed :: Job. Подводя итог, можно сказать, что это простая в использовании система, которую я люблю применять для новых проектов и ту, которую я рекомендую сейчас. Как и Resque, он включает приложение Sinatra для отслеживания заданий: какие из них запланированы, какие завершились сбоем и ожидают повторной попытки и т. Д. Поскольку он совместим с Resque, классы, обрабатываемые Sidekiq, должны реализовывать метод Также очень похоже на выполнение этого задания в будущем: Он даже поддерживает синтаксис Delayed :: Job: Для назначения приоритета заданию используется тот же подход, что и Resque: строгий порядок очереди.При их создании приоритет имеет первые. Опять же, это определяется при запуске Sidekiq: Это один из способов определения очередей. Их также можно определить как взвешенные очереди, поэтому их можно выбирать случайным образом с учетом того, что одна может иметь больший вес, чем другая, и, следовательно, с большой вероятностью будет выбрана: Теперь есть 2 очереди, которые можно представить в виде массива и выполнить выборку: Таким образом, у «низкой» очереди будет больше возможностей для выбора, независимо от того, пуста ли «высокая» очередь. Как и в Resque, класс должен определять используемую очередь: Существуют неофициальные гемы, которые поддерживают числовые приоритеты Delayed :: Job, но Sidekiq не поддерживает их в готовом виде. Поскольку каждая система имеет свой собственный синтаксис, Rails предлагает ActiveJob, стандартный способ работы с этими системами, поэтому приложение может быть независимым и иметь возможность переключаться между системами, как при использовании ActiveRecord для базы данных, сохраняя тот же синтаксис. Первый шаг - настроить его с выбранной системой. Инструкции обычно предоставляются самой системой. Классы и способ постановки объектов в очередь очень похожи на Sidekiq: Конечно, он поддерживает планирование: Способ определения очереди для класса такой же, как и в Sidekiq: Но его также можно установить при постановке задания в очередь: Обратите внимание, что эти очереди должны быть созданы с использованием выбранной системы очередей при запуске процесса. Фоновые задания - неотъемлемая часть любого большого проекта.Решение о выборе системы управления очередью придется принимать рано или поздно. Хотя есть и другие активные альтернативы, эти 3 являются наиболее популярными. Я рекомендую Sidekiq, потому что он прост в использовании и эффективен. Это также очень быстро по сравнению с Delayed :: Job и Resque. Его отличный пользовательский интерфейс для управления заданиями и очередями также может дать ему еще один балл. Тем не менее, для этого требуется Redis, поэтому, если вашему приложению не хватает оперативной памяти, возможно, было бы неплохо придерживаться Delayed :: Job и вашей базы данных.Это было бы хорошим началом, и с помощью ActiveJob вы всегда можете перейти на Sidekiq в будущем, не меняя ни одной строчки кода. Наш агент Ruby автоматически инструментирует несколько общих сред фоновых заданий. Вы также можете настроить его для отслеживания любых фоновых задач. Данные из фоновых заданий отображаются на странице Транзакции в APM как Не веб-транзакции . Следующие структуры фоновых заданий поддерживаются по умолчанию в последних версиях агента Ruby: Пользователи JRuby могут столкнуться с проблемами с показателями ЦП. Если вы используете эти платформы, мониторинг фоновых заданий обычно не требует дополнительной настройки. Вы можете настроить настраиваемые фоновые задания так, чтобы они отображались на странице APM Транзакции как Не веб-транзакции . Чтобы отслеживать Не-веб-транзакции при использовании неподдерживаемой платформы, необходимо добавить настраиваемый инструментарий. В качестве примера фоновое задание периодически запускает задачу под названием Добавьте модуль Используйте директиву Добавьте Вы можете передать строку в Используя API агента Ruby, вы можете назначить определенные методы для отслеживания Не-веб-транзакций . Это собирает трассировки для медленно выполняющихся заданий и связывает зафиксированные ошибки с транзакциями. Чтобы инструментировать метод класса, используйте singleton В качестве примера фоновое задание периодически запускает задачу под названием Добавьте модуль Используйте директиву Добавьте Дополнительные сведения см. в разделе «Пользовательские метрики Ruby». Убедитесь, что процесс не запущен, прежде чем агент подключится к внутренним серверам.Для этого сделайте агент Ruby синхронно подключенным к New Relic вместо асинхронного поведения по умолчанию. Используйте require 'new_relic / agent' NewRelic :: Agent.manual_start (: sync_startup => true) Используя require 'new_relic / agent' Если процесс короче, чем цикл сбора агента, необходимо вручную завершить работу агента с помощью Настройка newrelic.yml зависит от контекста фонового приложения. Если ваше фоновое приложение не является приложением Rails, на котором уже запущен агент Ruby, скопируйте файл newrelic.yml в каталог, в котором вы запускаете фоновое задание, или в подкаталог config . Убедитесь, что он включает ваш лицензионный ключ. Фоновые задания, которые не выполняются в контексте Rails, будут проверять переменную среды Если ваше фоновое задание выполняется в контексте существующего веб-приложения, которое уже отслеживается New Relic, агент Ruby автоматически выберет ваш существующий файл newrelic.yml . Фоновые задания, которые загружают среду Rails вашего приложения, будут использовать переменную среды Вы можете сделать так, чтобы задания, которые выполняются в контексте существующего веб-приложения New Relic, отображались под другим именем приложения в пользовательском интерфейсе APM. Начните до того, как Задайте для переменной среды $ NEW_RELIC_APP_NAME = "Мои фоновые задания" ./bin/my_background_worker.rb В большинстве случаев агент Ruby запускается автоматически, как только вам Автономные скрипты, работающие без Rails, обычно запускают агент, как только им Если вы используете гем демонов для запуска фоновых задач, агент Ruby может не запуститься, а также не будет вести журнал. Это происходит из-за того, что гем демонов изменяет рабочий каталог на Чтобы агент мог запускаться в этой ситуации, задайте переменные среды с местоположениями файла конфигурации агента и файла журнала; например: ENV ['NRCONFIG'] || = Имя_файла (__ FILE__) + '/../../config/newrelic.yml' ENV ['NEW_RELIC_LOG'] || = Имя_файла (__ FILE__) + '/../../log/newrelic_agent.log' Дополнительные сведения см. В документации по управлению запуском агента Инструкции по запуску агента применяются, когда выполнение фоновых заданий в демоне.Если сценарий выполняет одну фоновую задачу и завершает работу, вручную выключите агент с помощью Дополнительные ресурсы документации включают:
требуют, чтобы вы запустили службу очередей для конкретной библиотеки (в дополнение к
запуская ваше приложение Rails), чтобы обработка задания работала.Обратитесь к библиотеке
документацию с инструкциями по запуску серверной части очереди. 5 очередей
задание для запуска в определенной очереди:
класс GuestCleanupJob
конфиг.active_job.queue_name_prefix
в application.rb
:
# config / application.rb
модуль YourApp
класс Application
модуль YourApp
класс Application # app / jobs / guest_cleanup_job.rb
class GuestCleanupJob
class GuestCleanupJob
класс GuestCleanupJob
конфиг.active_job.queue_name_delimiter
в application.rb
:
# config / application.rb
модуль YourApp
класс Application
модуль YourApp
класс Application # приложение / вакансии / guest_cleanup_job.rb
class GuestCleanupJob
класса
class GuestCleanupJob : очередь
опция к установить
:
MyJob.установить (очередь:: другая_ очередь) .perform_later (запись)
Копировать queue_as
. В
блок будет выполнен в контексте задания (чтобы он мог получить доступ к self.arguments
),
и он должен возвращать имя очереди:
класс ProcessVideoJob
ProcessVideoJob.perform_later (Video.last)
Копировать
backends вам нужно указать очереди для прослушивания. 6 Обратных вызовов
другие обратные вызовы в Rails, вы можете реализовать обратные вызовы как обычные методы
и используйте метод класса в стиле макроса, чтобы зарегистрировать их как обратные вызовы:
класс GuestCleanupJob
style, если код внутри вашего блока настолько короткий, что умещается в одну строку.
Например, вы можете отправлять показатели для каждого задания в очереди:
класс ApplicationJob
before_enqueue
around_enqueue
after_enqueue
до_выполнения
вокруг_перформ
после_выполнения
7 Action Mailer
цикла запрос-ответ, поэтому пользователю не нужно его ждать.Активная работа
интегрирован с Action Mailer, поэтому вы можете легко отправлять электронные письма асинхронно:
# Если вы хотите отправить электронное письмо сейчас, используйте #deliver_now
UserMailer.welcome (@user) .deliver_now
# Если вы хотите отправить электронное письмо через Active Job, используйте #deliver_later
UserMailer.welcome (@user) .deliver_later
UserMailer.welcome (@user) .deliver_now
UserMailer.добро пожаловать (@user) .deliver_later
Копировать
отправить электронное письмо с использованием .deliver_later
), как правило, не будет работать, потому что Rake будет
вероятный конец, в результате чего пул потоков внутри процесса будет удален, прежде чем любой / все
из .deliver_later
писем обрабатываются. Чтобы избежать этой проблемы, используйте
.deliver_now
или запустите постоянную очередь в разработке. 8 Интернационализация
I18n.языковой стандарт
установлен при создании задания. Это полезно, если вы отправляете
электронные письма асинхронно:
I18n.locale =: eo
UserMailer.welcome (@user) .deliver_later # Электронная почта будет переведена на эсперанто.
Копировать 9 Поддерживаемые типы аргументов
NilClass
, String
, Integer
, Float
, BigDecimal
, TrueClass
, FalseClass
) Символ
Дата
Время
Дата и время
ActiveSupport :: TimeWithZone
ActiveSupport :: Продолжительность
Хэш
(ключи должны быть типа String
или Symbol
) ActiveSupport :: HashWithIndifferentAccess
Массив
Модуль
Класс
9.1 GlobalID
Объекты Active Record для вашей работы вместо пар класса / идентификатора, которые у вас тогда есть
десериализовать вручную. Раньше вакансии выглядели так:
класс TrashableCleanupJob
класс TrashableCleanupJob
GlobalID :: Identification
, который
по умолчанию был смешан с классами Active Record. 9.2 Сериализаторы
класс MoneySerializer
"currency" => money.currency
)
конец
def deserialize (хэш)
Деньги.новый (хэш ["сумма"], хеш ["валюта"])
конец
конец
Копировать
Rails.application.config.active_job.custom_serializers << MoneySerializer
10 исключений
работа:
класс GuestCleanupJob
10.1 Повтор или отмена невыполненных заданий
Например:
класс RemoteServiceJob
10.2 Десериализация
#perform
. #perform
метод называется Active Job вызовет ActiveJob :: DeserializationError
исключение. 11 Тестирование вакансий
руководство по тестированию. Обратная связь
Для начала вы можете прочитать наш раздел документации.
Пожалуйста, добавьте недостающую документацию для main. Обязательно проверьте
Edge Guides сначала проверят
если проблемы уже исправлены или нет в основной ветке.
Ознакомьтесь с Руководством по Ruby on Rails Guides
для стиля и условностей.
открыть вопрос.
документация приветствуется в списке рассылки rubyonrails-docs. фоновых заданий в Ruby
С задержкой :: Задание
delay
. Итак, если у нас есть объект, который нужно запустить следующим образом: объект.метод!
object.delay.method!
handle_asynchronously
: статистика класса
def calculate_totals (параметр1, параметр2)
# длительный метод
конец
handle_asynchronously: calculate_totals
конец
stats = Статистика.новый
stats.calculate_totals (param1, param2) # нет необходимости вызывать задержку
object.delay (run_at: 5.minutes.from_now, приоритет: 2) .method!
handle_asynchronously
, мы могли бы записать это следующим образом: handle_asynchronously: calculate_totals,
run_at: proc {5.minutes.from_now},
приоритет: 2
stats.calculate_totals (param1, param2)
Спасение
выполнить
.Это метод, который будет вызываться Resque: статистика класса
def self.perform (параметр1, параметр2)
Calcul_totals (параметр1, параметр2)
конец
частный
def calculate_totals (параметр1, параметр2)
# длительный метод
конец
конец
Resque.enqueue (Статистика, param1, param2)
self.perform
может делать что угодно, ему не обязательно быть «вызывающим», как в этом случае. Я мог бы переместить внутрь длительный метод. resque-scheduler
. После установки задания могут быть поставлены в очередь следующим образом: Resque.enqueue_in (5.minutes, Stats, param1, param2)
$ QUEUES = высокое, низкое восстановление рейка: работа
статистика класса
@queue =: высокий
# остальная часть кода
конец
Sidekiq
perform
: статистика класса
включить Sidekiq :: Worker
def self.выполнить (параметр1, параметр2)
Calcul_totals (параметр1, параметр2)
конец
частный
def calculate_totals (параметр1, параметр2)
# длительный метод
конец
конец
Stats.perform_async (параметр1, параметр2)
Stats.perform_in (5.minutes, param1, param2)
object.delay.method!
sidekiq -e development -i 0 -q high -q low
sidekiq -e development -i 0 -q high, 3 -q low, 1
# определить очереди
array = ["высокий", "высокий", "высокий", "низкий"]
# Выбери один
множество.образец
статистика класса
queue_as: high
# остальная часть кода
конец
ActiveJob
статистика класса
def выполнить (параметр1, параметр2)
Calcul_totals (параметр1, параметр2)
конец
частный
def calculate_totals (параметр1, параметр2)
# длительный метод
конец
конец
Stats.perform_later (param1, param2)
Статистика.set (ожидание: 5. минут) .perform_later (param1, param2)
статистика класса
queue_as: high
# остальная часть кода
конец
Stats.set (очередь:: высокий) .perform_later (param1, param2)
Заключение
Монитор фоновых процессов Ruby | New Relic Documentation
Поддерживаемые платформы
Важно
Мониторинг настраиваемых фоновых заданий
SalesOrganization # find_new_leads
. ControllerInstrumentation
. add_transaction_tracer
под определением метода : category =>: task
, чтобы сообщить агенту, что трассировка является не-веб-транзакцией .
требуется 'newrelic_rpm'
класс SalesOrganization
включают
:: NewRelic :: Agent :: Instrumentation :: ControllerInstrumentation
def find_new_leads
...
конец
add_transaction_tracer: find_new_leads,: category =>: task
конец
: категорию
, но значения будут отображаться на странице APM Транзакции , только если строка начинается с OtherTransaction /
. Мониторинг пользовательских фоновых методов
.
SalesOrganization # find_new_leads
. ControllerInstrumentation
под определением метода . add_transaction_tracer
: category =>: task
, чтобы сообщить агенту, что трассировка является не веб-транзакцией .
требуется 'newrelic_rpm'
класс SalesOrganization
def self.find_new_leads
...
конец
class << self
включают
:: NewRelic :: Agent :: Instrumentation :: ControllerInstrumentation
add_transaction_tracer: find_new_leads,: category =>: task
конец
end
Отслеживание кратковременных процессов
manual_start
и передайте : sync_startup => true
option:
15
15
потребует код агента, и он будет следить за тем, чтобы агент не запускался, пока вы не запустите его вручную. :: NewRelic :: Agents.shutdown
, чтобы гарантировать отправку всех данных в очереди. Настройка newrelic.yml для фоновых процессов
Фоновое приложение без Rails
NEW_RELIC_ENV
, чтобы определить, какой раздел файла конфигурации следует читать, возвращаясь к переменным среды RUBY_ENV
, RAILS_ENV
и RACK_ENV
в последовательность, и, наконец, по умолчанию разработка
, если ни одна из этих переменных среды не установлена. Среда фонового задания, отслеживаемая New Relic
RAILS_ENV
, чтобы определить, какой раздел файла newrelic.yml читать. Отчет для альтернативного имени приложения
newrelic_rpm
потребует ваш рабочий код. NEW_RELIC_APP_NAME
имя приложения, которое будет использоваться для фоновых заданий при запуске фоновых рабочих процессов. Это переопределит настройку app_name
в вашем newrelic.yml .
Убедитесь, что агент запускается
потребуется 'newrelic_rpm'
, если только агент не обнаружит имя исполняемого файла из черного списка, имя задачи rake или константу. Это предотвращает его запуск во время обычных задач rake и сеансов интерактивной консоли. Автономный скрипт без Rails
потребуется 'newrelic_rpm'
.Если у вас есть сценарий, который разветвляется или демонизируется до того, как он начинает свою основную работу, вы можете отложить этот вызов require
до завершения начальной настройки. Фоновые задачи с гемом демонов
/
перед выполнением фонового кода. Затем агент пытается разрешить пути к своему файлу конфигурации и файлу журнала относительно текущего рабочего каталога хост-процесса.
Сценарии монитора
:: NewRelic :: Agents.shutdown
, когда сценарий завершится. Это гарантирует, что сборщик New Relic получит данные. Например:
требуется newrelic_rpm
класс SalesOrganization
включают
:: NewRelic :: Agent :: Instrumentation :: ControllerInstrumentation
def find_new_leads
...
конец
add_transaction_tracer: find_new_leads,: category =>: task
конец
Организация продаж.new.find_new_leads
:: NewRelic :: Agent.shutdown
Для получения дополнительной помощи
mperham / sidekiq: Простая и эффективная фоновая обработка для Ruby
Простая и эффективная фоновая обработка для Ruby.
Sidekiq использует потоки для одновременной обработки множества заданий в
тот же процесс. Он не требует Rails, но будет тесно интегрироваться с
Rails для упрощения фоновой обработки.
Производительность
Версия | Задержка | Мусор создан для 10 тысяч рабочих мест | Время обработать 100 тыс. Заданий | Пропускная способность | Рубин |
---|---|---|---|---|---|
Sidekiq 6.0,2 | 3 мс | 156 МБ | 14,0 сек | 7100 работ / сек | МРТ 2.6.3 |
Sidekiq 6.0.0 | 3 мс | 156 МБ | 19 секунд | 5200 рабочих мест / сек | МРТ 2.6.3 |
Sidekiq 4.0.0 | 10 мс | 151 МБ | 22 секунды | 4500 рабочих мест / сек | |
Sidekiq 3.5.1 | 22 мс | 1257 МБ | 125 сек | 800 рабочих мест / сек | |
Resque 1.25,2 | – | – | 420 сек | 240 рабочих мест / сек | |
Задержка 4.1.1 | – | – | 465 сек | 215 рабочих мест / сек |
Этот тест можно найти в bin / sidekiqload
и предполагает сетевую задержку Redis в 1 мс.
Требования
- Redis: 4.0+
- Ruby: MRI 2.5+ или JRuby 9.2+.
Sidekiq 6.0 поддерживает Rails 5.0+, но не требует этого.
Установка
драгоценный камень установить sidekiq
Начало работы
См. Вики-страницу «Начало работы» и выполните простой процесс настройки.
Вы можете посмотреть этот плейлист на Youtube, чтобы узнать все о
Sidekiq и посмотрите на его возможности в действии. Вот веб-интерфейс:
Хотите обновить?
Я также продаю Sidekiq Pro и Sidekiq Enterprise, расширения для Sidekiq, которые предоставляют больше
функции, коммерческую лицензию и позволяют поддерживать высокие
качественная разработка с открытым исходным кодом одновременно.Пожалуйста, посмотрите
Домашняя страница Sidekiq для более подробной информации.
Подпишитесь на ежеквартальный информационный бюллетень , чтобы быть в курсе последних новостей
особенности и изменения Sidekiq и его старших братьев и сестер.
Проблемы?
Пожалуйста, не пишите напрямую никому из участников Sidekiq с вопросами или проблемами. Сообщество лучше всего обслуживает, когда обсуждения проводятся публично.
Если у вас возникла проблема, просмотрите вики-страницы "Часто задаваемые вопросы" и "Устранение неполадок".
Также хорошей идеей будет поиск проблем, связанных с вашей проблемой.
Клиенты
Sidekiq Pro и Sidekiq Enterprise получают частную поддержку по электронной почте. Вы можете приобрести на https://sidekiq.org; по электронной почте [email protected] для получения помощи.
Полезных ресурсов:
- Документация по продукту находится в вики.
- Время от времени делаются объявления в учетной записи Twitter @sidekiq.
- Тег Sidekiq на Stack Overflow содержит множество полезных вопросов и ответов.
Каждую пятницу утром в Sidekiq счастливый час: я общаюсь в видеочате и отвечаю на вопросы.Подробную информацию см. На странице поддержки Sidekiq.
Лицензия
Подробную информацию о лицензировании см. В ЛИЦЕНЗИИ.
Автор
Майк Перхэм, @getajobmike / @sidekiq, https://www.mikeperham.com / https://www.contribsys.com
фоновых заданий - как писать хорошие фоновые задания в Ruby On Rails?
В этой статье я подробно расскажу о рекомендациях и передовых методах написания эффективных фоновых заданий в вашем приложении Rails.
Во-первых, когда мы должны перевести наши веб-транзакции для обработки в фоновый режим? Ниже приведены три критерия , в которых мы должны использовать фоновые задания для обработки транзакций вместо того, чтобы выполнять их немедленно и заставлять пользователей ждать: -
- Транзакция всегда занимает больше, чем ваше среднее время отклика для завершения
- Транзакция связывается с внешней службой по сети
- Пользователю все равно, будет ли транзакция завершена немедленно
Теперь давайте поговорим о том, как мы можем писать безопасные, производительные и надежные фоновые задания.В основном есть несколько характеристик и передовых методов, которым мы можем следовать: -
Идемпотентность
В программировании термин «идемпотент» описывает операцию, которая дает одинаковый результат, если выполняется один или несколько раз. В нашем случае фоновое задание должно давать одинаковый результат (без побочных эффектов) независимо от того, сколько раз мы его запускали.
Ниже приведен пример неидемпотентного фонового задания, которое мы можем найти в большинстве приложений Rails: -
класс RegistrationMailJob
Если мы дважды запустим задание, описанное выше, мы отправим два приветственных письма, что плохо с точки зрения пользователя. При написании фонового задания мы всегда должны предполагать, что любое заданное задание может быть запущено более одного раза, поскольку большинство обработчиков фоновых заданий не могут гарантировать, что какое-либо заданное задание не будет запущено более одного раза. Обходной путь для этого - реализовать блокировку базы данных на уровне строк, как показано ниже: -
# app / models / user.rb
класс Пользователь
after_commit: send_signup_email
def send_signup_email
UserMailer.signup_email (сам) .deliver
конец
конец
# app / jobs / registration_mail_job.rb
класс RegistrationMailJob
Ссылаясь на приведенный выше пример, блок around_perform предотвратит следующие сценарии: -
- Если задание
RegistrationMailJob
ставится в очередь более одного раза. Электронное письмо будет отправлено только один раз. Первое задание установит для атрибутаuser.signup_email_sent
значение true, затем второе задание завершится после проверкипользователя.signup_email_sent
- В редком случае, когда два задания с одним и тем же пользователем выполняются одновременно, блок with_lock блокирует выполнение вторым рабочим заданием до тех пор, пока первое задание не будет завершено (как только первое задание будет завершено, пользователь
.signup_email_sent
будет установлено в значение true, и второе задание завершится согласно первому пункту) - Если метод доставки завершится неудачно, мы настроим задание на повторную попытку.
Написание минимально возможного задания
Вы должны писать свою работу как можно меньше с точки зрения строк кода и времени выполнения.По возможности вместо массовой обработки группы объектов в одном задании попробуйте разделить их на несколько заданий. Эмпирическое правило состоит в том, что каждое задание в очереди должно иметь одинаковое среднее время выполнения. Не должно быть никакой работы, которая занимает значительно больше времени, чем другие.
Настроить агрессивный тайм-аут
Чтобы работник не застрял на работе с чрезвычайно долгим временем отклика, мы можем установить агрессивный тайм-аут. Кроме того, поскольку мы пишем задания, которые являются идемпотентными, как упоминалось ранее, на самом деле нет причин для длительного тайм-аута, поскольку мы всегда можем повторить задание без каких-либо проблем.
Скажите нет уникальности работы
Если вы разработали свое задание как идемпотентное, вам не нужно беспокоиться об уникальности любого задания, поскольку идемпотентное задание может выполняться бесконечное количество раз без изменения вывода. С другой стороны, вы можете добиться желаемого, используя вместо этого дросселирование.
Правильная обработка ошибок
При возникновении исключения всегда следует реализовывать обработчик ошибок для любого заданного задания. Достаточно хорошо писать задания внутри транзакции базы данных или блокировки на уровне строки базы данных.Дело в том, что мы не хотим оставлять транзакции незавершенными, например, транзакция должна либо полностью завершиться сбоем и ничего не делать, либо завершиться успешно и вся работа должна быть завершена.
Использовать красный флаг на проблемной работе
Часто некоторые задания продолжают отказываться и никогда не завершаются. В таком случае вам необходимо установить флаг, чтобы мы были уведомлены, когда произойдет определенное количество сбоев, и можно было предпринять дальнейшие действия.
Например, Sidekiq имеет очередь «повторных попыток», и по умолчанию Sidekiq будет повторять выполнение вашего задания максимум 25 раз, прежде чем переместить задание в «мертвую» очередь.
Сводка
Мы всегда должны использовать фоновое задание, когда используются службы, связанные с внешней сетью, когда действие не нужно выполнять немедленно или когда действие может занять очень много времени. Фоновые задания всегда должны быть идемпотентными, чтобы мы могли запускать задание несколько раз, ничего не нарушая. Мы должны разбить наши фоновые задания на как можно более простые и маленькие, а не разбивать все на части в одно задание. Что касается установки тайм-аута, мы должны использовать более агрессивный подход к установке тайм-аута, поскольку лучше быстро выйти из строя, чем ждать ответа очень медленного фонового задания.И последнее, но не менее важное: у нас должен быть красный флаг, чтобы уведомлять нас о проблемной работе, которая терпела неудачу более определенного количества раз.
Ruby для администраторов: фоновые задания
Вилка
pid = вилка если pid # pid не равен нулю, мы находимся в родительском процессе помещает "Родительский процесс, дочерний pid # {pid}" помещает `ps -ef | grep # {pid} ` спать 5 ставит "Родительский процесс завершается" else # pid равен нулю, мы находимся в дочернем процессе ставит "Дочерний процесс, pid равен нулю" спать 2 ставит "Дочерний процесс завершается" конец
pid = fork do ставит "Дочерний процесс, pid равен нулю" спать 2 ставит "Дочерний процесс завершен" конец помещает "Родительский процесс, дочерний pid # {pid}" спать 5 ставит "Родительский процесс завершен"
Дождитесь завершения фонового процесса
pid1 = форк делать спать 10 помещает "Дочерний процесс 1 завершен" конец pid2 = fork do спать 5 помещает "Дочерний процесс 2 завершен" конец помещает "Родительский процесс, дочерние идентификаторы # {pid1}, # {pid2}" Процесс.подожди pid1 Process.wait pid2 ставит "Родительский процесс завершен"
Предотвратить зомби-апокалипсис
pid = fork do 10. раз сделать помещает `date` спать 1 конец ставит "Дочерний процесс завершен" конец помещает "Родительский процесс, дочерний pid # {pid}" Process.detach pid ставит "Родительский процесс завершен"
Когда ребенок стал демоном
pid = fork do $ stdin.reopen "/ dev / null" # игнорировать stdin $ stdout.reopen "/ tmp / test", "a" # stdout в файл $ stderr.повторно открыть "/ tmp / test", "a" # stderr в файл trap (: HUP) do # игнорировать SIGHUP помещает "SIGHUP получено, игнорируя" конец 60 раз. помещает `date` спать 1 конец ставит "Демонический процесс завершен" конец помещает "Родительский процесс, pid демона # {pid}" Process.detach pid ставит "Родительский процесс завершен"
Демонизируйте процесс с помощью Daemons Gem
требует 'демонов'Daemons.