2015-12-06 8 views
22

Ho un processo/app Go1.5.1. Quando eseguo /usr/sbin/lsof -p su quel processo, vedo un sacco di "impossibile identificare il protocollo".Il socket non è in grado di identificare il protocollo (perdita socket)

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 stato// 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] 
... 

ma non v'è alcuna uscita simile a netstat -a.

Quali sono queste prese e come posso scoprire cosa fanno?

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

perché stai costruendo l'intero stack client, dialer, trasporto, ecc. Per ogni chiamata? perché non utilizzare un singolo client per tutto? il client esegue il pooling e il riciclaggio delle connessioni, ecc. –

+8

Non hai timeout per le tue richieste, quindi qualsiasi richiesta che blocca lascerà una connessione aperta. Hai anche disabilitato i keepal TCP, quindi le connessioni interrotte potrebbero non essere mai rilevate. – JimB

+0

@ JIMB grazie per la risposta. – user587170

risposta

-1

Ci sono un paio di punti qui.

Non è stato possibile riprodurre il tuo comportamento, in ogni caso, can't identify protocol è solitamente collegato a socket non correttamente chiusi.

Alcuni commentatori hanno suggerito che non è necessario creare un client http in ogni gestore: è vero. Basta crearlo una volta e riutilizzarlo.

In secondo luogo, non sono sicuro del motivo per cui si sta creando la propria struttura http.Client e perché si disabilitano i keepalive. Non puoi semplicemente andare con http.Get? Il codice più semplice è più facile da eseguire il debug.

In terzo luogo, non sono sicuro perché si sta sovrascrivendo la funzione transport.Dial. Anche se si deve farlo, la documentazione (per Go 1.9.2) dice:

% 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) 

Questo commento sulla deprecazione e la mancanza di quadranti riutilizzo può indicare la fonte dei vostri problemi.

Per riassumere, quando nei tuoi panni, mi piacerebbe fare due cose: * creazione cliente mossa per il codice che viene eseguito una volta, o semplicemente usare client predefinito con http.Get * Mi piacerebbe ripulire questo cosa con sovrascrivendo campi di trasporto predefiniti, se è necessario farlo, utilizzare DialContext come suggerito.

Buona fortuna.

+0

Qual è la logica alla base del downvoting? Ho fatto un serio sforzo per riprodurre e capire il problema, quindi ho dato il feedback basato su quello. Come fa il downvoter a farlo meglio, esattamente? –

Problemi correlati