У вас что, функция хранит результат своей работы в каком-то внешнем состоянии? Может быть, вы ещё Stateful-код пишете и глобальными переменными балуетесь? А с параллелизацией никогда не имели дел?
Функция, выполняющая работу, должна возвращать инкапсулированный объект, из которого можно достать результат операции. Должно быть так:
# псевдокод
if ( find_defects().getResult().isSuccess() ) {… }
В нашей северной стране не принято общаться, как не принято улыбаться на улице. На это ещё накладывается интровертная натура аудитории Хабра, которая никогда не поймёт, зачем общаться, когда можно не общаться.
В то время как в зарубежной практике тема вполне популярная, примеры [раз], [два], под тем же соусом — чем больше человечной коммуникации — тем успешнее бизнес.
Ах, SOA и REST — мои любимые инструменты :) 975 ресурсов — респект парням!
Очень любопытно, почему отказавшись от концепции прямых вызовов RPC, они не перешли на очереди. Так как в отличие от REST (я имею в виду традиционно over http — ожидающего ответ в цикле запроса), очереди позволили бы отправлять ответ асинхронно, добавив ещё одну нотку к отказоустойчивости.
Когда я работал руководителем в одной русской CMS, мы такое тоннами писали.
Когда я работал руководителем в веб-студии, мы писали ровно то же самое, но наоборот, про самописный продукт.
А когда я работал в энтерпрайзе, и мы выбирали между коробкой или наймом команды, на нас лилось такое с обоих строн.
Хотите правды? Вообще по**й, самописный софт или коробочный, — если его пишут и внедряют дураки, которыми руководят м**аки, то жопа будет абсолютно одинаковая в обоих случаях.
Пытаться идти в IT-шные компании через резюме и HR — это тупиковый путь :) Вот недавно я приехал поговорить на топовую позицию (типа начальник всея девелопмента) к ребятам в Питере — так они не то чтобы по ссылкам на лэндинг не ходили, они вообще не имели никакого понятия о моём карьерном опыте, не читали мой профессиональный блог, не искали код моих проектов, не изучали посты в соцсетях. Они моё резюме в первый раз там и увидели — а оно большое, за 17 лет всякого набралось… Сидели, читали, угукали.
В то же время, из крупной московской конторы напрямую мне позвонил чувак — один из совладельцев. Болтали почти час, уже в середине болтовни перешли на «ты», перешли на профессиональный слэнг, обсуждали технологии, методики, знакомых профессионалов по отрасли и т.д.
В общем, фигня это всё про резюме и HR :) Их надо годами учить, чтобы они действительно разбирались в теме. Хотите нормально трудоустроиться — контачьте напрямую с целевыми людьми, тогда и никакого резюме не понадобится.
соотношение сигнал-шум в вашей цифровой жизни тут же катастрофически увеличивается
Гуманитарий статью писал? :) Сигнал-шум это дробь: «сигнал / шум» (сигнал разделить на шум). Когда оно увеличивается — это хорошо, это значит что полезного сигнала стало больше. А когда становится больше шума, то оно уменьшается.
Если хотели литературно выразиться в переносном смысле, можно было написать «катастрофически ухудшается».
Не сочтите за троллинг, я сам занимаюсь виртуализацией и сервисной архитектурой. Но мой самый больной вопрос: а зачем это всё, если ресурсы железа ограничены? Вы всё равно не сможете запустить больше воркеров (инстансов?) приложения, чем допустимо в рамках суммарного железа этого кластера. Вы не сможете сказать приложению «иди и запустись где-нибудь, где есть 512Mb оперативной памяти», если этой свободной оперативной памяти не осталось на железных машинах.
Популярные аргументы виртуализаторов о том, что дескать «если у нас возрастёт нагрузка — мы стартуем ещё 100500 воркеров и обслужим её» разбиваются элементарным утверждением, что достаточно держать постоянно запущенными 100500 воркеров — в режиме ожидания они всё равно практически не жрут ни память, ни CPU, ни электропитание. А в случае хостинга на арендованном железе, как правило, цена за него — фиксированная независимо от нагрузки.
Что полезного конкретно вам дала описанная виртуализация?
Респект! Но почему бы не искать прямо по EAV-модели в SQL? Это же очень легко делается, даже по произвольному количеству атрибутов. Взяли первый атрибут, по таблице связей entity_id — value_id получили множество товаров (entity_ids), далее взяли второй атрибут и сократили это множество, далее взяли следующий атрибут… и так далее.
Респект! Я делал похожую штуку, на несколько ином наборе инструментов.
Если позволите, несколько вопросов :)
— какой у вас роутинг в раббите? (директ, топик, фанаут?)
— как оно переживает деплой новой версии программного кода?
— что происходит в системе, если актор не справляется с обработкой сообщения и падает?
— данные о пользователях отдаются по явному запросу, или доставляются потребителям так же через очереди?
Отличная статья, но я огорчён тем что в ней нет даже попытки представить проектирование API как полностью идемпотентной системы — и соответственно, полностью иммутабельных данных.
Например, в идемпотентной системе не будет операции Edit Item — вместо неё будет каждый раз Create Item с новой версией содержимого. Тем самым мы «из коробки» получили бы историю версий (черновиков) документа, возврат к любой из них, статистику и т.д., и убрали бы риск ошибочной перезаписи документа. // Если кому-то это кажется экзотикой — знайте, что банальный Wordpress ведёт себя именно так.
В идемпотентной системе Status был бы не атрибутом документа, а отдельной сущностью, по которой предоставлялся бы отдельный «коробочный» CRUD-интерфейс (и далее либо document has status_id, либо status has document_id). Этот подход:
— решил бы задачи однозначности состояния документа (перевод в статус возможен только однократно, даже если кто-то по ошибке пошлёт несколько запросов),
— предоставил бы возможность указывать статусы для разных версий (черновиков) документов,
— возможность гибкого разграничения прав доступа для клиентов API (кто-то может только редактировать документ, а кто-то другой может задавать его статус),
— и «из коробки» дал бы возможность сохранять timestamp перехода в тот или иной статус.
То есть уже на втором пункте авторы вместо «придумайте сущность для каждого состояния» сразу идут по пути неиммутабельной системы, что уже неправильно.
Ребята, а нельзя было очередь перед демоном поставить? Ну повисели бы запросы в очереди пару миллисекунд, пока демон рестартует. Зачем так сложно делать то?
У вас по ТЗ сразу получился сервер задач, а не сервер очередей сообщений. Ключевое отличие как раз в том, с чего у вас начинается первая картинка — существование такого признака как «время активации». Отсюда и причины некоторых других различий.
А в целом всё круто, респект. Особенно респект за каскадные очереди, мы тоже голову сломали в своё время, пока додумались.
Респект.
Тоже пишу про SOA, но не на хабре, а в личном блоге.
На мой взгляд, осталась нераскрытой тема межсервисной коммуникации.
А это очень важно, например вопрос — синхронная она или нет. Должен ли отправляющий сервис ждать, пока получатель получит и обработает вызов. Может ли один бизнес-процесс распараллелиться на несколько одновременных межсервисных взаимодействий, а потом собраться в единый результат.
Мы на этом достаточно голову поломали в своё время.
Хороший пост, спасибо!
Однако, не раскрыты темы Dead-Letter Routing (роутинг при истечении срока хранения), Headers Routing, и определение отправителя сообщения (кто нам его прислал).
З.Ы.: там в предыдущем посте развернулось обсуждение за архитектуру :)
Именно 2 НЕуспеха здесь для иллюстрации ситуации, когда на одном неуспехе нельзя сделать окончательный вывод. Например, если у пользователя 99 рублей на счету, то можно его кредитовать на рубль (или уйти в минус на рубль), но всё равно предоставить услугу. Два фейла — это просто пример, но его тоже нужно учесть в проектировании.
Если пойти на то, чтобы хранить состояние [между сообщениями], то вся задача теряет актуальность. Однако и здесь вопрос гораздо шире. Дело в том, что «серьёзные» распределённые системы вообще требуют иного подхода, нежели общепринятый подход к программированию как к последовательности команд в программе.
В идеале, SOA-архитектура должна быть построена без хранимых состояний и разделяемой памяти. Почему этих штук следует избегать — вопрос слишком большой, чтобы описать его в рамках топика. Говоря кратко, надо строить систему из «тупых», легко заменяемых и дублируемых кирпичиков — конечных автоматов, а функционировать они должны по принципам архитектуры потока данных.
Материалы для любопытных читателей:
— общий обзор habrahabr.ru/post/122479/
— для самостоятельного гугления: event-driven development, паттерн observer-notifier, SOA-архитектура, архитектура потока данных, основы философии erlang, конечные автоматы, принципы фон-неймана.
Если мы разрешаем себе хранение состояний, тогда (казалось бы) вся задача теряет актуальность: храним промежуточные результаты в редисе и не паримся. Однако я могу совершенно точно сказать, что нельзя иметь хранимые состояния «на входе» в систему, где любой неавторизованный пользователь может нам этот редис засрать, навалив кучу (миллионы) входящих запросов. Это компромисс, на который надо идти очень осторожно.
У вас что, функция хранит результат своей работы в каком-то внешнем состоянии? Может быть, вы ещё Stateful-код пишете и глобальными переменными балуетесь? А с параллелизацией никогда не имели дел?
Функция, выполняющая работу, должна возвращать инкапсулированный объект, из которого можно достать результат операции. Должно быть так:
# псевдокод
if ( find_defects().getResult().isSuccess() ) {… }
Или хотя бы так:
# псевдокод
try { find_defects(); } catch Exception $e {… }
А совсем правильно — так:
# псевдокод:
successManager.subscribe('find_defects.results.success');
failManager.subscribe('find_defects.results.fail');
function find_defects() {
# do some work…
dispatch(results)
}
Так оно будет правильно работать в распределённых системах.
В нашей северной стране не принято общаться, как не принято улыбаться на улице. На это ещё накладывается интровертная натура аудитории Хабра, которая никогда не поймёт, зачем общаться, когда можно не общаться.
В то время как в зарубежной практике тема вполне популярная, примеры [раз], [два], под тем же соусом — чем больше человечной коммуникации — тем успешнее бизнес.
Очень любопытно, почему отказавшись от концепции прямых вызовов RPC, они не перешли на очереди. Так как в отличие от REST (я имею в виду традиционно over http — ожидающего ответ в цикле запроса), очереди позволили бы отправлять ответ асинхронно, добавив ещё одну нотку к отказоустойчивости.
Когда я работал руководителем в одной русской CMS, мы такое тоннами писали.
Когда я работал руководителем в веб-студии, мы писали ровно то же самое, но наоборот, про самописный продукт.
А когда я работал в энтерпрайзе, и мы выбирали между коробкой или наймом команды, на нас лилось такое с обоих строн.
Хотите правды? Вообще по**й, самописный софт или коробочный, — если его пишут и внедряют дураки, которыми руководят м**аки, то жопа будет абсолютно одинаковая в обоих случаях.
В то же время, из крупной московской конторы напрямую мне позвонил чувак — один из совладельцев. Болтали почти час, уже в середине болтовни перешли на «ты», перешли на профессиональный слэнг, обсуждали технологии, методики, знакомых профессионалов по отрасли и т.д.
В общем, фигня это всё про резюме и HR :) Их надо годами учить, чтобы они действительно разбирались в теме. Хотите нормально трудоустроиться — контачьте напрямую с целевыми людьми, тогда и никакого резюме не понадобится.
Если хотели литературно выразиться в переносном смысле, можно было написать «катастрофически ухудшается».
Rabbit как-то более предназначен для каналов (очередей) сообщений, да и с роутингом значительно интереснее.
Популярные аргументы виртуализаторов о том, что дескать «если у нас возрастёт нагрузка — мы стартуем ещё 100500 воркеров и обслужим её» разбиваются элементарным утверждением, что достаточно держать постоянно запущенными 100500 воркеров — в режиме ожидания они всё равно практически не жрут ни память, ни CPU, ни электропитание. А в случае хостинга на арендованном железе, как правило, цена за него — фиксированная независимо от нагрузки.
Что полезного конкретно вам дала описанная виртуализация?
По каким причинам этот подход был отвергнут?
Если позволите, несколько вопросов :)
— какой у вас роутинг в раббите? (директ, топик, фанаут?)
— как оно переживает деплой новой версии программного кода?
— что происходит в системе, если актор не справляется с обработкой сообщения и падает?
— данные о пользователях отдаются по явному запросу, или доставляются потребителям так же через очереди?
Например, в идемпотентной системе не будет операции Edit Item — вместо неё будет каждый раз Create Item с новой версией содержимого. Тем самым мы «из коробки» получили бы историю версий (черновиков) документа, возврат к любой из них, статистику и т.д., и убрали бы риск ошибочной перезаписи документа. // Если кому-то это кажется экзотикой — знайте, что банальный Wordpress ведёт себя именно так.
В идемпотентной системе Status был бы не атрибутом документа, а отдельной сущностью, по которой предоставлялся бы отдельный «коробочный» CRUD-интерфейс (и далее либо document has status_id, либо status has document_id). Этот подход:
— решил бы задачи однозначности состояния документа (перевод в статус возможен только однократно, даже если кто-то по ошибке пошлёт несколько запросов),
— предоставил бы возможность указывать статусы для разных версий (черновиков) документов,
— возможность гибкого разграничения прав доступа для клиентов API (кто-то может только редактировать документ, а кто-то другой может задавать его статус),
— и «из коробки» дал бы возможность сохранять timestamp перехода в тот или иной статус.
То есть уже на втором пункте авторы вместо «придумайте сущность для каждого состояния» сразу идут по пути неиммутабельной системы, что уже неправильно.
А в целом всё круто, респект. Особенно респект за каскадные очереди, мы тоже голову сломали в своё время, пока додумались.
Тоже пишу про SOA, но не на хабре, а в личном блоге.
На мой взгляд, осталась нераскрытой тема межсервисной коммуникации.
А это очень важно, например вопрос — синхронная она или нет. Должен ли отправляющий сервис ждать, пока получатель получит и обработает вызов. Может ли один бизнес-процесс распараллелиться на несколько одновременных межсервисных взаимодействий, а потом собраться в единый результат.
Мы на этом достаточно голову поломали в своё время.
Однако, не раскрыты темы Dead-Letter Routing (роутинг при истечении срока хранения), Headers Routing, и определение отправителя сообщения (кто нам его прислал).
З.Ы.: там в предыдущем посте развернулось обсуждение за архитектуру :)
Если пойти на то, чтобы хранить состояние [между сообщениями], то вся задача теряет актуальность. Однако и здесь вопрос гораздо шире. Дело в том, что «серьёзные» распределённые системы вообще требуют иного подхода, нежели общепринятый подход к программированию как к последовательности команд в программе.
В идеале, SOA-архитектура должна быть построена без хранимых состояний и разделяемой памяти. Почему этих штук следует избегать — вопрос слишком большой, чтобы описать его в рамках топика. Говоря кратко, надо строить систему из «тупых», легко заменяемых и дублируемых кирпичиков — конечных автоматов, а функционировать они должны по принципам архитектуры потока данных.
Материалы для любопытных читателей:
— общий обзор habrahabr.ru/post/122479/
— для самостоятельного гугления: event-driven development, паттерн observer-notifier, SOA-архитектура, архитектура потока данных, основы философии erlang, конечные автоматы, принципы фон-неймана.
Если мы разрешаем себе хранение состояний, тогда (казалось бы) вся задача теряет актуальность: храним промежуточные результаты в редисе и не паримся. Однако я могу совершенно точно сказать, что нельзя иметь хранимые состояния «на входе» в систему, где любой неавторизованный пользователь может нам этот редис засрать, навалив кучу (миллионы) входящих запросов. Это компромисс, на который надо идти очень осторожно.