sockets - बहुत से अनुरोधों के बाद 'एबी' कार्यक्रम फ्रीज, क्यों?




debugging networking (2)

जब भी मैं किसी वेब सर्वर को बेंचमार्क करने के लिए 'ab' का उपयोग करता हूं, तो यह बहुत से अनुरोध भेजे जाने के बाद थोड़ी देर के लिए स्थिर हो जाएगा, केवल 20 सेकंड या उसके बाद जारी रखने के लिए।

रुबी में लिखे गए निम्नलिखित HTTP सर्वर सिम्युलेटर पर विचार करें:

require 'socket'

RESPONSE = "HTTP/1.1 200 OK\r\n" +
           "Connection: close\r\n" +
           "\r\n" +
           "\r\n"

buffer = ""
server = TCPServer.new("127.0.0.1", 3000)  # Create TCP server at port 3000.
server.listen(1024)                        # Set backlog to 1024.
while true
    client = server.accept             # Accept new client.
    client.write(RESPONSE)             # Write a stock "HTTP" response.
    client.close_write                 # Shutdown write part of the socket.
    client.read(nil, buffer)           # Read all data from the socket.  
    client.close                       # Close it.
end

मैं फिर निम्नानुसार चलाता हूं:

ab -n 45000 -c 10 http://127.0.0.1:3000/

पहले कुछ सेकंड के दौरान, अब अपना काम करता है क्योंकि यह 100% सीपीयू का उपयोग करता है और इसका उपयोग करता है:

Benchmarking 127.0.0.1 (be patient)
Completed 4500 requests
Completed 9000 requests
Completed 13500 requests

लगभग 13500 अनुरोधों के बाद, सिस्टम सीपीयू उपयोग 0% तक गिर जाता है। अब कुछ पर जमे हुए प्रतीत होता है। समस्या सर्वर में नहीं है क्योंकि इस समय, सर्वर स्वीकार () को कॉल कर रहा है। लगभग 20 सेकंड के बाद जारी रहता है जैसे कुछ भी नहीं हुआ, और फिर से 100% CPU का उपयोग करेगा, केवल कई सेकंड के बाद फिर से फ्रीज करने के लिए।

मुझे संदेह है कि कर्नेल में कुछ कनेक्शन थ्रॉटलिंग है, लेकिन क्या और क्यों? मैं ओएस एक्स तेंदुए का उपयोग कर रहा हूँ। मैंने लिनक्स पर भी इसी तरह के व्यवहार को देखा है, हालांकि फ्रीज अनुरोधों की एक बड़ी संख्या में होता है और ऐसा अक्सर नहीं होता है।

यह समस्या मुझे बड़े HTTP मानक चलाने से रोकती है।


ऐसा लगता है जैसे आप क्षणिक बंदरगाहों से बाहर हो रहे हैं। जांचने के लिए, netstat कमांड का उपयोग करें और TIME_WAIT स्थिति में कई हजार बंदरगाहों को देखें।

मैक ओएस एक्स पर कुल 16384 बंदरगाहों के लिए डिफ़ॉल्ट तात्कालिक पोर्ट रेंज 49152 से 65535 है। आप इसे sysctl कमांड के साथ देख सकते हैं:

$ sysctl net.inet.ip.portrange.first net.inet.ip.portrange.last
net.inet.ip.portrange.first: 49152
net.inet.ip.portrange.last: 65535

एक बार जब आप क्षणिक बंदरगाहों से बाहर हो जाते हैं, तो आपको आमतौर पर TIME_WAIT स्थिति समाप्त होने तक प्रतीक्षा करना होगा (2 * अधिकतम सेगमेंट आजीवन) जब तक कि आप किसी विशेष पोर्ट नंबर का पुन: उपयोग नहीं कर लेते। आप 32768 पर शुरू करने के लिए रेंज को बदलकर बंदरगाहों की संख्या को दोगुना कर सकते हैं, जो कि लिनक्स और सोलारिस पर डिफ़ॉल्ट है। (अधिकतम पोर्ट नंबर 65535 है ताकि आप उच्च अंत में वृद्धि नहीं कर सकें।)

$ sudo sysctl -w net.inet.ip.portrange.first=32768
net.inet.ip.portrange.first: 49152 -> 32768

ध्यान दें कि आईएएनए द्वारा निर्दिष्ट आधिकारिक सीमा 49152 से 65535 है, और कुछ फ़ायरवॉल यह मान सकते हैं कि गतिशील रूप से असाइन किए गए बंदरगाह उस सीमा के भीतर आते हैं। अपने स्थानीय नेटवर्क के बाहर एक बड़ी सीमा का उपयोग करने के लिए आपको अपने फ़ायरवॉल को फिर से कॉन्फ़िगर करना पड़ सकता है।

अधिकतम सेगमेंट लाइफटाइम (मैक ओएस एक्स पर sysctl net.inet.tcp.msl ) को कम करना भी संभव है, जो TIME_WAIT स्थिति की अवधि को नियंत्रित करता है, लेकिन यह खतरनाक है क्योंकि इससे पुराने कनेक्शन नए हो सकते हैं वे जो एक ही पोर्ट नंबर का उपयोग कर रहे हैं। SO_REUSEADDR विकल्प के साथ विशिष्ट बंदरगाहों के लिए बाध्यकारी, या SO_LINGER विकल्प के साथ बंद होने वाली कुछ चालें भी हैं, लेकिन वे पुराने और नए कनेक्शन को मिश्रित करने का कारण भी बना सकते हैं, इसलिए आमतौर पर खराब विचार माना जाता है।


बंदरगाहों की संख्या बढ़ाने के बजाय, मैक ओएस एक्स पर TIME_WAIT की लंबाई बदलें।

यह केवल विकास में काम करता है, लेकिन अब मैं कई अनुरोधों के लिए अब पूछ सकता हूं क्योंकि मुझे समय समाप्त होने के बिना चाहिए।

डिफ़ॉल्ट टाइमआउट को 1000ms पर सेट करें:

$ sudo sysctl -w net.inet.tcp.msl=1000
net.inet.tcp.msl: 15000 -> 1000

दूसरे उत्तर में उल्लिखित brianp.net पृष्ठ अब उपलब्ध नहीं है। आप इसे इंटरनेट संग्रह से पुनर्प्राप्त कर सकते हैं।