application server, задача которого отрисовать UI, получить параметры запроса, максимально быстро сделать запрос в базу, и отрисовать результаты, и снова обработать следующий запрос. Таким образом, 1 или 2 воркера справляются с сотнями одновременных пользователей, так как запросы в базу быстрые, и пользователи не замечают тормозов.
В моем проекте OdooPBX мне нужно делать запросы в третью систему (Asterisk) по сети.
Например, получить статусы SIP пользователей.
И тут возникает проблема, так как делая это "в лоб", я подвешиваю воркер на сетевой операции ожидания ответа от Asterisk. Пока все "хорошо", Asterisk быстро отдает результат, все типа нормально. Но стоит по какой-либо причине астериску "заснуть", то воркер, находящийся в режиме ожидания, тоже "спит" - ждет ответа. Увеличением числа воркеров делу не поможешь - все равно все быстро забьются.
Что я придумал. Я использую внешний Job server (на базе Salt master).
Оду передает ему задачу (получить список SIP юзеров), и указывает res_model & res_method, который будет вызван внешним Job сервером после того, как он отработает и получит результаты. Он тут же вызывет по JSON-RPC нужный метод в Оду и отдаст ему данные. Таким образом, в Оду ничего не блокируется, но это накладывает ряд ограничений.
1) Приходится писать разорванный код. Т.е. вместо того, чтобы прямо в методе обработать результаты, нужно писать другой метод, который получит результаты. Таким образом, все функции, где есть внешние вызовы - разорванные.
2) Тестировать тоже не особо красиво.
3) Не удобно обработать ошибки.
4) Зависим от внешнего Job сервера.
Проблему можно решить, перейдя на gevent процесс. Тогда сетевая операция не блокирует Оду. Мы пишем цельные не разорванные функции в синхронном стиле, а gevent обо всем заботится.
Во втором подходе тоже есть свои минусы:
1) Особая установка - нужно объяснять, что надо обязательно прокинуть / на 8072 и использовать его. В некоторых случая это невозможно, например в odoo.sh (а у меня счас половина покупателей на odoo.sh живут).
2) Неизвестная надежность. Что если по какой-то причине какая-то операция "зависнет", например в вечном вычислении-цикле. Все сразу "встали". Ну или просто race condition какой-то, gevent же на уровне monkey patch работает.
3) Ну и проблемы с некоторыми операциями, например, транскодинга wav в mp3. Ладно когда у нас воркеры, парочку будут заняты секунд 5-10, другие будут "молотить". А в нашем gevent случае вычисляемый поток один, все будут ждать окончания транскодинга. Придется передавать такие операции через queue_job или odoo celery на обработку порту 8069, что опять же вносит новый компонент и усложняет систему.
В общем, у меня дилемма, куда пойти. А может есть другие варианты, которые я не увидел?
Буду благодарен за любые идеи!
Спасибо.
Тема крутая, но не для вечера пятницы 😄 В понедельник присоединюсь к обсуждению)
а вопрос - эти ваши операции которые могут подвиснуть надолго - они как взываются интерактивно юзером по какому-то действию - и надо ему еще что-то ответить? или это крон процесс, или изер что-то сделал но ответа не ждет
Я за внешний сервер для обработки задач. Главный плюс: возможность масштабирование без нагрузки на основную систему. Я бы вообще на него все ресурсоемкие операции вынес, включая конверсию файлов в мп3 итп. А на оду оставил бы только логику, которая отвечает за основные бизнес процессы.
> Итак, всем известно, что Odoo - это database application server, задача которого отрисовать UI, получить параметры запроса, максимально быстро сделать запрос в базу, и отрисовать результаты, и снова обработать следующий запрос. так оду давно не унижали)) php-программиста видно издалека
твои вводные 1. твоя задача получения sip пользователей выполняется долго 2. блокировать воркер оду на время выполнения этих длинных запросов - не рационально 3. ты не хочешь писать разорванные процедуры, которые сложно отлаживать Вывод: используй websocket-ы и отдельный сервер-прокладку, который откроет websocket-соединение с Asterisk-ом и будет его бесконечно держать. Оду посылает ему запросы, он общается с Asterisk-ом и отдает назад результаты. Разрывы конечно это не устранит, но тут либо разрывы, либо захват воркеров. Вобщем-то это то же самое, что ты сам придумал.
А как выглядит описание джоба для салт мастера (например получить список сип юзеров) — на каком языке? Может сделать свой джоб сервер на оду поверх queue_job? Тогда ничего разрывать не нужно: 1. На оба сервера устанавливаешь одинаковые модули. 2. Код для задачи (получить сип юзеров), выделяешь в отдельные метод с новым декоратором 3. Этот декоратор смотрит нужно ли передавать джоб на внешний сервер или выполнять самому (например для тестов) 4. Если передается на внешний сервер, то тот сервер выполняет ту же функцию 5. Если на внешнем сервере случилась ошибка, то он передает в рабочий сервер всю информацию и рабочий сервер печатает все через _logger.error (или можно в ir.logging сохранять даже) Без внешнего сервера сложно будет обойтись. Как раз из-за оду.сш. Но смотри на это как на возможность, а не проблему. Например можоно будет предоставлять свой джоб сервер по подписке)
Обсуждают сегодня