[
अंग्रेजी संस्करण ]
हाल ही में, उच्च भार के लिए अधिक से अधिक वेब एप्लिकेशन बनाए गए हैं, लेकिन रूपरेखा के साथ उन्हें लचीले ढंग से अपने तनाव का परीक्षण करने और अपने स्वयं के तर्क को जोड़ने की अनुमति है, यह बहुत अधिक नहीं है।
बेशक, कई अलग-अलग हैं (पोस्ट के अंत में वोट देखें), लेकिन कोई कुकीज़ का समर्थन नहीं करता है, कोई कमजोर लोड देता है, कोई बहुत भारी है, और वे ज्यादातर समान अनुरोधों के लिए उपयुक्त हैं, अर्थात् गतिशील रूप से प्रत्येक तर्क का उपयोग करके अपने तर्क का उपयोग करें और साथ ही जितनी जल्दी हो सके (और आदर्श रूप से जावा में समाप्त करने के लिए यदि वह हो) - मुझे ऐसा नहीं मिला।
इसलिए, मेरा खुद से स्केच करने का निर्णय लिया गया, क्योंकि इस मामले में ये केवल 3-5 क्लासिक्स हैं। बुनियादी आवश्यकताओं: गति और गतिशील क्वेरी पीढ़ी। इसी समय, गति केवल हजारों आरपीएस नहीं है, लेकिन आदर्श रूप से - जब तनाव केवल नेटवर्क बैंडविड्थ पर निर्भर करता है और किसी भी मुफ्त मशीन से काम करता है।
स्लाइडर
आवश्यकताओं के साथ यह स्पष्ट है, अब आपको यह तय करने की आवश्यकता है कि यह सभी पर क्या काम करेगा, अर्थात्। जो http / tcp क्लाइंट का उपयोग करता है। बेशक, हम पुराने थ्रेड-प्रति-कनेक्शन मॉडल का उपयोग नहीं करना चाहते हैं, क्योंकि हम तुरंत मशीन की शक्ति और जेवीएम में संदर्भ स्विचिंग की गति के आधार पर कई हजार आरपीएस में भाग लेते हैं। इस प्रकार
अपाचे-http- क्लाइंट और इस तरह बह गए हैं। यहां आपको तथाकथित देखने की जरूरत है
एनआईओ पर निर्मित गैर-अवरुद्ध नेटवर्क क्लाइंट।
सौभाग्य से, इस जगह में जावा दुनिया में लंबे समय से एक मानक डी वास्तविक स्रोत खुला है, जो कि बहुत ही सार्वभौमिक और निम्न-स्तर भी है, यह आपको tcp और udp के साथ काम करने की अनुमति देता है।
आर्किटेक्चर
अपने प्रेषक को बनाने के लिए, हमें Netty के संदर्भ में
ChannelUpstreamHandler हैंडलर की आवश्यकता है, जहां से हमारे अनुरोध भेजे जाएंगे।
अगला, आपको प्रति सेकंड (आरपीएस) अधिकतम संभव संख्या भेजने के लिए एक उच्च-प्रदर्शन टाइमर का चयन करने की आवश्यकता है। यहां आप मानक
शेड्यूल्ड एक्सिक्यूटर सर्विस ले सकते हैं, यह मूल रूप से इस से मुकाबला करता है, लेकिन कमजोर मशीनों पर, कार्यों को जोड़ने के दौरान ओवरहेड कम होने के कारण,
हशडव्हीलटीमर (नेटी के साथ शामिल) का उपयोग करना बेहतर होता है, इसके लिए केवल एक ट्यूनिंग की आवश्यकता होती है। शक्तिशाली मशीनों पर, उनके बीच व्यावहारिक रूप से कोई अंतर नहीं है।
और आखिरी, किसी भी मशीन से अधिकतम आरपीएस निचोड़ने के लिए जब यह ज्ञात नहीं है कि इस ओएस या कुल वर्तमान लोड में कनेक्शन पर क्या सीमा है, तो परीक्षण और त्रुटि विधि का उपयोग करना सबसे विश्वसनीय है: पहले कुछ ट्रान्सेंडैंटल मान सेट करें, उदाहरण के लिए, प्रति सेकंड एक लाख अनुरोध और फिर प्रतीक्षा करें। नए कनेक्शन बनाते समय त्रुटियों की संख्या कितनी होगी। प्रयोगों से पता चला है कि अधिकतम संख्या में आरपीएस आमतौर पर इस आंकड़े से थोड़ा कम है।
यानी हम प्रारंभिक आरपीएस मूल्य के लिए यह आंकड़ा लेते हैं और फिर यदि त्रुटियों को दोहराया जाता है, तो हम इसे 10-20% तक कम कर देते हैं।
कार्यान्वयन
अनुरोध जनरेशन
डायनेमिक क्वेरी पीढ़ी का समर्थन करने के लिए, हम एकमात्र तरीका बनाते हैं जिससे हमारा तनाव अगले अनुरोध की सामग्री प्राप्त करने का कारण होगा:
public interface RequestSource { ChannelBuffer next(); }
ChannelBuffer Netty में बाइट स्ट्रीम का एक अमूर्त हिस्सा है, अर्थात यहां, अनुरोध की संपूर्ण सामग्री को बाइट्स की एक धारा के रूप में वापस किया जाना चाहिए। Http और अन्य पाठ प्रोटोकॉल के मामले में, यह क्वेरी स्ट्रिंग (पाठ) का सिर्फ एक बाइट प्रतिनिधित्व है।
इसके अलावा, http के मामले में,
आपको अनुरोध
के अंत में 2 न्यूलाइन वर्ण डालना चाहिए (\ n \ n), यह नेट्टी के लिए अनुरोध के अंत का संकेत है (अन्यथा अनुरोध नहीं भेजेगा)
प्रेषण
Netty के लिए अनुरोध भेजने के लिए, आपको सबसे पहले रिमोट सर्वर से स्पष्ट रूप से जुड़ना होगा, इसलिए क्लाइंट के शुरू में हम आवधिक कनेक्शनों को एक आवृत्ति के साथ वर्तमान आरपीएस के अनुसार शुरू करते हैं:
scheduler.startAtFixedRate(new Runnable() { @Overrid public void run() { try { ChannelFuture future = bootstrap.connect(addr); connected.incrementAndGet(); } catch (ChannelException e) { if (e.getCause() instanceof SocketException) { processLimitErrors(); } ... }, rpsRate);
एक सफल कनेक्शन के बाद, हम तुरंत अनुरोध स्वयं भेजते हैं, इसलिए हमारा नेटी हैंडलर आसानी से SimpleChannelUpstreamHandler से विरासत में मिलेगा, जहां इसके लिए एक विशेष विधि है। लेकिन एक चेतावनी है: एक नया कनेक्शन तथाकथित द्वारा संसाधित किया जाता है मुख्य धागा ("बॉस"), जहां लंबे समय तक संचालन मौजूद नहीं होना चाहिए, जो एक नए अनुरोध की पीढ़ी हो सकती है, इसलिए आपको इसे दूसरे धागे में स्थानांतरित करना होगा, परिणामस्वरूप, अनुरोध भेजने पर स्वयं कुछ ऐसा दिखाई देगा:
private class StressClientHandler extends SimpleChannelUpstreamHandler { .... @Override public void channelConnected(ChannelHandlerContext ctx, final ChannelStateEvent e) throws Exception { ... requestExecutor.execute(new Runnable() { @Override public void run() { e.getChannel().write(requestSource.next()); } }); .... } }
हैंडलिंग में त्रुटि
अगला, नए कनेक्शन बनाने के लिए त्रुटि से निपटने के लिए जब अनुरोध भेजने की वर्तमान आवृत्ति बहुत अधिक है। और यह सबसे गैर-तुच्छ हिस्सा है, या इसके बजाय इसे स्वतंत्र करना मुश्किल है, क्योंकि विभिन्न ऑपरेटिंग सिस्टम इस स्थिति में अलग तरह से व्यवहार करते हैं। उदाहरण के लिए, linux एक BindException फेंकता है, विंडोज़ एक ConnectException फेंकता है, और MacOS X या तो इनमें से एक को फेंकता है, या सामान्य रूप से एक InternalError (बहुत अधिक खुली फ़ाइलें)। इस प्रकार एम-एक्सिस पर, तनाव सबसे अप्रत्याशित रूप से व्यवहार करता है।
इस संबंध में, कनेक्शन के दौरान त्रुटियों को संभालने के अलावा, यह हमारे हैंडलर में भी करना आवश्यक है (साथ ही आँकड़ों की त्रुटियों की संख्या की गणना):
private class StressClientHandler extends SimpleChannelUpstreamHandler { .... @Override public void exceptionCaught(ChannelHandlerContext ctx, ExceptionEvent e) throws Exception { e.getChannel().close(); Throwable exc = e.getCause(); ... if (exc instanceof BindException) { be.incrementAndGet(); processLimitErrors(); } else if (exc instanceof ConnectException) { ce.incrementAndGet(); processLimitErrors(); } ... } .... }
सर्वर प्रतिक्रियाएँ
अंत में, हमें यह तय करने की आवश्यकता है कि हम सर्वर से प्रतिक्रियाओं के साथ क्या करेंगे। चूंकि यह एक तनाव परीक्षण है और केवल बैंडविड्थ हमारे लिए महत्वपूर्ण है, यह केवल आंकड़ों को पढ़ने के लिए बना रहता है:
private class StressClientHandler extends SimpleChannelUpstreamHandler { @Override public void messageReceived(ChannelHandlerContext ctx, MessageEvent e) throws Exception { ... ChannelBuffer resp = (ChannelBuffer) e.getMessage(); received.incrementAndGet(); ... } }
Http प्रतिक्रियाओं के प्रकारों की गणना भी हो सकती है (4xx, 2xx)
पूरा कोड
अतिरिक्त माल के साथ सभी कोड जैसे फाइलों से http टेम्पलेट पढ़ना, एक टेम्पलेट इंजन, टाइमआउट, आदि।
GitHub (अंतिम-तनाव) पर एक तैयार मावेन परियोजना के रूप में निहित है। वहां आप
तैयार वितरण किट (जार फ़ाइल) डाउनलोड कर सकते हैं।
निष्कर्ष
बेशक सभी खुले कनेक्शन की सीमा के खिलाफ हैं। उदाहरण के लिए, लिनक्स पर, कुछ ओएस सेटिंग्स (एलिमिट, आदि) को बढ़ाते हुए, स्थानीय मशीन पर आधुनिक हार्डवेयर पर लगभग 30K आरपीएस प्राप्त करना संभव था। सैद्धांतिक रूप से, कनेक्शन और नेटवर्क की सीमा के अलावा, व्यवहार में अधिक प्रतिबंध नहीं होने चाहिए, हालांकि, ओवरहेड जेवीएम खुद को महसूस करता है और वास्तविक आरपीएस निर्दिष्ट एक की तुलना में 20-30% कम है।