Эта перезагрузка действительно не решит проблему: один разMySQLАктивное отключение, в результате чего большое количество сервисовCLOSE_WAITВесь процесс расследования.
Недавно столкнулся с онлайн-сервисомsocketРесурсы постоянно пополняются. Проанализируйте онлайн-проблемы с помощью различных инструментов и найдите код проблемы. Вот обзор и краткое описание процесса обнаружения и устранения неполадок.
Сначала посмотрите на две картинки. Изображение контролируется, когда служба работает нормальноsocketСтатус, другой конечно ненормальный!
Рисунок 1: нормальный мониторинг
Рисунок 2: Мониторинг при отклонении от нормы
Судя по исполнению рисунка,04:00В начале ресурс сокета все возрастал, а после перезапуска в нижней части каждой долины возвращался к нормальному значению, а затем продолжал расти без сброса, и интервал между каждым пиком становился все короче и короче.
После перезагрузки я проверил журнал и не увидел егоpanic, в настоящее время нет никакой дополнительной проверки, я действительно думаю, что перезапуск Дафа – это хорошо.
Информационный бюллетень
Сервис разработан на Golang и нормально работает уже почти год, доступен для вызовов других сервисов, основные базовые ресурсы — DB/Redis/MQ.
Для удобства последующего описания приведена схема архитектуры сервиса.
Рисунок 3: Архитектура службы
Архитектура очень проста.
Проблема приходит утром08:20Примерно в начале пришла тревога, что сервис появляется504, первая реакция на этот раз, что сервис давно не перезапускался (почти два месяца), могут быть какие-то утечки памяти, и я сразу без долгих раздумий перезапустил. То есть в нижней части первой долины на рисунке 2 после перезапуска службы она вернулась к нормальным уровням (перезапуск действительно прост в использовании, доволен).
Около14:00Когда снова появилось предупреждение504, я чувствовал себя немного не так в то время, но поскольку в этот день случилась масштабная акция, я немедленно снова перезапустил сервис. Примерно через час будильник снова запускается.После нескольких последовательных перезапусков обнаруживается, что временной интервал, который необходимо перезапустить, становится все короче и короче. Найти проблему в этот момент далеко не просто.Этот перезапуск действительно не решит проблему., поэтому немедленно подайте заявку на получение прав доступа к компьютеру и приступайте к устранению неполадок. Скриншоты ниже взяты из моего репродукционного демо и не имеют никакого отношения к онлайну.
проблема найдена
После возникновения проблемы сначала проанализируйте и сделайте вывод, затем проверьте и, наконец, найдите и измените. Основываясь на производительности в то время, были сделаны следующие предположения соответственно.
пс: Все последующие скриншоты взяты со скриншотов при локальном воспроизведении
Выведите один
Ресурс сокета постоянно заполняется, и раньше он никогда не появлялся, а сегодня вдруг появился,Сомневаетесь, что объем запросов слишком велик, чтобы перегрузить сервис
После просмотра в реальном времениqpsПосле этого от идеи отказались, сумма хотя и увеличилась, но все еще находилась в допустимых пределах сервера (далеко от эталонного значения стресс-теста).
Вывод два
Два сбоя машины происходят одновременно, перезапустите один, и другой будет облегчен.Это очень странно, так как служба развернута в двух кластерах независимо.
При вышеуказанном основании результатом запуска является то, что базовые ресурсы, от которых зависит служба, должны быть вдобавок к проблеме, иначе невозможно, чтобы службы независимого кластера имели проблемы одновременно.
Поскольку мониторинг показывает, чтоsocketпроблема, поэтомуnetstatКоманда проверяет текущую ссылку tcp (локальный тест, фактическое значение онлайн намного больше)
/go/src/hello # netstat -na | awk '/^tcp/ {++S[$NF]} END {for(a in S) print a, S[a]}'
LISTEN 2
CLOSE_WAIT 23 # 非常异常
TIME_WAIT 1
обнаружил, что подавляющее большинство ссылок находится вCLOSE_WAITгосударство, это очень невероятная ситуация. затем используйтеnetstat -an
команда проверена.
Рисунок 4: Много CLOSE_WAIT
CLOSED указывает, что соединение сокета не используется. ПРОСЛУШИВАНИЕ означает прослушивание входящих соединений. SYN_SENT означает попытку установить соединение. SYN_RECEIVED для начальной синхронизации соединения. ESTABLISHED означает, что соединение установлено. CLOSE_WAIT указывает, что удаленный компьютер закрыл соединение и ожидает закрытия соединения через сокет. FIN_WAIT_1 указывает, что соединение сокета закрыто и соединение закрывается. CLOSING сначала закрывает соединение с локальным сокетом, затем закрывает соединение с удаленным сокетом и, наконец, ожидает подтверждающего сообщения. LAST_ACK После того, как удаленный калькулятор выключится, дождитесь сигнала подтверждения. FIN_WAIT_2 После закрытия соединения сокета дождитесь сигнала закрытия от удаленного компьютера. TIME_WAIT После закрытия соединения дождитесь закрытия удаленного компьютера и повторной отправки.
Затем начните сосредотачиваться на том, почему существует большое количество соединений mysql.CLOSE_WAITШерстяная ткань? Чтобы было понятно, давайте вставим немного информации о четырех взмахах рук TCP.
ПТС махнул четыре раза
ПосмотримTCPКак происходит процесс четырех взмахов рук:
Рис. 5. Четыре волны TCP
Опишите этот процесс на китайском языке:
Client: 服务端大哥,我事情都干完了,准备撤了
, что соответствует отправке клиентомFIN
Сервер:知道了,但是你等等我,我还要收收尾
, здесь соответствует полученному серверуFINответил позжеACK
После двух вышеуказанных шагов сервер будет вCLOSE_WAITгосударство. Через некоторое времяServerэто конец
Сервер:小弟,哥哥我做完了,撤吧
, сервер отправилFIN
Клиент:大哥,再见啊
, вот клиент-серверACK
В этот момент сервер может убежать, а клиент — нет. Зачем? Клиент также должен ждать2MSLКакое-то время, почему клиент не может работать прямо здесь? В основном для предотвращения отправкиACKСервер не получает его, сервер повторно отправляетFINПозволю себе еще раз спросить, если клиент после отправки убежит, то при повторной отправке сервером на него никто не обратит внимания. Длительность ожидания также очень важна.
Maximum Segment LifetimeМаксимальное время жизни пакета, то есть максимальное время существования любого пакета в сети, по истечении которого пакет будет отброшен.
Не фотографируйтесь здеськлиент/серверЗапутался с клиент-серверной частью в проекте, нужно только помнить: та сторона, которая активно закрывает, отправляетFINПакет (Клиент), ответ стороны, которая пассивно отключается (Сервер)ACKпакет, в это время пассивно закрытая сторона входитCLOSE_WAITгосударство. Если все хорошо, то сторона, которая пассивно выключится позже, также выдастFINупаковать, а затем перейти наLAST_ACKгосударство.
Поскольку это так,TCPПри анализе захвата пакетов:
/go # tcpdump -n port 3306
# 发生了 3次握手
11:38:15.679863 IP 172.18.0.5.38822 > 172.18.0.3.3306: Flags [S], seq 4065722321, win 29200, options [mss 1460,sackOK,TS val 2997352 ecr 0,nop,wscale 7], length 0
11:38:15.679923 IP 172.18.0.3.3306 > 172.18.0.5.38822: Flags [S.], seq 780487619, ack 4065722322, win 28960, options [mss 1460,sackOK,TS val 2997352 ecr 2997352,nop,wscale 7], length 0
11:38:15.679936 IP 172.18.0.5.38822 > 172.18.0.3.3306: Flags [.], ack 1, win 229, options [nop,nop,TS val 2997352 ecr 2997352], length 0
# mysql 主动断开链接
11:38:45.693382 IP 172.18.0.3.3306 > 172.18.0.5.38822: Flags [F.], seq 123, ack 144, win 227, options [nop,nop,TS val 3000355 ecr 2997359], length 0 # MySQL负载均衡器发送fin包给我
11:38:45.740958 IP 172.18.0.5.38822 > 172.18.0.3.3306: Flags [.], ack 124, win 229, options [nop,nop,TS val 3000360 ecr 3000355], length 0 # 我回复ack给它
... ... # 本来还需要我发送fin给他,但是我没有发,所以出现了close_wait。那这是什么缘故呢?
src > dst: flags data-seqno ack window urgent options
src > dst указывает от адреса источника к адресу назначения flags — информация о флаге в TCP-пакете, S — флаг SYN, F(FIN), P(PUSH) , R(RST) «.» (без флага) data-seqno — порядковый номер данных в пакете ack — следующий ожидаемый порядковый номер window - размер окна приемного буфера срочный Указывает, есть ли в пакете срочный указатель варианты есть варианты
Объединив вышеизложенную информацию, объясню словами:Балансировщик нагрузки MySQLотправить в мой сервисFINпакет, я ответил, и в этот момент я вошелCLOSE_WAITстатус, но я, как пассивная закрывающая сторона, не отправилFIN, из-за чего мой сервер находится вCLOSE_WAITсостояние, не в состоянии, наконец, войтиCLOSEDгосударство.
Тогда я делаю вывод, что возможные причины этой ситуации следующие:
-
балансировщик нагрузкивышел ненормально,
这基本是不可能的,他出现问题绝对是大面积的服务报警,而不仅仅是我一个服务
-
Балансировщик нагрузки MySQLТайм-аут установлен слишком коротким, так что бизнес-код еще не был обработан.Балансировщик нагрузки MySQLзакрыть TCP-соединение
这也不太可能,因为这个服务并没有什么耗时操作,当然还是去检查了负载均衡器的配置,设置的是60s。
-
проблема с кодом,MySQLСоединение не может быть разорвано
目前看起来应该是代码质量问题,加之本次数据有异常,触发到了以前某个没有测试到的点,目前看起来很有可能是这个原因
Найдите причину ошибки
Поскольку бизнес-логика кода написана не мной, я беспокоюсь, что проблема не будет замечена какое-то время, поэтому я использую ее напрямую.perf
Используйте диаграмму пламени, чтобы нарисовать все отношения вызова. Поскольку выше мы делаем вывод, что соединение mysql не освобождается в коде. не более, чем:
- действительно близко не называется
- Есть трудоемкие операции (график пламени виден очень четко), в результате чего происходит тайм-аут
- Транзакция mysql обрабатывается неправильно, например: откат или фиксация
Так как флейм-граф содержит слишком много контента, я сложил ненужную информацию, чтобы всем было понятно.
Рисунок 6: Рассматриваемый график пламени
На флеймграфе четко видно, что транзакция открыта, а в остальном разделе не видитCommitилиRollbackработать. Это определенно будет эксплуатационной проблемой. Тогда также ясно, что проблема в следующем:
MainController.updateВнутри метода особо нечего сказать, перейдите непосредственно к методу обновления для проверки. Был найден следующий код:
func (c *MainController) update() (flag bool) {
o := orm.NewOrm()
o.Using("default")
o.Begin()
nilMap := getMapNil()
if nilMap == nil {// 这里只检查了是否为nil,并没有进行rollback或者commit
return false
}
nilMap[10] = 1
nilMap[20] = 2
if nilMap == nil && len(nilMap) == 0 {
o.Rollback()
return false
}
sql := "update tb_user set name=%s where id=%d"
res, err := o.Raw(sql, "Bug", 2).Exec()
if err == nil {
num, _ := res.RowsAffected()
fmt.Println("mysql row affected nums: ", num)
o.Commit()
return true
}
o.Rollback()
return false
}
На этом все анализы заканчиваются. После просмотраgetMapNilnil возвращается, но следующие условия оценки не откатываются.
if nilMap == nil {
o.Rollback()// 这里进行回滚
return false
}
Суммировать
Весь процесс анализа по-прежнему занимает много времени. Самое главное, что субъективное сознание слишком сильное, почему после года пробега вдруг оказывается, что проблемы нет? Итак, в начале была проблема с SRE, DBA, различными инфраструктурами (люди всегда сначала сомневаются в других). На это ушло много времени.
Рассмотрим правильную идею анализа:
- После возникновения проблемы вы должны немедленно проверить журнал, и действительно, в журнале не обнаружено никаких проблем;
- Мониторинг четко показывает, что сокет постоянно растет, и понятно, что его нужно использовать немедленно
netstat
Проверьте ситуацию, чтобы увидеть, какой процесс является банком; - в соответствии с
netstat
проверить, используяtcpdump
Захватите пакет и проанализируйте, почему соединениеПассивное отключение(знание TCP очень важно); - Если вы знакомы с кодом, вы должны проверить непосредственно бизнес-код, если вы не знакомы с ним, вы можете использовать его
perf
Распечатайте вызывающую ссылку кода; - Независимо от того, анализируете ли вы код или график пламени, вы должны быть в состоянии быстро найти проблему на этом этапе.
Так почему же оно появилось на этот раз?CLOSE_WAITШерстяная ткань? Большинству студентов уже должно быть понятно, я кратко объясню здесь:
Поскольку эта строка кода не откатила транзакцию, сервер не инициировал активное закрытие. следовательноБалансировщик нагрузки MySQLПри достижении 60 с активно запускалась операция закрытия, но через захват пакетов tcp было обнаружено, что сервер не отвечает, это связано с тем, что транзакция в коде не была обработана, что привело к большому количеству портов и ресурсов соединения. заняты. Опубликовать данные захвата пакета при размахивании:
# mysql 主动断开链接
11:38:45.693382 IP 172.18.0.3.3306 > 172.18.0.5.38822: Flags [F.], seq 123, ack 144, win 227, options [nop,nop,TS val 3000355 ecr 2997359], length 0 # MySQL负载均衡器发送fin包给我
11:38:45.740958 IP 172.18.0.5.38822 > 172.18.0.3.3306: Flags [.], ack 124, win 229, options [nop,nop,TS val 3000360 ecr 3000355], length 0 # 我回复ack给它
Я надеюсь, что эта статья поможет вам устранить неполадки в Интернете. Чтобы помочь вам понять, график пламени в правильном случае и график пламени в неправильном случае прилагаются ниже. Вы можете сравнить сами.
В статье, на которую я ссылался, были поставлены два вопроса для размышления об этой ситуации. Я думаю, что это очень важно. Давайте подумаем об этом сами:
- Почему сотни машинCLOSE_WAITНе приведет ли это к дальнейшему доступу? Мы не часто говорим, что машина65535Доступны ли файловые дескрипторы?
- Почему у меня балансировка нагрузки, а две машины, на которых развернут сервис, выключаются практически одновременноCLOSE_WAIT ?
Справочная статья:
Личный общедоступный номер:dayuTalk
Контактный адрес электронной почты: dayugog@gmail.com
Гитхаб:github.com/helei112g
Я пишу свое, ты вознаграждаешь свое