Opened 8 years ago
Last modified 7 years ago
#482 closed задача
Алгоритм — at Version 29
Reported by: | san | Owned by: | dimag |
---|---|---|---|
Priority: | critical | Milestone: | |
Component: | ПО MC04-Dispatcher. Пульт диспетчера/техника | Keywords: | |
Cc: | alx |
Description (last modified by )
Описать алгоритм взаимодействия программы с ES.
(как отправляются команды, принимаются ответы, таймауты, и прочие особности работы)
Пока в алгоритме обнаружены следующие недостатки:
- Следуюшую команду на сервер нельзя отправлять не дождавшись ответа, а программа так делает
- Зачем ждать ивента 60мс, если и так всё складывается в очередь. Нет объяснения.
- Каков смысл отправки двух команд "проверки связи". Нет объяснения.
Change History (29)
follow-up: 6 comment:2 by , 8 years ago
Работа потока сообщений организованна следующим образом:
Таймер вызывает функцию в потоке для считывания команд с интервалом 1 раз в 50 миллисекунд.
1)Раз в пять секунд проверяем наличие связи с сервером, если связь есть:
Для этого:
Отправляем первый запрос bgapi status, ответ на него не смотрим, смотрим принята ли команда на FreeSwitch сервере к обработке. Запрос отправляется с помощью функции ниже:
esl_send_recv_timed(handle, request.c_str(), iDelay),
где handle - идентификатор соединения, _cmd-команда, iDelay-время ожидания, для данного равно 200, что означает, что ждать ответа 200 мс, если нет, то считать команду не выполненной.
Если первый запрос был получен FreeSwitch сервером, неудачен, то топравляем повторный запрос но со временем ожидания 600 мс, если и этот запрос неудачен, то считаем что соединения нет, выводим соответствующее сообщение, выходим
2) Если нет соединения, то каждую секунду пытаемся его восстановить. Если соединение успешно восстановилось, скрываем ошибку, обновляем информацию о текущих пользователях и конференциях. Если не восстановилось, то ожидаем в течение 1 секунды, делаем новую попытку соединения.
3)Если соединение есть, то считываем накопленные сообщения с помощью
esl_recv_event_timed(handle, 60, 1, NULL), где handle - идентификатор соединения, 60 - время ожидания ответа с сервером, 1 - возвращать последнее сохранённое сообщение, NULL - не сохранять сообщение в специально отведённой структуре, а сохранять в handle.
Если сообщение считано то пробуем считать другие с помощью той же функции, пока все сообщения не будут выбраны из очереди.
Далее приступаем к обработке сообщений.
comment:3 by , 8 years ago
Cc: | added |
---|
comment:4 by , 8 years ago
Уточняю comment:1 "Поток команд"
пункт 2) Что произойдёт если ответ от сервера не придёт ?
follow-up: 9 comment:6 by , 8 years ago
1 раз в 50 секунд.
- или 50раз в секунду?
ждать ответа 200 мс, если нет, то считать команду не выполненной.
топравляем повторный запрос но со временем ожидания 600 мс,
- Запросы, я так понимаю, идут друг за другом в рамках одного соединения?
Какой смысл в различных тамаутах?
- Есть ли вообще смысл в двух запросах "проверки соединения" подряд в рамках одного TCP-соединения? не тоже-самое будет, если я отправлю один запрос с длинным таймаутом?
если и этот запрос неудачен, то считаем что соединения нет, выводим соответствующее сообщение, выходим
- Соединение разрываем?
esl_recv_event_timed(handle, 60, 1, NULL)
- Я так понимаю что эта функция просмотрит сначала очередь сообщений, и если там пусто то будет 60мс ждать не придёт ли что от сервера. Так?
Алексей, обращаюсь отдельно к тебе, ответь пожалуста тоже, на мой вопрос №3.
comment:7 by , 8 years ago
Replying to dimag:
Если не будет ответа от сервера, например, связь разорвана, то будет возвращена ошибка.
- Каков таймаут ожидания ответа от сервера в этом случае?
- Если соединение разорвано и вызов функции возвратит ошибку, какова реакция программы на эту ошибку?
follow-up: 11 comment:8 by , 8 years ago
2)Для улучшения реагирования на команды пользователя
4)Пробуем создать новое ESL соединение, после успешного создания ESL соединения старое разрывается, во всех дальнейших запросах используется новое соединение
5)Функция ждёт новых сообщений в течение 60 мс, если сообщение есть, то возвращает его с результатом функции ESL_SCUESS, если нет сообщений и не появилось, то возвращает ESL_FAIL.
6)Интервал 0 - время ожидания определяет ESL
7)Ничего дальше не делаем, выходим из обработчика сообщений в потоке команд.
comment:9 by , 8 years ago
Replying to san:
- Есть ли вообще смысл в двух запросах "проверки соединения" подряд в рамках одного TCP-соединения? не тоже-самое будет, если я отправлю один запрос с длинным таймаутом?
Алексей, обращаюсь отдельно к тебе, ответь пожалуста тоже, на мой вопрос №3.
Я смысла в такой "двойной" проверке не нахожу. Из каких соображений применен такой алгоритм, не могу себе представить.
Более того, когда мы беседовали устно в Зале Совещаний, без ответа остался вопрос о том, допускает ли вообще протокол event-socket'а передавать новый запрос до того как получен ответ на предыдущий. Если протокол этого не допускает (как, например, протокол HTTP), то описанный алгоритм просто не соответствует спецификации протокола и, следовательно, не будет работать.
comment:10 by , 8 years ago
В любой данный момент времени проводиться только один запрос к ESL. Вызов второго запроса блокируется с помощью мутексов. Все обращение к ESL у меня проводятся внутри мутекса.
comment:11 by , 8 years ago
До мьютексов мы ещё не добрались
Цитирую тебя
Отправляем первый запрос ... ждать ответа 200 мс, если нет, то считать команду не выполненной... то топравляем повторный запрос
- Правильно я понимаю суть:
- Посылаем запрос, если ответа нет более 200мс, то посылаем новый запрос.
????
Если да, то:
2)Для улучшения реагирования на команды пользователя
- Каким образом реагировани на команды пользователя улучшится, благодаря отправке двух сообщений подряд?
- Повторю вопрос Алексея: Допускает ли вообще протокол event-socket'а передавать новый запрос до того как получен ответ на предыдущий?
follow-up: 13 comment:12 by , 8 years ago
9)Есть и вторая причина о которой я уже говорил, Wi-Fi - иногда бывают разрывы связи, которые длятся несколько сотен миллисекунд. Если такой разрыв придётся на момент вызова, то будет ложная индикация разрыва связи, для этого предусмотрен второй длинной по времени ожидания запрос.
10)В документации которая попалась мне об этом не было написанно, но в примерах запрос выполнялся вместе с мутексами, то есть, скорее всего, в данный текущий момент времени можно посылать только 1 запрос.
comment:13 by , 8 years ago
Replying to dimag:
9)Есть и вторая причина о которой я уже говорил, Wi-Fi - иногда бывают разрывы связи, которые длятся несколько сотен миллисекунд. Если такой разрыв придётся на момент вызова, то будет ложная индикация разрыва связи, для этого предусмотрен второй длинной по времени ожидания запрос.
- Чем в этом случае два идущих подряд запроса 200+600мс будут лучше чем один запрос с таймаутом 800мс ?
follow-up: 18 comment:14 by , 8 years ago
- Уточни, как всё-таки ты считаешь, можно передавать новый запрос до того как получен ответ на предыдущий или нет?
comment:15 by , 8 years ago
Если в этот момент пользователь отправит какую-то команду, то могут быть задержки.
follow-up: 19 comment:16 by , 8 years ago
9)Быстрее будет обрабатываться команды, так как если делать запрос с ожиданием 800 мс, то это может затормозить выполнение команды которую послал пользователь и получение сообщений, то есть получение сообщение может задержаться и программа станет обновлять состояние пользователей с заметной задержкой.
comment:17 by , 8 years ago
- Если ответ на команду не приходит 800мс, следовательно и другие ивенты не придут,
так? тогда о каком обновлении статусов речь?
follow-up: 21 comment:18 by , 8 years ago
Replying to san:
- Уточни, как всё-таки ты считаешь, можно передавать новый запрос до того как получен ответ на предыдущий или нет?
Я попробую ответить исходя из логики. Ответ сервера на команду имеет такой формат:
Content-Type: api/response Content-Length: xxx bla-bla-bla +OK
Таким образом, в ответе сервера на запрос клиента не содержится информации, которая позволила бы однозначно соотнести полученный ответ с отправлявшимся ранее запросом. Следовательно, применять описанный Димой алгоритм нельзя, так как программа не имеет возможности определить, на какой из запросов получен ответ. Возможен такой сценарий:
- Клиент отправляет серверу запрос 1.
- Сервер получает из сокета запрос 1 и приступает к его обработке.
- Возникает таймаут, esl_send_recv_timed() завершается с ошибкой.
- Клиент отправляет серверу запрос 2.
- Сервер закончил обработку запроса 1 и передал клиенту ответ на запрос 1.
- Сервер читает из сокета запрос 2 и приступает к его обработке.
- Клиент получил ответ на запрос 1, и трактует его как ответ на запрос 2. esl_send_recv_timed() завершается успешно.
- Клиент отправляет серверу следующий запрос 3.
- Сервер закончил выполнение запроса 2 и передал клиенту ответ на запрос 2.
- Клиент получает ответ на запрос 2 и трактует его как ответ на запрос 3.
... и так далее...
comment:19 by , 8 years ago
Replying to dimag:
если делать запрос с ожиданием 800 мс, то это может затормозить выполнение команды
Команда выполняется сервером. Это другой компьютер! Каким образом ваша программа может повлиять на время выполнения сервером команды???
follow-up: 22 comment:20 by , 8 years ago
Если ответ не придёт в течение 800 мс, то можно считать что соединение отсутствует, но зачем такая большая задержка, лучше быстро проверить наличие соединения за 200 мс, потом, при отсутствие, которое может быть вызвано проблемами Wi-Fi или неполадками на проводной линии, ждать ответа в течение 600 мс.
follow-up: 23 comment:21 by , 8 years ago
Replying to alx:
Replying to san:
- Уточни, как всё-таки ты считаешь, можно передавать новый запрос до того как получен ответ на предыдущий или нет?
Я попробую ответить исходя из логики. Ответ сервера на команду имеет такой формат:
Content-Type: api/response Content-Length: xxx bla-bla-bla +OKТаким образом, в ответе сервера на запрос клиента не содержится информации, которая позволила бы однозначно соотнести полученный ответ с отправлявшимся ранее запросом. Следовательно, применять описанный Димой алгоритм нельзя, так как программа не имеет возможности определить, на какой из запросов получен ответ. Возможен такой сценарий:
- Клиент отправляет серверу запрос 1.
- Сервер получает из сокета запрос 1 и приступает к его обработке.
- Возникает таймаут, esl_send_recv_timed() завершается с ошибкой.
- Клиент отправляет серверу запрос 2.
- Сервер закончил обработку запроса 1 и передал клиенту ответ на запрос 1.
- Сервер читает из сокета запрос 2 и приступает к его обработке.
- Клиент получил ответ на запрос 1, и трактует его как ответ на запрос 2. esl_send_recv_timed() завершается успешно.
- Клиент отправляет серверу следующий запрос 3.
- Сервер закончил выполнение запроса 2 и передал клиенту ответ на запрос 2.
- Клиент получает ответ на запрос 2 и трактует его как ответ на запрос 3.
... и так далее...
Ответы на запросы, приходящие в виде сообщений, распознать можно. Ответ сервера на команду - это сообщение.
comment:22 by , 8 years ago
Replying to dimag:
Если ответ не придёт в течение 800 мс, то можно считать что соединение отсутствует, но зачем такая большая задержка,
Для исключения ложного срабатывания.
Насколько я помню, заказчик требует определять отсутствие связи с сервером за время, не превышающее 10 секунд. Следовательно, если Вы сделаете таймаут 9 секунд (9000 миллисекунд), требование заказчика будет выполнено. Я не вижу причины делать таймаут меньше 9 секунд.
лучше быстро проверить наличие соединения за 200 мс,
Такой короткий таймаут будет давать много ложных срабатываний.
потом, при отсутствие, которое может быть вызвано проблемами Wi-Fi или неполадками на проводной линии, ждать ответа в течение 600 мс.
То есть Вы признаете тот факт, что таймаут 200 мс слишком короткий. В таком случае непонятно, нафига он используется.
comment:23 by , 8 years ago
Replying to dimag:
Ответы на запросы, приходящие в виде сообщений, распознать можно. Ответ сервера на команду - это сообщение.
Объясните, пожалуйста, как в приведенном ниже сценарии клиент может определить, на какой именно запрос получен ответ. Сценарий такой:
- Клиент отправляет запрос 1:
api command1
- Клиент отправляет запрос 2:
api command2
- Клиент получает ответ:
Content-Type: api/response Content-Length: xxx ...какие-то-данные...
comment:24 by , 8 years ago
То что получает клиент в пункте 3, это сообщение, сообщения и команды разные вещи. Одна команда может сгенерировать много сообщений.
Посылка каждой команды всегда возвращает ответ в виде возвращаемого значения функции, показывающего, успешно доставлена ли команда и если успешно, то ответ сервера начинающегося с OK, если команда успешно принята сервером или ERR, если команда ошибочна в поле handle->last_sr_event->body.
На некоторые команды я жду ответа, например api list_users, api create_uuid, результат возвращается в том же поле.
comment:25 by , 8 years ago
Я сделаю дальнейшее уточнение. В процессе работы я отсылаю на сервер команды, запросы и получаю сообщения.
Команда - то что я посылаю на сервер и жду на неё ответ в виде определённого сообщения, сообщений, например bgapi originate. Я посылаю эту команду и жду на неё ответа, по результатам ответа я обновляю структуры данных и перерисовываю картинку с состоянием конференций
Запрос - команда, результат которой возвращается и ответ на которую я жду в теле идентификатора соединения, это ответ обрабатывается и используется в дальнейшем, никакие сообщения на запрос не ожидаются, пример запроса - например api create_uuid. Жду пока появиться ответ, беру ответ в handle->last_sr_event->body и использую.
Сообщения - результат выполнения моих команд и команд других абонентов или операторов, по результатам сообщений я обновляю структуры данных и перерисовываю картинку с состоянием конференций.
comment:28 by , 8 years ago
Replying to dimag:
2)формируется команда, которая отправляется через ESL на FreeSwitch сервер с помощью функции esl_send_recv, определённую из функции esl_send_recv_timed:
#define esl_send_recv(_handle, _cmd) esl_send_recv_timed(_handle, _cmd, 0)
где handle - идентификатор соединения, _cmd-команда, 0-время ожидая, означающее что ждать пока от FreeSwtich не поступит ответ о поставке переданной команды на обработку. Если не будет ответа от сервера, например, связь разорвана, то будет возвращена ошибка.
- Сколько будет длится ожидание ответа на команду? бесконечно?
- Что будет если в момент пока ответ ещё ожидается тикнет таймер "проверки связи"?
comment:29 by , 8 years ago
Description: | modified (diff) |
---|
Добавил в тело тикета описание выявленных недостатков алгоритма.
Алексей, может быть у тебя есть что добавить?
Прежде всего есть 2 потока: поток комманд и поток сообщений.
Работа потока команд организованна следующим образом:
1)Приходит сообщение по которому генерируется команда для FreeSwitch сервера.
2)формируется команда, которая отправляется через ESL на FreeSwitch сервер с помощью функции esl_send_recv, определённую из функции esl_send_recv_timed:
#define esl_send_recv(_handle, _cmd) esl_send_recv_timed(_handle, _cmd, 0)
где handle - идентификатор соединения, _cmd-команда, 0-время ожидая, означающее что ждать пока от FreeSwtich не поступит ответ о поставке переданной команды на обработку. Если не будет ответа от сервера, например, связь разорвана, то будет возвращена ошибка.
3)Если в сообщение требует сделать 2 и более команды, то снова формируется команда как пункте 2, пока все команды не будут отосланы.
4)Сообщение обработано, поток команд переходит в ожидание нового сообщения