предисловие
Всем знакомо слово «сердцебиение», оно, конечно, не относится к биению сердца между мужчиной и женщиной, а связано с длительной связью.
Как следует из названия, это основа для доказательства того, жив ли он еще.
В каких случаях вам нужно сердцебиение?
В настоящее время большинство приложений, с которыми мы столкнулись, представляют собой приложения, основанные на длинных соединениях, которым для «поддержания активности» требуются тактовые импульсы.
В случае длительного соединения клиент и сервер не всегда находятся в состоянии связи.Если две стороны не общались в течение длительного времени, обе стороны не будут знать текущее состояние другой стороны, поэтому необходимо отправить небольшое сообщение, чтобы сообщить другой стороне.“我还活着”
.
Есть еще несколько целей:
- Когда сервер обнаруживает, что у клиента нет пульса, он может активно закрыть канал и перевести его в автономный режим.
- Клиент обнаруживает, что сервер долгое время не отвечал на пульс, и может повторно подключиться для получения нового соединения.
только чтоcimЕсть две потребности говорить о.
Реализация сердцебиения
На самом деле есть два способа реализовать сердцебиение:
-
TCP
Реализация протокола (keepalive
механизм). - Прикладной уровень реализует его сам.
так какTCP
Протокол слишком низкоуровневый, и для разработчиков ремонтопригодность и гибкость относительно плохие, а еще это зависит от операционной системы.
Итак, мы обсуждаем здесь реализацию прикладного уровня.
Как показано на рисунке выше, на прикладном уровне пакет пульса обычно отправляется клиентом.ping
Серверу сервер отвечает ответом после его полученияpong
Это показывает, что обе стороны живы и здоровы.
Как только один из концов задерживает N временных окон и не получает сообщение, выполняется другая обработка.
Автоматическая переподключение клиента
Давайте сначала возьмем клиента.Время от времени клиент отправляет пакет сердцебиения на сервер и получает ответ от сервера.
Нормальная реализация должна быть:
- Запустите задачу на время и регулярно отправляйте пакеты пульса.
- Обновите местное время после получения ответа от сервера.
- Есть еще одно задание на время, чтобы регулярно проверять это
“本地时间”
превышен ли порог. - После этого считается, что сервер неисправен и должен быть восстановлен.
Это также вызывает сердцебиение, но это не дружелюбно.
В случае обычной связи между клиентом и сервером запланированная задача по-прежнему будет отправлять контрольные пакеты; это бессмысленно и несколько избыточно.
Таким образом, в идеале клиент должен отправить этот пакет пульса, чтобы подтвердить, работает ли сервер, когда сообщение записи, полученное клиентом, находится в режиме ожидания.
Хорошая новостьNetty
Это было учтено для нас, поставляется с готовымIdleStateHandler
Предназначен для обработки сердцебиения.
приди и посмотриcim
Реализация в:
существуетpipeline
Добавлено 10 секунд, чтобы не получать сообщение о записиIdleStateHandler
, то он перезвонитChannelInboundHandler
серединаuserEventTriggered
метод.
Поэтому, как только время записи истекло, немедленно отправьте пульс на сервер (чтобы быть более совершенным, должно быть определенное количество повторных попыток после того, как пульс не будет отправлен);
Таким образом, пакет пульса будет отправлен только тогда, когда он бездействует.
Но куда писать логику переподключения, если ответа от сервера нет долгое время?
Сначала рассмотрим этот пример:
При получении сообщения pong, на которое отвечает сервер, время записывается в текущем канале, то есть разница между этим временем и текущим временем может быть устранена в запланированной задаче, чтобы определить, превышено ли пороговое значение.
Повторно подключите, если он превышает.
В то же время каждый пульс привязан к текущему времени и предыдущему ответу сервера.Channel
Вычтите указанное выше время, чтобы определить, требуется ли повторное подключение.
этоheartBeatHandler.process(ctx);
логика исполнения.
Псевдокод выглядит следующим образом:
@Override
public void process(ChannelHandlerContext ctx) throws Exception {
long heartBeatTime = appConfiguration.getHeartBeatTime() * 1000;
Long lastReadTime = NettyAttrUtil.getReaderTime(ctx.channel());
long now = System.currentTimeMillis();
if (lastReadTime != null && now - lastReadTime > heartBeatTime){
reconnect();
}
}
Мифы об IdleStateHandler
Все выглядит нормально, но логика переподключения на самом деле не реализована таким образом.
Главный вопрос в том, чтоIdleStateHandler
Неправильно понятый.
Мы предполагаем следующий сценарий:
- Клиент подключается к серверу путем входа в систему и поддерживает длительное соединение.Когда все нормально, обе стороны отправляют пакеты сердцебиения для поддержания соединения.
- В это время происходит взлом сервера и происходит даун машины, поэтому в идеале клиент не должен получать ответ от сервера в течение длительного времени.
userEventTriggered
Выполнение задач на время. - судить
当前时间 - UpdateWriteTime > 阈值
при повторном подключении.
Но это имело неприятные последствия и не выполняло шаги 2 и 3.
Потому что как только серверdown
машине или отключен от сети клиента, клиент будет перезваниватьсяchannelInactive
событие.
IdleStateHandler
какChannelInbound
также переписанchannelInactive()
метод.
здесьdestroy()
Метод отменит ранее открытые задачи синхронизации.
Следовательно, больше не будет запланированных задач для выполнения, и не будет возможности выполнить эту услугу переподключения..
Надежная реализация
Таким образом, у нас должен быть отдельный поток, чтобы определить, нужно ли нам повторно подключаться, независимо отIdleStateHandler
.
тогдаcim
Когда клиент обнаруживает, что сеть отключена, запускается запланированная задача:
Причина, по которой он не включается при запуске клиента, состоит в том, чтобы немного сэкономить потребление потока. Хотя проблемы с сетью неизбежны, ее включение при необходимости может сэкономить ресурсы.
В данном задании фактически выполняется переподключение, а конкретный код не будет выкладываться из-за нехватки места, кому интересно, можно обратиться к нему самостоятельно.
Заодно проверить эффект.
Запустите два сервера, а затем запустите клиент, чтобы подключиться к предыдущему и поддерживать длительное соединение. В это время служба внезапно отключается вручную, и клиент может автоматически повторно подключиться к доступному узлу службы.
После запуска клиента сервер также может получать обычныеping
Информация.
использовать:info
Команда для просмотра состояния связи текущего клиента и определения того, что соединение9000
порт.
:info — это новая команда для просмотра некоторой информации о клиенте.
В этот момент я выключил подключенный узел.
kill -9 2142
В это время клиент автоматически переподключится к доступному узлу. Этот узел также получает оперативные журналы и пакеты пульса.
Сервер автоматически удаляет автономных клиентов
Теперь давайте посмотрим на сервер, эффект, которого он хочет добиться, состоит в том, чтобы задержать N секунд без получения клиентского запроса.ping
Пакет считает, что клиент находится в автономном режиме, иcim
По сценарию вам нужно выгнать его и отключить.
ошибка отправки сообщения
Тут еще есть недоразумение, в названииctx.writeAndFlush()
При отправке сообщения получить обратный звонок.
из которыхisSuccess
Его нельзя использовать в качестве критерия успеха или неудачи отправки сообщения.
То есть, даже если клиент напрямую отключен от сети, сервер получит это после отправки сообщения сюда.success
все ещеtrue
.
Это потому, что здесьsuccess
Просто дайте нам знать, что сообщение было написаноTCP
Буфер прошел успешно.
Есть немало людей, у которых такое же непонимание, как у меня раньше, этоNetty
официальный ответ.
Связанныйissue
:
В то же время я хотел бы поблагодарить 95 Lao Xu и The Flash за их расследование.
Таким образом, мы не можем закрыть соединение клиента на основе этого, но судить, как указано вышеChannel
Разница между привязанным временем и текущим временем превышает пороговое значение.
выше этоcim
Реализация сервера, логика соответствует сказанному в начале, а также совпадает сDubbo
Механизм сердцебиения несколько похож.
Итак, давайте проведем эксперимент: клиент и сервер, которые нормально общаются, когда я отключу клиент напрямую от сети, сервер автоматически удалит клиента.
Суммировать
Таким образом, выполняются два требования в начале текста.
- Когда сервер обнаруживает, что у клиента нет пульса, он может активно закрыть канал и перевести его в автономный режим.
- Клиент обнаруживает, что сервер долгое время не отвечал на пульс, и может повторно подключиться для получения нового соединения.
При этом я тоже наступил на два недоразумения.Достаточно в одиночку наступить на яму.Надеюсь, что каждый, кто прочитал эту статью, приобретет что-то, чтобы не наступить на яму.
Все актуальные коды этой статьи находятся здесь, а желающие могут просмотреть ее самостоятельно:
Если эта статья оказалась для вас полезной, пожалуйста, перешлите ее.