Đi: hoảng sợ: lỗi thời gian chạy: địa chỉ bộ nhớ không hợp lệ hoặc tham chiếu con trỏ nil


95

Khi chạy chương trình Go của tôi, nó hoảng sợ và trả về như sau:

panic: runtime error: invalid memory address or nil pointer dereference
[signal 0xb code=0x1 addr=0x38 pc=0x26df]

goroutine 1 [running]:
main.getBody(0x1cdcd4, 0xf800000004, 0x1f2b44, 0x23, 0xf84005c800, ...)
        /Users/matt/Dropbox/code/go/scripts/cron/fido.go:65 +0x2bb
main.getToken(0xf84005c7e0, 0x10)
        /Users/matt/Dropbox/code/go/scripts/cron/fido.go:140 +0x156
main.main()
        /Users/matt/Dropbox/code/go/scripts/cron/fido.go:178 +0x61

goroutine 2 [syscall]:
created by runtime.main
        /usr/local/Cellar/go/1.0.3/src/pkg/runtime/proc.c:221

goroutine 3 [syscall]:
syscall.Syscall6()
        /usr/local/Cellar/go/1.0.3/src/pkg/syscall/asm_darwin_amd64.s:38 +0x5
syscall.kevent(0x6, 0x0, 0x0, 0xf840085188, 0xa, ...)
        /usr/local/Cellar/go/1.0.3/src/pkg/syscall/zsyscall_darwin_amd64.go:199 +0x88
syscall.Kevent(0xf800000006, 0x0, 0x0, 0xf840085188, 0xa0000000a, ...)
        /usr/local/Cellar/go/1.0.3/src/pkg/syscall/syscall_bsd.go:546 +0xa4
net.(*pollster).WaitFD(0xf840085180, 0xf840059040, 0x0, 0x0, 0x0, ...)
        /usr/local/Cellar/go/1.0.3/src/pkg/net/fd_darwin.go:96 +0x185
net.(*pollServer).Run(0xf840059040, 0x0)
        /usr/local/Cellar/go/1.0.3/src/pkg/net/fd.go:236 +0xe4
created by net.newPollServer
        /usr/local/Cellar/go/1.0.3/src/pkg/net/newpollserver.go:35 +0x382

Tôi đã xem xét các phản hồi mà những người khác có cùng ngoại lệ, nhưng không thể thấy bất kỳ điều gì đơn giản (tức là một lỗi không được xử lý).

Tôi đang chạy nó trên một máy không có quyền truy cập vào các máy chủ API được liệt kê trong mã, nhưng tôi hy vọng nó sẽ trả về một lỗi thích hợp (vì tôi đã cố gắng bắt lỗi loại đó).

package main

/*
Fido fetches the list of public images from the Glance server, captures the IDs of images with 'status': 'active' and then queues the images for pre-fetching with the Glance CLI utility `glance-cache-manage`. Once the images are added to the queue, `glance-cache-prefetcher` is called to actively fetch the queued images into the local compute nodes' image cache.

See http://docs.openstack.org/developer/glance/cache.html for further details on the Glance image cache.
*/

import (
    "bytes"
    "encoding/json"
    "fmt"
    "io/ioutil"
    /*
        "log"
        "log/syslog"
    */
    "net/http"
    "os"
    "os/exec"
)

func prefetchImages() error {

    cmd := exec.Command("glance-cache-prefetcher")
    err := cmd.Run()

    if err != nil {
        return fmt.Errorf("glance-cache-prefetcher failed to execute properly: %v", err)
    }

    return nil
}

func queueImages(hostname string, imageList []string) error {

    for _, image := range imageList {
        cmd := exec.Command("glance-cache-manage", "--host=", hostname, "queue-image", image)
        err := cmd.Run()

        if err != nil {
            return fmt.Errorf("glance-cache-manage failed to execute properly: %v", err)
        } else {
            fmt.Printf("Image %s queued", image)
        }
    }

    return nil
}

func getBody(method string, url string, headers map[string]string, body []byte) ([]byte, error) {

    client := &http.Client{}
    req, err := http.NewRequest(method, url, bytes.NewReader(body))

    if err != nil {
        return nil, err
    }

    for key, value := range headers {
        req.Header.Add(key, value)
    }

    res, err := client.Do(req)
    defer res.Body.Close()

    if err != nil {
        return nil, err
    }

    var bodyBytes []byte

    if res.StatusCode == 200 {
        bodyBytes, err = ioutil.ReadAll(res.Body)
    } else if err != nil {
        return nil, err
    } else {
        return nil, fmt.Errorf("The remote end did not return a HTTP 200 (OK) response.")
    }

    return bodyBytes, nil

}

func getImages(authToken string) ([]string, error) {

    type GlanceDetailResponse struct {
        Images []struct {
            Name   string `json:"name"`
            Status string `json:"status"`
            ID     string `json:"id"`
        }
    }

    method := "GET"
    url := "http://192.168.1.2:9292/v1.1/images/detail"
    headers := map[string]string{"X-Auth-Token": authToken}

    bodyBytes, err := getBody(method, url, headers, nil)

    if err != nil {
        return nil, fmt.Errorf("unable to retrieve the response body from the Glance API server: %v", err)
    }

    var glance GlanceDetailResponse
    err = json.Unmarshal(bodyBytes, &glance)

    if err != nil {
        return nil, fmt.Errorf("unable to parse the JSON response:", err)
    }

    imageList := make([]string, 10)

    for _, image := range glance.Images {
        if image.Status == "active" {
            imageList = append(imageList, image.ID)
        }
    }

    return imageList, nil

}

func getToken() (string, error) {

    type TokenResponse struct {
        Auth []struct {
            Token struct {
                Expires string `json:"expires"`
                ID      string `json:"id"`
            }
        }
    }

    method := "POST"
    url := "http://192.168.1.2:5000/v2.0/tokens"
    headers := map[string]string{"Content-type": "application/json"}
    creds := []byte(`{"auth":{"passwordCredentials":{"username": "glance", "password":"<password>"}, "tenantId":"<tenantkeygoeshere>"}}`)

    bodyBytes, err := getBody(method, url, headers, creds)

    if err != nil {
        return "", err
    }

    var keystone TokenResponse
    err = json.Unmarshal(bodyBytes, &keystone)

    if err != nil {
        return "", err
    }

    authToken := string((keystone.Auth[0].Token.ID))

    return authToken, nil
}

func main() {

    /*
        slog, err := syslog.New(syslog.LOG_ERR, "[fido]")

        if err != nil {
            log.Fatalf("unable to connect to syslog: %v", err)
            os.Exit(1)
        } else {
            defer slog.Close()
        }
    */

    hostname, err := os.Hostname()

    if err != nil {
        // slog.Err("Hostname not captured")
        os.Exit(1)
    }

    authToken, err := getToken()

    if err != nil {
        // slog.Err("The authentication token from the Glance API server was not retrieved")
        os.Exit(1)
    }

    imageList, err := getImages(authToken)

    err = queueImages(hostname, imageList)

    if err != nil {
        // slog.Err("Could not queue the images for pre-fetching")
        os.Exit(1)
    }

    err = prefetchImages()

    if err != nil {
        // slog.Err("Could not queue the images for pre-fetching")
        os.Exit(1)
    }

    return
}

Câu trả lời:


118

Theo tài liệu cho func (*Client) Do:

"Lỗi được trả về nếu do chính sách máy khách (chẳng hạn như CheckRedirect) gây ra hoặc nếu có lỗi giao thức HTTP. Phản hồi không phải 2xx không gây ra lỗi.

Khi err là nil, resp luôn luôn chứa non-nil resp.Body. "

Sau đó nhìn vào mã này:

res, err := client.Do(req)
defer res.Body.Close()

if err != nil {
    return nil, err
}

Tôi đoán errlà không nil. Bạn đang truy cập vào .Close()phương thức res.Bodytrước khi bạn kiểm tra err.

Các deferchỉ trì hoãn các cuộc gọi chức năng. Trường và phương pháp được truy cập ngay lập tức.


Vì vậy, thay vào đó, hãy thử kiểm tra lỗi ngay lập tức.

res, err := client.Do(req)

if err != nil {
    return nil, err
}
defer res.Body.Close()

1
Hoàn hảo! trì hoãn di chuyển sau khi kiểm tra lỗi đã giải quyết nó.
Melvin

if err! = nil, res.Body = nil, tại sao res.Body.Close () có thể được gọi?
oohcode

2
@oohcode if err! = nil then res.Body.Close () không bao giờ được gọi, vì có một câu lệnh trả về bên trong if-block.
giraffe.guru

12

Điểm tham chiếu con trỏ nil ở dòng 65 là điểm trì hoãn trong

res, err := client.Do(req)
defer res.Body.Close()

if err != nil {
    return nil, err
}

Nếu err! = Nil thì res == nil và res.Body hoảng sợ. Xử lý lỗi trước khi bảo vệ res.Body.Close ().


3

Vì tôi đã đến đây với vấn đề của mình, tôi sẽ thêm câu trả lời này mặc dù nó không liên quan chính xác đến câu hỏi ban đầu. Khi bạn đang triển khai một giao diện, hãy chắc chắn rằng bạn không quên thêm con trỏ kiểu vào khai báo hàm thành viên của mình. Thí dụ:

type AnimalSounder interface {
    MakeNoise()
}

type Dog struct {
    Name string
    mean bool
    BarkStrength int
}

func (dog *Dog) MakeNoise() {
    //implementation
}

Tôi quên phần * (con chó Dog) , tôi không khuyến khích nó. Sau đó, bạn gặp rắc rối xấu khi gọi MakeNoice trên biến giao diện AnimalSounder kiểu Dog.


-15

đối với tôi một giải pháp cho vấn đề này là thêm vào sql.Open ... sslmode = disable

Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.