2015-12-06 15 views
22

Tôi có quy trình/ứng dụng Go1.5.1. Khi tôi chạy /usr/sbin/lsof -p trong quá trình đó, tôi thấy rất nhiều "không thể xác định giao thức".Ổ cắm không thể xác định giao thức (rò rỉ ổ cắm)

monitor_ 13105 root 101u sock  0,6  0t0 16960100 can't identify protocol 
monitor_ 13105 root 102u sock  0,6  0t0 21552427 can't identify protocol 
monitor_ 13105 root 103u sock  0,6  0t0 17565091 can't identify protocol 
monitor_ 13105 root 104u sock  0,6  0t0 18476870 can't identify protocol 

proc tình trạng/giới hạn/fd

[[email protected]_q ~]# cat /proc/13105/status 
Name: monitor_client 
State: S (sleeping) 
Tgid: 13105 
Pid: 13105 
PPid: 13104 
TracerPid: 0 
Uid: 0 0 0 0 
Gid: 0 0 0 0 
Utrace: 0 
FDSize: 16384 
Groups: 
... 


[[email protected]_q ~]# cat /proc/13105/limits 
Limit      Soft Limit   Hard Limit   Units  
Max cpu time    unlimited   unlimited   seconds 
Max file size    unlimited   unlimited   bytes  
Max data size    unlimited   unlimited   bytes  
Max stack size   10485760    unlimited   bytes  
Max core file size  0     unlimited   bytes  
Max resident set   unlimited   unlimited   bytes  
Max processes    3870     3870     processes 
Max open files   9999     9999     files  
Max locked memory   65536    65536    bytes  
Max address space   unlimited   unlimited   bytes  
Max file locks   unlimited   unlimited   locks  
Max pending signals  3870     3870     signals 
Max msgqueue size   819200    819200    bytes  
Max nice priority   0     0      
Max realtime priority  0     0      
Max realtime timeout  unlimited   unlimited   us 

[[email protected]_q ~]# ll /proc/13105/fd/ 
lrwx------ 1 root root 64 Dec 7 00:15 8382 -> socket:[52023221] 
lrwx------ 1 root root 64 Dec 7 00:15 8383 -> socket:[51186627] 
lrwx------ 1 root root 64 Dec 7 00:15 8384 -> socket:[51864232] 
lrwx------ 1 root root 64 Dec 7 00:15 8385 -> socket:[52435453] 
lrwx------ 1 root root 64 Dec 7 00:15 8386 -> socket:[51596071] 
lrwx------ 1 root root 64 Dec 7 00:15 8387 -> socket:[52767667] 
lrwx------ 1 root root 64 Dec 7 00:15 8388 -> socket:[52090632] 
lrwx------ 1 root root 64 Dec 7 00:15 8389 -> socket:[51739068] 
lrwx------ 1 root root 64 Dec 7 00:15 839 -> socket:[22963529] 
lrwx------ 1 root root 64 Dec 7 00:15 8390 -> socket:[52023223] 
lrwx------ 1 root root 64 Dec 7 00:15 8391 -> socket:[52560389] 
lrwx------ 1 root root 64 Dec 7 00:15 8392 -> socket:[52402565] 
... 

nhưng không có đầu ra tương tự trong netstat -a.

Các ổ cắm này là gì và làm cách nào để tìm hiểu những gì chúng làm?

monitor_client.go

package main 

import (
    "crypto/tls" 
    "encoding/json" 
    "fmt" 
    "log" 
    "net" 
    "net/http" 
    nurl "net/url" 
    "strconv" 
    "strings" 
    "syscall" 
    "time" 
) 

type Result struct { 
    Error  string  `json:"error"` 
    HttpStatus int   `json:"http_status"` 
    Stime  time.Duration `json:"http_time"` 
} 

//http://stackoverflow.com/questions/20990332/golang-http-timeout-and-goroutines-accumulation 
//http://3.3.3.3/http?host=3.2.4.2&servername=a.test&path=/&port=33&timeout=5&scheme=http 
func MonitorHttp(w http.ResponseWriter, r *http.Request) { 
    var host, servername, path, port, scheme string 
    var timeout int 
    u, err := nurl.Parse(r.RequestURI) 
    if err != nil { 
     log.Fatal(err) 
     return 
    } 
    if host = u.Query().Get("host"); host == "" { 
     host = "127.0.0.0" 
    } 
    if servername = u.Query().Get("servername"); servername == "" { 
     servername = "localhost" 
    } 
    if path = u.Query().Get("path"); path == "" { 
     path = "/" 
    } 
    if port = u.Query().Get("port"); port == "" { 
     port = "80" 
    } 
    if scheme = u.Query().Get("scheme"); scheme == "" { 
     scheme = "http" 
    } 

    if timeout, _ = strconv.Atoi(u.Query().Get("timeout")); timeout == 0 { 
     timeout = 5 
    } 

    //log.Printf("(host)=%s (servername)=%s (path)=%s (port)=%s (timeout)=%d", host, servername, path, port, timeout) 

    w.Header().Set("Content-Type", "application/json") 

    res := httptool(host, port, servername, scheme, path, timeout) 
    result, _ := json.Marshal(res) 
    fmt.Fprintf(w, "%s", result) 
} 

func httptool(ip, port, servername, scheme, path string, timeout int) Result { 

    var result Result 
    startTime := time.Now() 
    host := ip + ":" + port 

    transport := &http.Transport{ 
     TLSClientConfig: &tls.Config{InsecureSkipVerify: true}, 
     DisableKeepAlives: true, 
    } 

    dialer := net.Dialer{ 
     Timeout: time.Duration(timeout) * time.Second, 
     KeepAlive: 0 * time.Second, 
    } 
    transport.Dial = func(network, address string) (net.Conn, error) { 
     return dialer.Dial(network, address) 
    } 

    client := &http.Client{ 
     Transport: transport, 
    } 
    rawquery := "" 
    url := fmt.Sprintf("%s://%s%s%s", scheme, host, path, rawquery) 
    req, err := http.NewRequest("GET", url, nil) 
    if err != nil { 
     result.HttpStatus = -1 
     errs := strings.Split(err.Error(), ": ") 
     result.Error = errs[len(errs)-1] 
     result.Stime = time.Now().Sub(startTime)/time.Millisecond 
     return result 
    } 
    req.Header.Set("User-Agent", "monitor worker") 
    req.Header.Set("Connection", "close") 
    req.Host = servername 
    resp, err := client.Do(req) 
    //https://github.com/Basiclytics/neverdown/blob/master/check.go 
    if err != nil { 
     nerr, ok := err.(*nurl.Error) 
     if ok { 
      switch cerr := nerr.Err.(type) { 
      case *net.OpError: 
       switch cerr.Err.(type) { 
       case *net.DNSError: 
        errs := strings.Split(cerr.Error(), ": ") 
        result.Error = "dns: " + errs[len(errs)-1] 
       default: 
        errs := strings.Split(cerr.Error(), ": ") 
        result.Error = "server: " + errs[len(errs)-1] 
       } 
      default: 
       switch nerr.Err.Error() { 
       case "net/http: request canceled while waiting for connection": 
        errs := strings.Split(cerr.Error(), ": ") 
        result.Error = "timeout: " + errs[len(errs)-1] 

       default: 
        errs := strings.Split(cerr.Error(), ": ") 
        result.Error = "unknown: " + errs[len(errs)-1] 
       } 
      } 

     } else { 
      result.Error = "unknown: " + err.Error() 
     } 
     result.HttpStatus = -2 
     result.Stime = time.Now().Sub(startTime)/time.Millisecond 
     return result 
    } 
    resp.Body.Close() 
    result.HttpStatus = resp.StatusCode 
    result.Error = "noerror" 
    result.Stime = time.Now().Sub(startTime)/time.Millisecond //spend time (ms) 
    return result 
} 

func setRlimit() { 
    var rLimit syscall.Rlimit 
    err := syscall.Getrlimit(syscall.RLIMIT_NOFILE, &rLimit) 
    if err != nil { 
     log.Printf("Unable to obtain rLimit", err) 
    } 
    if rLimit.Cur < rLimit.Max { 
     rLimit.Max = 9999 
     rLimit.Cur = 9999 
     err = syscall.Setrlimit(syscall.RLIMIT_NOFILE, &rLimit) 
     if err != nil { 
      log.Printf("Unable to increase number of open files limit", err) 
     } 
    } 
} 

func main() { 
    setRlimit() 
    s := &http.Server{ 
     Addr:   ":59059", 
     ReadTimeout: 7 * time.Second, 
     WriteTimeout: 7 * time.Second, 
    } 
    http.HandleFunc("/http", MonitorHttp) 

    log.Fatal(s.ListenAndServe()) 
} 
+4

tại sao bạn xây dựng toàn bộ ứng dụng khách, trình quay số, truyền tải, v.v ... cho mỗi cuộc gọi? tại sao không sử dụng một khách hàng duy nhất cho tất cả mọi thứ? khách hàng thực hiện kết nối tổng hợp và tái chế, v.v. –

+8

Bạn không có thời gian chờ cho các yêu cầu của mình, vì vậy mọi yêu cầu treo sẽ để lại kết nối mở. Bạn cũng đã vô hiệu hóa TCP keepalives, do đó, các kết nối bị hỏng có thể không bao giờ được phát hiện. – JimB

+0

@JimB cảm ơn câu trả lời của bạn. – user587170

Trả lời

-1

Có vài điểm ở đây.

Tôi không thể tạo lại hành vi của bạn, dù sao, can't identify protocol thường được gắn với các ổ cắm không được đóng đúng cách.

Một số người nhận xét đề xuất bạn không phải tạo ứng dụng khách http trong mỗi trình xử lý - đó là sự thật. Đơn giản chỉ cần tạo nó một lần và tái sử dụng.

Thứ hai, tôi không chắc chắn lý do tại sao bạn tạo cấu trúc http.Client của riêng mình và tại sao bạn vô hiệu hóa keepalives. Bạn không thể chỉ cần đi với http.Get? Mã đơn giản dễ debug hơn.

Thứ ba, không chắc chắn lý do bạn ghi đè chức năng transport.Dial. Thậm chí nếu bạn phải làm điều đó, các tài liệu hướng dẫn (ví Go 1.9.2) nói:

% go doc http.transport.dial 
type Transport struct { 
    // Dial specifies the dial function for creating unencrypted TCP 
    connections. 
    // 
    // Deprecated: Use DialContext instead, which allows the transport 
    // to cancel dials as soon as they are no longer needed. 
    // If both are set, DialContext takes priority. 
    Dial func(network, addr string) (net.Conn, error) 

Đó bình luận về deprecation và thiếu quay số tái sử dụng có thể trỏ đến nguồn gốc của vấn đề của mình.

Tóm lại, khi ở trong giày của bạn, tôi sẽ làm hai việc: * di chuyển tạo khách hàng sang mã thực hiện một lần hoặc chỉ sử dụng ứng dụng khách mặc định với http.Get * Tôi sẽ xóa nội dung này bằng ghi đè các trường truyền tải mặc định, nếu bạn phải làm như vậy thì tôi sẽ sử dụng DialContext như được đề xuất.

Chúc may mắn.

+0

Lý do cơ bản cho việc giảm giá? Tôi đã nỗ lực nghiêm túc để tái tạo và hiểu vấn đề, sau đó đưa ra phản hồi dựa trên điều đó. Làm thế nào downvoter sẽ làm điều đó tốt hơn, chính xác? –