2013-07-30 8 views
49

मैं वर्तमान में गोलांग में HTTP पोस्ट करते समय कनेक्शन का पुन: उपयोग करने का एक तरीका खोजने के लिए संघर्ष कर रहा हूं।गोलांग में http कनेक्शन का पुन: उपयोग

मैं एक परिवहन और इतने की तरह ग्राहक बना लिया है:

// Create a new transport and HTTP client 
tr := &http.Transport{} 
client := &http.Client{Transport: tr} 

मैं तो एक goroutine जो इतनी तरह एक ही endpoint के लिए कई पदों बना रही है में इस ग्राहक सूचक गुजर रही है:

r, err := client.Post(url, "application/json", post) 

नेटस्टैट को देखते हुए ऐसा लगता है कि प्रत्येक पोस्ट के लिए एक नया कनेक्शन होता है जिसके परिणामस्वरूप बड़ी संख्या में समवर्ती कनेक्शन खुले होते हैं।

इस मामले में कनेक्शन का पुन: उपयोग करने का सही तरीका क्या है?

उत्तर

8

आईआईआरसी, डिफ़ॉल्ट ग्राहक पुन: उपयोग कनेक्शन करता है। क्या आप response बंद कर रहे हैं?

कॉलर्स को राहत बंद करनी चाहिए। जब ​​से इसे पढ़ा जाता है। यदि resp.Body बंद नहीं है, तो क्लाइंट के अंतर्निहित RoundTripper (आमतौर पर परिवहन) बाद में "जीवित रहने" अनुरोध के लिए सर्वर पर एक सतत टीसीपी कनेक्शन का पुन: उपयोग करने में सक्षम नहीं हो सकता है।

+0

हाय, प्रतिक्रिया के लिए धन्यवाद। हां, क्षमा करें मुझे इसे भी शामिल करना चाहिए था। मैं r.Body.Close() के साथ कनेक्शन बंद कर रहा हूँ। – sicr

+0

@sicr, क्या आप सकारात्मक हैं कि सर्वर वास्तव में कनेक्शन को बंद नहीं करता है? मेरा मतलब है, ये उत्कृष्ट कनेक्शन '* _WAIT' राज्यों में से एक में हो सकते हैं या इस – kostix

+1

@ कोस्टिक्स जैसे कुछ मैं नेटस्टैट को देखते समय राज्य के साथ बड़ी संख्या में कनेक्शन देखता हूं। ऐसा प्रतीत होता है कि प्रत्येक POST अनुरोध पर एक नया कनेक्शन उत्पन्न किया जा रहा है क्योंकि उसी कनेक्शन का पुन: उपयोग किया जा रहा है। – sicr

61

आप यह सुनिश्चित करना चाहिए कि आप जब तक पढ़ने प्रतिक्रिया पूराClose() कॉल करने से पहले है।

उदा।

res, _ := client.Do(req) 
io.Copy(ioutil.Discard, res.Body) 
res.Body.Close() 

सुनिश्चित करने के लिए http.Client कनेक्शन पुन: उपयोग के दो काम करने के लिए सुनिश्चित हो:

  • पढ़ें जब तक प्रतिक्रिया पूरा हो गया है (यानी ioutil.ReadAll(resp.Body))
  • कॉल Body.Close()
+1

मैं एक ही होस्ट में पोस्ट कर रहा हूं। हालांकि, मेरी समझ यह है कि MaxIdleConnsPerHost निष्क्रिय निष्क्रिय होने के परिणामस्वरूप होगा। क्या यह मामला नहीं है? – sicr

+0

यह निष्क्रिय कनेक्शन का पुन: उपयोग करने का प्रयास करेगा, लेकिन मुझे लगता है कि यह आपकी समस्या नहीं है, क्योंकि यह टीसीपी कनेक्शन का पुनः उपयोग करने में सक्षम होना चाहिए। क्या आपने कनेक्शन जोड़ा था: परिवहन के लिए जीवित हेडर रखें? –

+3

+1, क्योंकि मैंने इसी तरह के प्रोग्राम में 'डिफर्स res.Body.Close()' कहा था, लेकिन उस भाग को निष्पादित करने से पहले कभी-कभी फ़ंक्शन से वापस लौटना समाप्त हो गया था (यदि 'resp.StatStodeCode! = 200', उदाहरण के लिए) जो ओपन फाइल डिस्क्रिप्टरों के ** बहुत ** ** को छोड़कर अंततः मेरे कार्यक्रम को मार डाला। इस धागे को मारने से मुझे कोड और चेहरे का हिस्सा खुद का पुनरीक्षण कर दिया। धन्यवाद। – sa125

20

संपादित करें: यह अधिक है उन लोगों के लिए एक नोट जो हर अनुरोध के लिए एक परिवहन और ग्राहक का निर्माण करते हैं।

http://golang.org/src/pkg/net/http/transport.go#L46

तो अगर आप प्रत्येक अनुरोध के लिए एक नया परिवहन बनाते हैं तो वह नए कनेक्शन के लिए हर बार पैदा करेगा:

Transport struct कि फिर से उपयोग के लिए कनेक्शन रखती है। इस मामले में समाधान ग्राहकों के बीच एक परिवहन उदाहरण साझा करना है।

22

यदि कोई अभी भी इसे कैसे करना है, इस पर उत्तर ढूंढ रहा है, तो मैं यह कर रहा हूं।

package main 

import (
    "bytes" 
    "io/ioutil" 
    "log" 
    "net/http" 
    "time" 
) 

var (
    httpClient *http.Client 
) 

const (
    MaxIdleConnections int = 20 
    RequestTimeout  int = 5 
) 

// init HTTPClient 
func init() { 
    httpClient = createHTTPClient() 
} 

// createHTTPClient for connection re-use 
func createHTTPClient() *http.Client { 
    client := &http.Client{ 
     Transport: &http.Transport{ 
      MaxIdleConnsPerHost: MaxIdleConnections, 
     }, 
     Timeout: time.Duration(RequestTimeout) * time.Second, 
    } 

    return client 
} 

func main() { 
    var endPoint string = "https://localhost:8080/doSomething" 

    req, err := http.NewRequest("POST", endPoint, bytes.NewBuffer([]byte("Post this data"))) 
    if err != nil { 
     log.Fatalf("Error Occured. %+v", err) 
    } 
    req.Header.Set("Content-Type", "application/x-www-form-urlencoded") 

    // use httpClient to send request 
    response, err := httpClient.Do(req) 
    if err != nil && response == nil { 
     log.Fatalf("Error sending request to API endpoint. %+v", err) 
    } else { 
     // Close the connection to reuse it 
     defer response.Body.Close() 

     // Let's check if the work actually is done 
     // We have seen inconsistencies even when we get 200 OK response 
     body, err := ioutil.ReadAll(response.Body) 
     if err != nil { 
      log.Fatalf("Couldn't parse response body. %+v", err) 
     } 

     log.Println("Response Body:", string(body)) 
    } 

} 

जाओ खेल का मैदान: http://play.golang.org/p/oliqHLmzSX

सारांश में, मैं एक HTTP ग्राहक बनाने के लिए एक अलग पद्धति बनाने रहा हूँ और वैश्विक चर को यह बताए और फिर इसे का उपयोग कर अनुरोध करने के लिए।नोट

defer response.Body.Close() 

यह कनेक्शन को बंद करने और इसे पुन: उपयोग के लिए तैयार फिर से सेट हो जाएगा।

आशा है कि यह किसी की मदद करेगा।

+0

यह मेरे लिए काम किया, धन्यवाद! –

+0

क्या http.Client को वैश्विक चर के रूप में वैश्विक चर के रूप में उपयोग कर रहा है यदि उस चर का उपयोग करके फ़ंक्शन को कॉल करने वाले एकाधिक goroutines हैं? –

+1

@ bn00d 'defer प्रतिक्रिया है। बॉडी। बंद करें() 'सही? मैं पूछता हूं क्योंकि बंद करने से हम मुख्य रूप से मुख्य कार्य निष्कर्ष निकालने तक पुन: उपयोग के लिए कॉन बंद नहीं करेंगे, इस प्रकार किसी को 'सीधे() 'के बाद सीधे' .Close()' को कॉल करना चाहिए। यह आपके उदाहरण बी/सी में किसी समस्या की तरह प्रतीत नहीं होता है, यह वास्तव में एकाधिक रिक्त बनाने का प्रदर्शन नहीं करता है, यह केवल एक रिक बनाता है और फिर बाहर निकलता है, लेकिन अगर हम वापस कई req बनाना चाहते हैं, तो ऐसा लगता है कि 'defer' 'ed, '.lose()' til func exits नहीं कहा जाएगा। या क्या मैं कुछ न कुछ भूल रहा हूं? धन्यवाद। –

संबंधित मुद्दे