2017-02-08 3 views
0

이동 ENV 쓰기 :golang 요청 헤더 동시 읽기 및

GOARCH = "AMD64"

GOBIN = "/ 루트 /"

GOEXE = ""

GOHOSTARCH을 = "amd64"

GOHOSTOS = "linux"

(210)

GOOS = "리눅스"

GOPATH = "/ 데이터/작업 공간 /는 Kubernetes"

GORACE = ""

GOROOT = "/ usr/지방 /"이동

GOTOOLDIR = "/ usr/지방// 패키지/도구/linux_amd64 이동"

GO15VENDOREXPERIMENT = "1"

CC = "gcc가"

을 17,451,515,

GOGCCFLAGS = "- FPIC -m64 -pthread -fmessage 길이 = 0"

CXX = "g ++"

CGO_ENABLED = "1"

이동 버전 :

이동 버전 go1.6.3 리눅스/AMD64

이 문제는 부하가 높은 "성능 테스트 env"kube-apiserver에서 발생합니다. KUBE-apiserver 패닉 종료 :

fatal error: concurrent map read and map write 

goroutine 77930636 [running]: 
runtime.throw(0x2f4c4c0, 0x21) 
    /root/.gvm/gos/go1.6.3/src/runtime/panic.go:547 +0x90 fp=0xca67b477f0  sp=0xca67b477d8 
runtime.mapaccess1_faststr(0x2a8e520, 0xc9e29000f0, 0x2c11220, 0xa, 0x433e360) 
    /root/.gvm/gos/go1.6.3/src/runtime/hashmap_fast.go:202 +0x5b fp=0xca67b47850 sp=0xca67b477f0 
k8s.io/kubernetes/pkg/httplog.(*respLogger).Log(0xcbddf2ae70) 
     /data/gerrit/src/k8s.io/kubernetes/_output/local/go/src/k8s.io/kubernetes/pkg/httplog/log.go:180 +0x43d fp=0xca67b47af8 sp=0xca67b47850 
k8s.io/kubernetes/pkg/apiserver.RecoverPanics.func1(0x7f099f157090, 0xcbddf2ae70, 0xcd7569e380) 
    /data/gerrit/src/k8s.io/kubernetes/_output/local/go/src/k8s.io/kubernetes/pkg/apiserver/handlers.go:174 +0x15d fp=0xca67b47b50 sp=0xca67b47af8 
    net/http.HandlerFunc.ServeHTTP(0xc821a4eac0, 0x7f099f157058, 0xca0f4eb450, 0xcd7569e380) 
    /root/.gvm/gos/go1.6.3/src/net/http/server.go:1618 +0x3a fp=0xca67b47b70 sp=0xca67b47b50 
net/http.serverHandler.ServeHTTP(0xc8215a7b80, 0x7f099f157058, 0xca0f4eb450, 0xcd7569e380) 
    /root/.gvm/gos/go1.6.3/src/net/http/server.go:2081 +0x19e fp=0xca67b47bd0 sp=0xca67b47b70 
net/http.(*conn).serve(0xc8b5d6b980) 
    /root/.gvm/gos/go1.6.3/src/net/http/server.go:1472 +0xf2e fp=0xca67b47f98 sp=0xca67b47bd0 
runtime.goexit() 
    /root/.gvm/gos/go1.6.3/src/runtime/asm_amd64.s:1998 +0x1 fp=0xca67b47fa0 sp=0xca67b47f98 
created by net/http.(*Server).Serve 
    /root/.gvm/gos/go1.6.3/src/net/http/server.go:2137 +0x44e 

해당하는 소스 코드 :

패키지/apiserver/handlers.go

145 func RecoverPanics(handler http.Handler) http.Handler { 
146 return http.HandlerFunc(func(w http.ResponseWriter, req *http.Request) { 
147 defer func() { 
148  if x := recover(); x != nil { 
149   http.Error(w, "apis panic. Look in log for details.", http.StatusInternalServerError) 
150   glog.Errorf("APIServer panic'd on %v %v: %v\n%s\n", req.Method, req.RequestURI, x, debug.Stack()) 
151  } 
152 }() 
153 defer httplog.NewLogged(req, &w).StacktraceWhen(
     httplog.StatusIsNot(
      http.StatusOK, 
      http.StatusCreated, 
      http.StatusAccepted, 
      http.StatusBadRequest, 
      http.StatusMovedPermanently, 
      http.StatusTemporaryRedirect, 
      http.StatusConflict, 
      http.StatusNotFound, 
      http.StatusUnauthorized, 
      http.StatusForbidden, 
      errors.StatusUnprocessableEntity, 
      http.StatusSwitchingProtocols, 
      http.StatusRequestTimeout, 
      errors.StatusTooManyRequests, 
     ), 
170).Log() 

    // Dispatch to the internal handler 
    handler.ServeHTTP(w, req) 
174 }) 
} 

패키지/httplog/log.go :

159 func (rl *respLogger) Log() { 
160 latency := time.Since(rl.startTime) 
161 if glog.V(2) { 
162  extraInfo := "" 
163  if latency >= time.Millisecond*200 && latency < time.Second { 
     extraInfo = fmt.Sprintf("%d00.Millisecond", latency/(time.Millisecond*100)) 
    } else if latency >= time.Second && latency < time.Minute { // Warning 
     extraInfo = fmt.Sprintf("%d.Second", latency/(time.Second)) 
    } else if latency >= time.Minute { // nce will timeout 
     extraInfo = fmt.Sprintf("%d.Minutes", latency/(time.Minute)) 
    } 
    method := rl.req.Method 
    if len(rl.req.Header["Detailed-Method"]) > 0 { 
     method = rl.req.Header["Detailed-Method"][0] 
    } 

    remoteIP := rl.getXForwardIPAdress(rl.req) 


    if !rl.hijacked { 
     //glog.InfoDepth(1, fmt.Sprintf("%s %s: (%v) %v%v%v [%s %s]", rl.req.Method, rl.req.RequestURI, latency, rl.status, rl.statusStack, rl.addedInfo, rl.req.Header["User-Agent"], rl.req.RemoteAddr)) 
180   glog.InfoDepth(1, fmt.Sprintf("%v %s %s: (%sms) %v%v [%s %s]-%s %v", rl.req.Header["X-Requestid"], method, rl.req.RequestURI, GetMilliLatency(latency), rl.status, rl.addedInfo, rl.req.Header["User-Agent"], remoteIP, extraInfo, rl.statusStack)) 
    } else { 
     //glog.InfoDepth(1, fmt.Sprintf("%s %s: (%v) hijacked [%s %s]", rl.req.Method, rl.req.RequestURI, latency, rl.req.Header["User-Agent"], rl.req.RemoteAddr)) 
     glog.InfoDepth(1, fmt.Sprintf("%v %s %s: (%sms) hijacked [%s %s]-%s", rl.req.Header["X-Requestid"], method, rl.req.RequestURI, GetMilliLatency(latency), rl.req.Header["User-Agent"], remoteIP, extraInfo)) 
    } 
} 

}

handler.ServeHTTP에서 request.Header가 변경되었습니다. 이유를 찾을 수 없습니다.

(1) "serveHTTP"및 "지연 로그"는 연속 방식으로 작동합니다. 존재하지 않는 "동시 읽기 및 쓰기"

(2) "동시 읽기 및 쓰기"가 있더라도 패닉을 처리하기위한 "복구"func이 있으므로 kube-apiserver가 종료되지 않습니다.

질문에 많은 시간을 들였는데 누가 나를 도울 수 있습니까?고마워요

+0

경주 감지기 도움이 될 것입니까? https://blog.golang.org/race-detector –

+0

도움 주셔서 감사합니다. 나는 -race 옵션을 사용하여 kube-apiserver를 만든 다음 아무 것도 실행하지 않고 "DATA RACE"bla bla를 실행하지 않습니다. – Bling

+0

"디자인 덕분에 경주 감지기는 실제 코드로 실행될 때만 경주 상태를 감지 할 수 있습니다. 즉, 현실적인 작업 부하에서 경주 가능 바이너리를 실행하는 것이 중요합니다."지금 당장 유감입니다. 그 공포는 방금 우리의 "성능 테스트 env"에서 한 번 발생했으며 쉽게 재현 할 수 없습니다. – Bling

답변

0

제 생각 엔 글로벌 타임 아웃 핸들러가 요청에서 작동하는 goroutines을 실제로 멈출 수 없다는 것입니다. 따라서 연결 시간이 초과되면 (변경하지 않은 경우 1m) 오류가 반환됩니다. goroutine이 실제로 동시에 완료되면 연결에 쓸 수도 있습니다. 우리가 문제를 일으키는 것을 막기 위해 자물쇠를 추가했다고 생각했지만 아마 머리글이 항상 자물쇠에 의해 보호되는 것은 아닙니다. 깨끗한 소스 트리에서 이것을 재현 할 수 있다면 Kubernetes github repo에서 문제를 제기하십시오.

+0

도움 주셔서 감사합니다. 그러나 글로벌 타임 아웃 핸들러에서 타임 아웃이 발생하면 요청 헤더를 읽거나 변경하지 않습니다. 그리고 "동시지도 읽기 쓰기"가 발생한 장소는 "읽기"작업을 수행합니다. – Bling

+0

resthandler.go에서 작업 할 때 요청할 자체 정의 헤더를 추가합니다. req.Request.Header [ "Detailed-Method"] = [] 문자열 { "GET-"+ scope.Resource.Resource} 로그에 인쇄 된 방법이 k8s가 아닌 http 표준 이므로 조작상의 편의를위한 것입니다. 표준. 그러나 나는 이것이 그 이유라고 생각하지 않는다 (그들은 연속적이다). 그리고 아마도 핸들러에서 요청을 변경해서는 안되며 사소한 문제가 발생할 수 있습니다. – Bling