2012-04-09 7 views
17

क्या तकनीक है जब एक TwistedWeb सर्वर चल रहा एकाधिक प्रोसेसर/कोर का उपयोग करने का उपयोग कर रहे हैं? क्या ऐसा करने का एक अनुशंसित तरीका है?TwistedWeb

मेरी twisted.web आधारित वेब सेवा अमेज़ॅन ईसी 2 उदाहरणों पर चल रही है, जिसमें अक्सर कई सीपीयू कोर (8, 16) होते हैं, और काम के प्रकार जो अतिरिक्त प्रोसेसिंग पावर से सेवा कर रहे हैं, इसलिए मैं भी इसका उपयोग करने के लिए बहुत पसंद है।

मैं समझता हूँ कि यह, haproxy, व्यंग्य या एक वेब सर्वर, एक रिवर्स प्रॉक्सी के रूप में विन्यस्त उपयोग करने के लिए मुड़ के कई उदाहरण सामने संभव है। वास्तव में, हम वर्तमान में इस तरह के एक सेटअप का उपयोग कर रहे हैं, nginx एक ही मेजबान पर चल रहे कई अपस्ट्रीम twisted.web सेवाओं के लिए एक रिवर्स प्रॉक्सी के रूप में सेवा कर रहा है, लेकिन प्रत्येक अलग बंदरगाह पर चल रहा है।

यह ठीक काम करता है, लेकिन मुझे वास्तव में क्या दिलचस्पी है, यह एक समाधान है जहां कोई "फ्रंट-फेस" सर्वर नहीं है, लेकिन सभी ट्विस्टेड प्रक्रियाएं किसी भी तरह से एक ही सॉकेट से जुड़ती हैं और अनुरोध स्वीकार करती हैं। क्या ऐसी चीज भी संभव है ... या मैं पागल हो रहा हूँ? ऑपरेटिंग सिस्टम लिनक्स (सेंटोस) है।

धन्यवाद।

एंटोन।

उत्तर

39

तरीके एक मुड़ आवेदन के लिए मल्टीप्रोसेस आपरेशन का समर्थन करने के लिए की एक संख्या हैं। शुरुआत में जवाब देने का एक महत्वपूर्ण सवाल यह है कि, आप अपने समवर्ती मॉडल की अपेक्षा करते हैं, और आपका एप्लिकेशन साझा राज्य से कैसे संबंधित है।

एक ही प्रक्रिया में मुड़कर आवेदन, समेकन सभी सहकारी (ट्विस्टेड एसिंक्रोनस आई/ओ एपीआई से मदद के साथ) और साझा राज्य कहीं भी रखा जा सकता है एक पाइथन ऑब्जेक्ट जाएगा। आपका एप्लिकेशन कोड यह जानकर चलता है कि, जब तक यह नियंत्रण छोड़ देता है, कुछ और नहीं चलेगा। इसके अतिरिक्त, आपके आवेदन का कोई भी हिस्सा जो साझा राज्य के कुछ हिस्सों तक पहुंचना चाहता है, शायद इतना आसानी से कर सकता है, क्योंकि उस स्थिति को शायद उबाऊ पुरानी पायथन ऑब्जेक्ट में रखा जा सकता है जो एक्सेस करना आसान है।

आप कई प्रक्रियाओं है, भले ही वे चल रहे सभी मुड़-आधारित अनुप्रयोगों रहे हैं, तो आप संगामिति के दो रूपों की है। एक पिछले मामले के समान है - एक विशेष प्रक्रिया के भीतर, समेकन सहकारी है। हालांकि, आपके पास एक नई तरह है, जहां कई प्रक्रियाएं चल रही हैं। आपके प्लेटफॉर्म की प्रक्रिया शेड्यूलर किसी भी समय इन प्रक्रियाओं के बीच निष्पादन स्विच कर सकता है, और इस पर आपका बहुत कम नियंत्रण होता है (साथ ही जब यह होता है तो बहुत कम दृश्यता)। यह अलग-अलग कोरों पर एक साथ चलाने के लिए आपकी दो प्रक्रियाओं को भी शेड्यूल कर सकता है (यह शायद आप जो भी उम्मीद कर रहे हैं)। इसका मतलब यह है कि आप स्थिरता के बारे में कुछ गारंटी खो देते हैं, क्योंकि एक प्रक्रिया को पता नहीं होता कि दूसरी प्रक्रिया कब आ सकती है और कुछ साझा राज्य पर काम करने की कोशिश की जाती है। यह विचार के अन्य महत्वपूर्ण क्षेत्र में जाता है, आप वास्तव में प्रक्रियाओं के बीच राज्य कैसे साझा करेंगे।

एकल प्रक्रिया मॉडल के विपरीत, आप अब किसी भी सुविधाजनक है, आसानी से पहुँचा जहां अपने सभी कोड वहां पहुंच सकते हैं अपने राज्य स्टोर करने के लिए स्थान नहीं हैं। यदि आप इसे एक प्रक्रिया में डालते हैं, तो उस प्रक्रिया में मौजूद सभी कोड इसे सामान्य पायथन ऑब्जेक्ट के रूप में आसानी से एक्सेस कर सकते हैं, लेकिन आपकी किसी अन्य प्रक्रिया में चल रहे किसी भी कोड के पास अब तक आसान पहुंच नहीं है। आपकी प्रक्रियाओं को एक दूसरे के साथ संवाद करने के लिए आपको एक आरपीसी प्रणाली खोजने की आवश्यकता हो सकती है।या, आप अपनी प्रक्रिया को विभाजित कर सकते हैं ताकि प्रत्येक प्रक्रिया केवल उन अनुरोधों को प्राप्त करे जिनके लिए उस प्रक्रिया में संग्रहीत राज्य की आवश्यकता होती है। इसका एक उदाहरण सत्रों के साथ एक वेबसाइट हो सकता है, जहां उपयोगकर्ता के बारे में सभी राज्य अपने सत्र में संग्रहीत होते हैं, और उनके सत्र कुकीज़ द्वारा पहचाने जाते हैं। एक फ्रंट-एंड प्रक्रिया वेब अनुरोध प्राप्त कर सकती है, कुकी का निरीक्षण कर सकती है, उस सत्र के लिए कौन सी बैक-एंड प्रक्रिया ज़िम्मेदार है, और उसके बाद अनुरोध को उस बैक-एंड प्रक्रिया पर अग्रेषित करें। इस योजना का अर्थ है कि बैक-एंड आमतौर पर संवाद करने की आवश्यकता नहीं होती है (जब तक आपका वेब एप्लिकेशन पर्याप्त सरल होता है - यानी, जब तक उपयोगकर्ता एक दूसरे के साथ बातचीत नहीं करते हैं, या साझा डेटा पर काम नहीं करते हैं)।

ध्यान दें कि उस उदाहरण में, एक प्री-फोर्किंग मॉडल उपयुक्त नहीं है। फ्रंट-एंड प्रक्रिया में विशेष रूप से सुनवाई बंदरगाह का स्वामित्व होना चाहिए ताकि वह बैक-एंड प्रक्रिया द्वारा संभाले जाने से पहले सभी आने वाले अनुरोधों का निरीक्षण कर सके।

बेशक, राज्य के प्रबंधन के लिए कई अन्य मॉडल के साथ कई प्रकार के आवेदन हैं। बहु-प्रसंस्करण के लिए सही मॉडल का चयन करने से पहले यह समझने की आवश्यकता होती है कि आपके आवेदन के लिए किस प्रकार की सहमति मिलती है, और आप अपने आवेदन के राज्य को कैसे प्रबंधित कर सकते हैं।

कहा जा रहा है कि ट्विस्ट के बहुत नए संस्करण (इस बिंदु के रूप में रिलीज़ नहीं) के साथ, कई प्रक्रियाओं के बीच एक सुनवाई टीसीपी पोर्ट साझा करना काफी आसान है।

from os import environ 
from sys import argv, executable 
from socket import AF_INET 

from twisted.internet import reactor 
from twisted.web.server import Site 
from twisted.web.static import File 

def main(fd=None): 
    root = File("/var/www") 
    factory = Site(root) 

    if fd is None: 
     # Create a new listening port and several other processes to help out.                  
     port = reactor.listenTCP(8080, factory) 
     for i in range(3): 
      reactor.spawnProcess(
        None, executable, [executable, __file__, str(port.fileno())], 
       childFDs={0: 0, 1: 1, 2: 2, port.fileno(): port.fileno()}, 
       env=environ) 
    else: 
     # Another process created the port, just start listening on it.                    
     port = reactor.adoptStreamPort(fd, AF_INET, factory) 

    reactor.run() 


if __name__ == '__main__': 
    if len(argv) == 1: 
     main() 
    else: 
     main(int(argv[1])) 

पुराने संस्करणों के साथ, आप कभी कभी बंदरगाह साझा करने के लिए fork उपयोग करने के साथ प्राप्त कर सकते हैं: कोड स्निपेट जो एक ही रास्ता है कि आप कुछ नए API का उपयोग हो सकता है यह पूरा करने को दर्शाता है है। बहरहाल, यह नहीं बल्कि त्रुटियों की संभावना है, कुछ प्लेटफार्मों पर विफल रहता है, और मुड़ उपयोग करने के लिए एक समर्थित तरीका नहीं है:

from os import fork 

from twisted.internet import reactor 
from twisted.web.server import Site 
from twisted.web.static import File 

def main(): 
    root = File("/var/www") 
    factory = Site(root) 

    # Create a new listening port 
    port = reactor.listenTCP(8080, factory) 

    # Create a few more processes to also service that port 
    for i in range(3): 
     if fork() == 0: 
      # Proceed immediately onward in the children. 
      # The parent will continue the for loop. 
      break 

    reactor.run() 


if __name__ == '__main__': 
    main() 

यह कांटा, के सामान्य व्यवहार की वजह से काम करता है जहां नव निर्मित प्रक्रिया (बच्चे) मूल प्रक्रिया (अभिभावक) से सभी स्मृति और फ़ाइल वर्णनकर्ताओं को विरासत में मिला है। चूंकि प्रक्रियाओं को अन्यथा अलग किया जाता है, इसलिए दो प्रक्रियाएं एक दूसरे के साथ हस्तक्षेप नहीं करती हैं, कम से कम जहां तक ​​वे पाइथन कोड निष्पादित कर रहे हैं। चूंकि फ़ाइल डिस्क्रिप्टर विरासत में हैं, या तो माता-पिता या कोई भी बच्चे पोर्ट पर कनेक्शन स्वीकार कर सकता है।

HTTP अनुरोधों को अग्रेषित करने के बाद से यह एक आसान काम है, मुझे संदेह है कि आप इन तकनीकों में से किसी एक का उपयोग करके प्रदर्शन सुधार में अधिक ध्यान देंगे। पूर्व प्रॉक्सीइंग की तुलना में थोड़ा अच्छा है, क्योंकि यह आपके तैनाती को सरल बनाता है और गैर-HTTP अनुप्रयोगों के लिए अधिक आसानी से काम करता है। उत्तरार्द्ध शायद स्वीकार्य होने के लायक होने की ज़िम्मेदारी अधिक है।

+0

जीन-पॉल, सबसे पहले, इस विस्तृत उत्तर के लिए धन्यवाद। बहुत सराहना की! मेरा आवेदन बहुत आसान है, प्रक्रियाओं के बीच कोई साझा स्थिति नहीं है, संभालने के लिए कोई सत्र नहीं है, प्रक्रियाओं के बीच संचार की आवश्यकता नहीं है। मेरा कार्य समांतर कंप्यूटिंग के लिए उपयुक्त है, और (सिद्धांत रूप में) मैं वास्तव में डॉन ' टी मेरी देखभाल करता है अगर मेरी प्रक्रियाएं एक ही सर्वर पर चलती हैं, या प्रत्येक प्रक्रिया अपनी मशीन पर चलती है। बहु-कोर CPUs के साथ बड़े सर्वर का उपयोग करने का कारण यह है कि अमेज़ॅन उन पर बेहतर I/O प्रदर्शन प्रदान करता है। –

+0

ने थोड़ा सा ट्विंक ट्रंक स्रोत (posixbase.py, tcp.py) ब्राउज़ किया, और ऐसा लगता है कि पूर्ववर्ती सॉकेट का पुन: उपयोग करने के लिए परिवर्तन वास्तव में बहुत नए हैं। निश्चित रूप से इन नई सुविधाओं पर नजर रखेगा, और उन्हें 12.1 रिलीज में देखने की उम्मीद है :) –

+0

मैंने कुछ परीक्षण किया है और 2 प्रश्न हैं जो मुझे पहेली करते हैं: 1) न केवल श्रमिक, बल्कि मास्टर आने वाले कनेक्शन स्वीकार करेंगे - मैं केवल श्रमिकों को कैसे स्वीकार कर सकता हूं? 2) क्या यह सही है कि 'reactor.listenTCP' कॉल पर 'बैकलॉग' सेट करना कुल में सॉकेट पर लागू होगा - जो कर्नेल में कतार गहराई है, और इसलिए सभी श्रमिकों के लिए संयुक्त है? – oberstet

3

अनुशंसित तरीका आईएमओ haproxy (या एक और लोड बैलेंसर) का उपयोग करना है जैसा कि आप पहले से ही हैं, बाधाएं सही ढंग से कॉन्फ़िगर किए जाने पर लोड बैलेंसर नहीं होनी चाहिए। इसके अलावा, आप कुछ फॉलओवर विधि चाहते हैं जो haproxy आपकी प्रक्रियाओं में से एक के मामले में प्रदान करता है।

यह वही TCP सॉकेट करने के लिए कई प्रक्रियाओं बाध्य करने के लिए संभव नहीं है, लेकिन यह यूडीपी साथ संभव है।

+0

इयान, आपके उत्तर के लिए धन्यवाद। आप सही हैं, लोड बैलेंसर बाधा नहीं है, मेरी मुख्य चिंता ज्यादातर सुरक्षा और बफर ओवरफ्लो-प्रकार की बग थी, जिसका अर्थ है कि अगर मैं nginx या haproxy के साथ जाता हूं, तो मुझे vulnerabilites का ट्रैक रखने और संस्करण को अपडेट करने की आवश्यकता है वह सॉफ्टवेयर नियमित रूप से। अगर पूरे ट्विस्ट थे तो वह पूरा नृत्य साइड-स्टेप किया जा सकता था। वेब –

+0

शायद कुछ सोच रहा था कि पूर्व-कांटा के साथ अपाचे क्या होता है ... –

+3

अपाचे के समान कुछ भी निश्चित रूप से संभव है। यह सच है कि सॉकेट को उसी पते पर * बाध्य * नहीं किया जा सकता है, लेकिन एक सॉकेट कुछ निश्चित पते * * * साझा किया जा सकता है *। साथ ही, मुझे नहीं लगता कि "हैप्रोक्सी का उपयोग करने का अनुशंसित तरीका" एक बहुत अच्छा कथन है। मुझे यकीन है कि कोई इसकी सिफारिश करेगा, लेकिन बहुत से लोग कुछ और सिफारिश करेंगे। –

1

यदि आप HTTPS पर भी अपनी वेब सामग्री की सेवा करना चाहते हैं, तो यही है कि आपको @ जीन-पॉल के स्निपेट के शीर्ष पर क्या करना होगा।

from twisted.internet.ssl import PrivateCertificate 
from twisted.protocols.tls import TLSMemoryBIOFactory 

''' 
Original snippet goes here 
.......... 
............... 
''' 

privateCert = PrivateCertificate.loadPEM(open('./server.cer').read() + open('./server.key').read()) 
tlsFactory = TLSMemoryBIOFactory(privateCert.options(), False, factory) 
reactor.adoptStreamPort(fd, AF_INET, tlsFactory) 

fd का उपयोग करके, आप या तो HTTP या HTTPS पर दोनों नहीं में काम करेगा। यदि आप माता-पिता प्रक्रिया पर listenSSL दोनों रखना चाहते हैं और एसएसएल fd शामिल करते हैं तो आप एसएसएल पोर्ट से दूसरे प्रक्रिया के रूप में बच्चे की प्रक्रिया को जन्म देते समय प्राप्त करते हैं।

पूरा snipper यहाँ है:

from os import environ 
from sys import argv, executable 
from socket import AF_INET 

from twisted.internet import reactor 
from twisted.web.server import Site 
from twisted.web.static import File 

from twisted.internet import reactor, ssl 
from twisted.internet.ssl import PrivateCertificate 
from twisted.protocols.tls import TLSMemoryBIOFactory 

def main(fd=None, fd_ssl=None): 
    root = File("/var/www") 
    factory = Site(root) 

    spawned = [] 
    if fd is None: 
     # Create a new listening port and several other processes to help out.                  
     port = reactor.listenTCP(8080, factory) 
     port_ssl = reactor.listenSSL(8443, factory, ssl.DefaultOpenSSLContextFactory('./server.key', './server.cer')) 
     for i in range(3): 
      child = reactor.spawnProcess(
       None, executable, [executable, __file__, str(port.fileno()), str(port_ssl.fileno())], 
       childFDs={0: 0, 1: 1, 2: 2, port.fileno(): port.fileno(), port_ssl.fileno(): port_ssl.fileno()}, 
       env=environ) 
      spawned.append(child) 
    else: 
     # Another process created the port, just start listening on it.                    
     port = reactor.adoptStreamPort(fd, AF_INET, factory) 
     cer = open('./server.cer') 
     key = open('./server.key') 
     pem_data = cer.read() + key.read() 
     cer.close() 
     pem.close() 
     privateCert = PrivateCertificate.loadPEM(pem_data) 
     tlsFactory = TLSMemoryBIOFactory(privateCert.options(), False, factory) 
     reactor.adoptStreamPort(fd_ssl, AF_INET, tlsFactory) 

    reactor.run() 

    for p in spawned: 
     p.signalProcess('INT') 


if __name__ == '__main__': 
    if len(argv) == 1: 
     main() 
    else: 
     main(int(argv[1:]))