2016-03-03 24 views
0

我有以下代码的http处理程序,它在随后的请求中从Amazon S3下载原始图像并将其转换为所需长宽比并将其保存回s3。这段代码泄漏内存,并在某段时间后崩溃。我已经处理了我的一切,也做了代码分析。但是,仍然无法弄清楚这个问题。如果有人能在这里找到答案,将不胜感激仅供参考,我正在使用go version go1.5.3 linux/amd64版本。从分析go lang内存泄漏http Handler

输出:

3259.27kB of 3302.42kB total (98.69%) 
Dropped 258 nodes (cum <= 16.51kB) 
Showing top 30 nodes out of 91 (cum >= 27.76kB) 
     flat flat% sum%  cum cum% 
1552.59kB 47.01% 47.01% 1552.59kB 47.01% bytes.makeSlice 
    584kB 17.68% 64.70%  584kB 17.68% imagick._Cfunc_GoBytes 
    257.38kB 7.79% 72.49% 257.38kB 7.79% encoding/pem.Decode 
    168.11kB 5.09% 77.58% 168.11kB 5.09% crypto/tls.(*block).reserve 
    165.09kB 5.00% 82.58% 389.49kB 11.79% crypto/x509.parseCertificate 
    105.32kB 3.19% 85.77% 105.32kB 3.19% reflect.unsafe_NewArray 
    83.64kB 2.53% 88.30% 83.64kB 2.53% math/big.nat.make 
    75.55kB 2.29% 90.59% 75.55kB 2.29% net/http.(*Transport).dialConn 
    64.02kB 1.94% 92.53% 64.02kB 1.94% regexp.(*bitState).reset 
    43.77kB 1.33% 93.85% 43.77kB 1.33% crypto/x509.(*CertPool).AddCert 
    40.44kB 1.22% 95.08% 40.44kB 1.22% crypto/x509/pkix.(*Name).FillFromRDNSequence 
    40.16kB 1.22% 96.29% 40.16kB 1.22% encoding/asn1.parsePrintableString 
    24.07kB 0.73% 97.02% 24.07kB 0.73% net/http.newBufioWriterSize 
    18.98kB 0.57% 97.60% 18.98kB 0.57% net/http.newBufioReader 
    16.14kB 0.49% 98.09% 64.77kB 1.96% crypto/tls.(*Conn).readHandshake 
    12.01kB 0.36% 98.45% 237.09kB 7.18% encoding/asn1.parseField 
    8.01kB 0.24% 98.69% 91.65kB 2.78% crypto/x509.parsePublicKey 
     0  0% 98.69% 112.33kB 3.40% bufio.(*Reader).Read 
     0  0% 98.69% 80.32kB 2.43% bufio.(*Reader).fill 
     0  0% 98.69% 27.76kB 0.84% bufio.(*Writer).ReadFrom 
     0  0% 98.69% 27.76kB 0.84% bufio.(*Writer).flush 
     0  0% 98.69% 1648.33kB 49.91% bytes.(*Buffer).ReadFrom 
     0  0% 98.69% 16.59kB 0.5% bytes.(*Buffer).Write 
     0  0% 98.69% 16.59kB 0.5% bytes.(*Buffer).grow 
     0  0% 98.69% 843.06kB 25.53% crypto/tls.(*Conn).Handshake 
     0  0% 98.69% 112.33kB 3.40% crypto/tls.(*Conn).Read 
     0  0% 98.69% 27.76kB 0.84% crypto/tls.(*Conn).Write 
     0  0% 98.69% 843.06kB 25.53% crypto/tls.(*Conn).clientHandshake 
     0  0% 98.69% 160.96kB 4.87% crypto/tls.(*Conn).readRecord 
     0  0% 98.69% 27.76kB 0.84% crypto/tls.(*Conn).writeRecord 

代码:

func main() {  

    imagick.Initialize() 
    defer imagick.Terminate() 

     myMux := http.NewServeMux() 
     myMux.HandleFunc("/", serveHTTP)  

     if err := http.ListenAndServe(":8082", myMux); err != nil { 
     logFatal("Error when starting or running http server: %v", err) 
    }  

} 

func serveHTTP(w http.ResponseWriter, r *http.Request) { 

     var isMaster bool = true   
    var desiredAspectRatio float64 = 1.77 

    if r.RequestURI == "/favicon.ico" { 
     w.WriteHeader(http.StatusNotFound) 
     return 
    } 

    if len(strings.TrimSpace(r.URL.Query().Get("ar"))) != 0 { 
     desiredAspectRatio, _ = strconv.ParseFloat(r.URL.Query().Get("ar"), 64) 
    } 

    if len(strings.TrimSpace(r.URL.Query().Get("ism"))) != 0 {  
     isMaster, _ = strconv.ParseBool(r.URL.Query().Get("ism")) 
    } 

    imageUrl := strings.ToLower(r.URL.Path[1:])  

    isProcessed := CreateMaster(imageUrl, desiredAspectRatio, isMaster)  

    if isProcessed == false { 
     w.WriteHeader(http.StatusNotFound) 
     return 
    } 

    if !sendResponse(w, r, imageUrl) { 
     // w.WriteHeader() is skipped intentionally here, since the response may be already partially created. 
     return 
    } 


    logRequestMessage(r, "SUCCESS")  
} 


func sendResponse(w http.ResponseWriter, r *http.Request, imageUrl string) bool { 

    w.Header().Set("Content-Type", "text/plain")   

    if _, err := w.Write([]byte("master created")); err != nil { 
     logRequestError(r, "Cannot send image from imageUrl=%v to client: %v", imageUrl, err) 
     return false 
    } 
    return true 
} 


func CreateMaster(keyName string, desiredAspectRatio float64, isMaster bool) bool {    

    s3Client := s3.New(session.New(), &aws.Config{Region: aws.String(region)}) 
     params := &s3.GetObjectInput{ 
     Bucket: aws.String(bucketName), 
     Key: aws.String(keyName), 
     } 

    fmt.Println(" Master creation request for key : " + keyName) 
    out, err := s3Client.GetObject(params) 

    if err != nil { 
     return false          
    } 

    defer out.Body.Close() 
    img, err := ioutil.ReadAll(out.Body) 

    if err != nil { 
      return false  
    }     

    mw := imagick.NewMagickWand() 
    defer mw.Destroy() 

    err = mw.ReadImageBlob(img) 
    if err != nil { 
     return false     
    } 


    if isMaster == false { 
     paramsPut := &s3.PutObjectInput{ 
        Bucket:   aws.String(masterBucketName), 
        Key:   aws.String(keyName), 
        Body:   bytes.NewReader(mw.GetImageBlob()), 
      } 

     _, err = s3Client.PutObject(paramsPut) 
     if err != nil { 
      log.Printf("Couldn't put the image on s3 : " + keyName + "%s\n", err)  
     } 

     return true 
    } 


     originalWidth := float64(mw.GetImageWidth()) 
     originalHeight := float64(mw.GetImageHeight()) 

    imageAspectRatio := originalWidth/originalHeight 
     masterWidth := cwMasterWidth 
     masterHeight := cwMasterHeight 
     masterAspectRatio := math.Trunc((cwMasterWidth/cwMasterHeight) * 100)/100 

    if masterAspectRatio != desiredAspectRatio {   
        masterAspectRatio = desiredAspectRatio 
       } 


    pwm := imagick.NewPixelWand() 
    defer pwm.Destroy() 

    tx := imagick.NewMagickWand() 
    defer tx.Destroy() 

    if isMaster == true {    

      var w, h uint = 0, 0 
      size := fmt.Sprintf("%dx%d^+0+0", w, h) 
        if imageAspectRatio <= masterAspectRatio {     
         // trim the height 
      w = uint(originalWidth) 
      h = (uint(originalWidth/masterAspectRatio)) 
       size = fmt.Sprintf("%dx%d^+0+0", w, h) 
        } else { 
         // trim the width 
      w = uint(originalHeight * masterAspectRatio) 
      h = uint(originalHeight) 
      size = fmt.Sprintf("%dx%d^+0+0", w, h) 
        } 

      tx = mw.TransformImage("", size)   
      tx.SetImageGravity(imagick.GRAVITY_CENTER) 
      offsetX := -(int(w) - int(tx.GetImageWidth()))/2 
      offsetY := -(int(h) - int(tx.GetImageHeight()))/2 
      err := tx.ExtentImage(w, h, offsetX, offsetY) 

        if float64(tx.GetImageWidth()) > masterWidth && float64(tx.GetImageHeight()) > masterHeight {           
         err = tx.ResizeImage(uint(masterWidth), uint(masterHeight), imagick.FILTER_BOX, 1) 
      if err != nil { 
       log.Printf("Inside CreateMaster function Couldn't resize the image : " + keyName + "%s\n", err) 
       return false     
      }               
        }          
       }  

    paramsPut := &s3.PutObjectInput{ 
        Bucket:   aws.String(masterBucketName), 
        Key:   aws.String(keyName), 
        Body:   bytes.NewReader(tx.GetImageBlob()), 
      } 

    _, err = s3Client.PutObject(paramsPut) 
    if err != nil { 
     log.Printf("Inside CreateMaster function Couldn't put the image on s3 : " + keyName + "%s\n", err) 
     return false   
    } 

    return true 
} 
+0

我不熟悉'imagemagic'或's3' API;但是如果'会话'与用户会话有任何关系,那么你没有正确处理它。通常,内存中会话使用映射,并且它们将一直保留在内存中,直到您明确删除它(或实施了某种超时策略)。 –

+1

我对imagick也不熟悉,但对'imagick.Terminate()'的调用永远不会执行。也许这是一个线索。除此之外,我浏览了代码,看起来很好。 – thwd

+0

@thwd如果我不包含这些行,那么/ tmp文件夹的大小将呈指数级增长。这就是为什么我将它们包含在我的代码 – Naresh

回答

3

您正在泄漏魔杖。

在这里,你分配一个新的魔杖,推迟其销毁:

tx := imagick.NewMagickWand() 
defer tx.Destroy() 

但是再往下去,在“如果”块,你是从调用返回到TransformImage()魔杖更换:

 tx = mw.TransformImage("", size)   
     tx.SetImageGravity(imagick.GRAVITY_CENTER) 

如果你彻底摆脱新magick棒的第一次分配,而只是确保Destroy()新的魔杖TransformImage()返回,泄漏消失。

参考的issue tracker, #72,详情

+0

非常感谢你。 – Naresh