Кто-то из группы golang спросил, почему программа необъяснимо зависает, а потом уже не отвечает ни на какие запросы. Одноядерный процессор заполнен.
Эта функция очень похожа на ситуацию, с которой столкнулась некая система нашей компании.После длительного анализа позиционирования и подведения итогов я также по-разному читал код времени выполнения и gc golang и, наконец, нашел тип, который появился в бизнес. Код выглядит следующим образом:
package main
import "time"
func main() {
var ch = make(chan int, 100)
go func() {
for i := 0; i < 100; i++ {
ch <- 1
}
}()
for {
// the wrong part
if len(ch) == 100 {
sum := 0
itemNum := len(ch)
for i := 0; i < itemNum; i++ {
sum += <-ch
}
if sum == itemNum {
return
}
}
}
}
Зациклить основную горутину, чтобы определить, заполнены ли данные в ch, и поместить 100 фрагментов данных в ch другой горутины. Вроде бы программа должна завершиться нормально, да?
Если интересно, можете попробовать сами. Фактически, эта программа застрянет в следующем цикле for в немного более старых версиях golang. Причина?
использовать:
GODEBUG="schedtrace=300,scheddetail=1" ./test1
Вы должны увидеть, что в это время флаг gcwaiting равен 1. Поэтому я сначала подозревал, что это ошибка golang gc. . Но, в конце концов, потребовалось много времени, чтобы выяснить, что это все еще проблема с моим собственным кодом.
Поскольку в цикле for нет вызова функции, компилятор не будет вставлять код планирования, поэтому горутина, выполняющая цикл for, не может быть вызвана, и если gc встретится во время цикла, он застрянет на этапе gcwaiting. , и весь процесс застрянет на стадии gcwaiting Навсегда зависнуть и умереть на этом цикле. и больше не отвечаю.
Конечно, вышеприведенная программа не будет зависать в последних версиях golang 1.8/1.9 (экспериментальные результаты, без подробностей). В примечании к обновлению официальный представитель заявил, что теоретически другие горутины будут иметь возможность планироваться в плотном цикле, поэтому мы решили поверить официальному лицу и попробовать следующую программу:
package main
import (
"fmt"
"io"
"log"
"net/http"
"runtime"
"time"
)
func main() {
runtime.GOMAXPROCS(runtime.NumCPU())
go server()
go printNum()
var i = 1
for {
// will block here, and never go out
i++
}
fmt.Println("for loop end")
time.Sleep(time.Second * 3600)
}
func printNum() {
i := 0
for {
fmt.Println(i)
i++
}
}
func HelloServer(w http.ResponseWriter, req *http.Request) {
io.WriteString(w, "hello, world!\n")
}
func server() {
http.HandleFunc("/", HelloServer)
err := http.ListenAndServe(":12345", nil)
if err != nil {
log.Fatal("ListenAndServe: ", err)
}
}
Через несколько секунд запуска завитка:
curl localhost:12345
Мне все еще не хочется доверять официальному лицу. После исследований и исследований я случайно написал, как найти такую ошибку. Сначала проанализируйте характеристики программы, когда возникает этот тип ошибки:
1. 卡死在 for 循环上
2. gcwaiting=1
3. 没有系统调用
Поскольку системного вызова нет, это не вызвано системным вызовом pot, поэтому мы не можем использовать такие инструменты, как strace, чтобы увидеть, зависает ли программа на системном вызове. И gcwaiting=1 на самом деле не помогает нам определить, в чем проблема.
Затем остальные застревают в цикле for Интенсивные циклы for обычно приводят к заполнению ядра процессора. Если вы раньше занимались системным программированием, вы должны хорошо разбираться в инструменте perf, вы можете использовать:
perf top
Выборка использования ЦП, чтобы мы могли найти программные функции, которые используют верхний ЦП. Фактическиperf top
Результат выполнения также очень интуитивно понятен:
99.52% ffff [.] main.main
0.06% [kernel] [k] __do_softirq
0.05% [kernel] [k] 0x00007fff81843a35
0.03% [kernel] [k] mpt_put_msg_frame
0.03% [kernel] [k] finish_task_switch
0.03% [kernel] [k] tick_nohz_idle_enter
0.02% perf [.] 0x00000000000824d7
0.02% [kernel] [k] e1000_xmit_frame
0.02% [kernel] [k] VbglGRPerform
Видите ли, наша программа фактически застряла на функции main.main. Позиционирование за секунды с помощью одной команды.
Маме больше не нужно беспокоиться о том, что моя программа случайно напишет бесконечный цикл. На самом деле, иногда, почему мой обычный цикл становится бесконечным, не так просто проверить, как в простой демонстрации выше. В настоящее время вы также можете использовать delve, и недавно помог jsoniter найти ошибку, аналогичную приведенной выше:
GitHub.com/gin-a-oh-nickname/a…
Найдите функцию из perf, затем используйте pid для подключения к процессу, найдите горутину, которая выполняет цикл, а затем объедините вывод локальных переменных вплоть до следующего.
Проблема находится над.
Последняя строка мелким шрифтом: Спасибо г-ну Мао за его руководство в группе голанг.
Если вы считаете, что с написанием что-то не так, или вы отдаете дань уважения г-ну Мао, упомянутому в конце статьи, или вас очень интересует jsoniter, библиотека для разбора json, которая вот-вот появится на k8s, вы хотите знать больше об авторе мысленного путешествия jsoniter Даниэля, то у вас есть следующие варианты:
1. Присоединяйтесь к станции B и Мао Цзуну, чтобы изучить продвинутый уровень осанки.
2. Присоединяйтесь к Didi и автору jsoniter Даниэлю @taowen в качестве коллег.
3. Добавьте капли в спрей автора этой статьи
Резюме можно отправлять по адресу:caochunhui@didichuxing.com