2009-03-11 6 views
9

Recentemente ho letto this document che elenca un numero di strategie che potrebbero essere utilizzate per implementare un server socket. Vale a dire, essi sono:Scrittura di un server basato su socket in Python, strategie consigliate?

  1. servire molti clienti con ogni thread, e utilizzare non bloccante di I/O e la notifica prontezza livello-innescato
  2. servire molti clienti con ogni thread, e utilizzare non bloccante di I/O e la prontezza di notifica di modifica
  3. servire molti clienti con ogni thread del server e utilizzare I/O asincrono
  4. servire un cliente con ogni thread del server e utilizzare il blocco di I/O
  5. costruire il codice del server nel kernel

Ora, gradirei un suggerimento su cui dovrebbe essere utilizzato in CPython, che sappiamo ha alcuni punti positivi e alcuni punti negativi. Sono principalmente interessato alle prestazioni in concorrenza elevata, e sì, alcune delle implementazioni attuali sono troppo lente.

Quindi, se posso iniziare con quello facile, "5" è fuori, dato che non ho intenzione di hackerare qualcosa nel kernel.

"4" Sembra che debba uscire per via del GIL. Ovviamente, qui puoi usare il multiprocessing al posto dei thread, e questo dà un notevole impulso. Anche il blocco dell'IO ha il vantaggio di essere più facile da capire.

E qui la mia conoscenza diminuisce un po ':

"1" è tradizione selezionare o sondaggio che potrebbe essere banalmente combinato con multiprocessing.

"2" è la notifica di disponibilità cambio, utilizzato dal epoll più recente e kqueue

"3" Non sono sicuro che ci sono tutte le implementazioni del kernel per questo che hanno wrapper Python.

Quindi, in Python abbiamo una borsa di grandi strumenti come Twisted. Forse sono un approccio migliore, anche se ho messo a confronto Twisted e l'ho trovato troppo lento su una macchina con più processori. Forse avere 4 twisted con un load balancer potrebbe farlo, non lo so. Tutto il consiglio sarebbe apprezzato.

risposta

7

asyncore è fondamentalmente "1" - Utilizza internamente select e un solo thread gestisce tutte le richieste. Secondo i documenti può anche usare poll. (MODIFICA: Rimosso riferimento contorto, pensavo che usasse l'asyncore, ma mi sbagliavo).

"2" può essere implementato con python-epoll (solo su Google - mai visto prima). MODIFICA: (dai commenti) In python 2.6 lo select module ha epoll, kqueue e kevent build-in (su piattaforme supportate). Quindi non hai bisogno di librerie esterne per servire in modo edge-triggered.

Non escludere "4", poiché il GIL verrà eliminato quando un thread sta effettivamente eseguendo o sta aspettando operazioni di I/O (il più delle volte probabilmente). Non ha senso se hai un numero enorme di connessioni, naturalmente. Se hai un sacco di elaborazione da fare, allora python potrebbe non avere senso con nessuno di questi schemi.

Per flessibilità, forse guardare Twisted?

In pratica il problema si riduce alla quantità di elaborazione che verrà eseguita per le richieste. Se hai un sacco di elaborazione e hai bisogno di sfruttare l'operazione parallela multi-core, probabilmente avrai bisogno di più processi. D'altra parte se hai solo bisogno di ascoltare su molte connessioni, quindi seleziona o epoll, con un piccolo numero di thread dovrebbe funzionare.

+0

Penso che epoll sia nello stdlib in 2.6+ e easy_installable per 2.5. Il pacchetto si chiama select-something. Mi dispiace per la vaghezza. –

+0

Twisted può anche usare epoll. In effetti, Twisted trasforma tutte le API di notifica eventi supportate in un'API uniforme che presenta all'utente. Quindi, se il meglio che la piattaforma può fare è selezionare, la tua app utilizza select. Se ha epoll, la tua app utilizza epoll. Tutto in modo trasparente per te. –

+0

È scritto 'asyncore' – new123456

1

http://docs.python.org/library/socketserver.html#asynchronous-mixins

Quanto macchine multi-processore (multi-core). Con CPython a causa di GIL avrai bisogno di almeno un processo per core, in scala. Come dici tu che hai bisogno di CPython, potresti provare a fare un benchmark con ForkingMixIn. Con Linux 2.6 potresti dare dei risultati interessanti.

L'altro modo è utilizzare Stackless Python. Quello è how EVE solved it. Ma capisco che non è sempre possibile.

+0

Grazie, ma vi ho benchmark quelle cose? Sono lenti. Non inventerei la ruota se non dovessi farlo. –

+0

+1 stackless/EVE, ma ho detto CPython –

1

mi piace la risposta di Douglas', ma come un a parte ...

si potrebbe usare un filo di spedizione/processo centralizzato che ascolta le notifiche di prontezza usando select e delegati a un pool di thread di lavoro/processes per contribuire a realizzare i tuoi obiettivi di parallelismo.

Come detto da Douglas, tuttavia, il GIL non verrà mantenuto durante le operazioni di I/O più lunghe (dato che non ci sono cose Python-API che stanno accadendo), quindi se la latenza della risposta ti preoccupa puoi provare a spostare il porzioni critiche del codice all'API CPython.

2

Posso suggerire ulteriori collegamenti?

cogen è una libreria multipiattaforma per la programmazione basata su coroutine orientata alla rete che utilizza i generatori avanzati di python 2.5. Nella pagina principale del progetto cogen ci sono collegamenti a diversi progetti con scopi simili.

3

E la "forcella"? (Suppongo che sia ciò che fa ForkingMixIn) Se le richieste sono gestite in un'architettura "shared nothing" (diversa dal DB o dal file system), fork() inizia abbastanza velocemente sulla maggior parte dei * nix e non ti devi preoccupare su tutti gli stupidi bug e le complicazioni del threading.

I thread sono una malattia di progettazione imposta dai sistemi operativi con processi troppo pesanti, IMHO. La clonazione di una tabella di pagina con attributi copy-on-write sembra un piccolo prezzo, soprattutto se si sta eseguendo un interprete in ogni caso.

Mi dispiace non posso essere più specifico, ma io sono più di un programmatore Perl-transizione-to-Rubino (quando non sono schiavi su masse di Java sul lavoro)


Aggiornamento : Ho finalmente fatto dei tempi sul thread vs fork nel mio "tempo libero". Check it out:

http://roboprogs.com/devel/2009.04.html

Expanded: http://roboprogs.com/devel/2009.12.html

+0

Inoltre, è possibile inserire il codice per altri moduli che si sa che verranno utilizzati prima di avviare i processi figli. Ciò impedirà loro di essere tokenizzati (JIT-ed, qualunque cosa) più e più volte. In secondo luogo, mantieni i dati nel genitore piccolo, usa "exit" come un super garbage collector. – Roboprog

3

Una sollution è gevent. Gevent maries un polling di evento basato su libevent con commutazione di attività cooperativa leggera implementata da greenlet.

Ciò che ottieni sono tutte le prestazioni e la scalabilità di un sistema di eventi con l'eleganza e il modello diretto della programmazione IO di blocco.

(io non so quale sia il SO convenzione circa rispondendo alle domande davvero vecchi è, ma ho deciso che sarei ancora aggiungere i miei 2 centesimi)

Problemi correlati