Je me suis fait une version multithread du serveur WSGI que l'on trouve avec wsgiref. Ça pourra sans doute intéresser certain d'entre vous

WSGIMultiThreadServer.py:
Code : Sélectionner tout - Visualiser dans une fenêtre à part
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
import threading 
from Queue import Queue, Empty, Full
from wsgiref.simple_server import WSGIServer, WSGIRequestHandler
 
class WSGIThreadWorker(threading.Thread):
 
    def __init__(self, pServer, pQueue, pGroup=None, pName=None, pVerbose=None):
        threading.Thread.__init__(self, group=pGroup, target=None, name=pName, args=None, kwargs=None, verbose=pVerbose)
        self.__queue = pQueue
        self.__server = pServer
 
    def run(self):
        while True:
            try:
                lRequest, lClientAddress = self.__queue.get()
                try:
                    self.__server.finish_request(lRequest, lClientAddress)
                    self.__server.close_request(lRequest)
                except:
                    self.__server.handle_error(lRequest, lClientAddress)
                    self.__server.close_request(lRequest)
                self.__queue.task_done()
            except Empty:
                # Doing something if it's the queue is empty
                pass
            except Full:
                # Doing something if it's the queue is full
                pass
 
class WSGIThreadedServer(WSGIServer):
 
    def __init__(self, pServerAddress, pRequestHandlerClass, pMaxThread = 5, pBindAndActivate=True):
        WSGIServer.__init__(self, server_address = pServerAddress, RequestHandlerClass = pRequestHandlerClass, bind_and_activate = pBindAndActivate)
        self.__maxThread = pMaxThread
        self.__queue = Queue()
        self.__threads = []
 
        for i in range(self.__maxThread):
            lNewThreadWorker = WSGIThreadWorker(self, self.__queue, pName = "WSGIThreadWorker-" + str(i+1))
            lNewThreadWorker.daemon = True
            lNewThreadWorker.start()
            self.__threads += [lNewThreadWorker]
 
    def process_request(self, pRequest, pClientAddress):
        self.__queue.put((pRequest,pClientAddress))
 
def make_multithread_server(pHost, pPort, app, pMaxThread = 5, pServerClass=WSGIThreadedServer, pHandlerClass=WSGIRequestHandler):
    server = pServerClass((pHost, pPort), pHandlerClass, pMaxThread = pMaxThread)
    server.set_app(app)
    return server
pour le tester voici test.py:
Code : Sélectionner tout - Visualiser dans une fenêtre à part
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
#!/usr/bin/env python
import os
import threading
import logging
import signal
 
logger = None
 
def ShudownGracefully(pSignal, pFrame):
    logger.info('Shutdown gracefully')
 
if logger is None:
    logger = logging.getLogger('WSGIMultiThreadServer')
    handler = logging.FileHandler('WSGIMultiThreadServer.log')
    formatter = logging.Formatter('%(asctime)-15s %(process)06d %(thread)06d %(filename)s %(lineno)d %(module)s %(name)s %(levelname)s %(message)s')
    handler.setFormatter(formatter)
    logger.addHandler(handler)
    logger.setLevel(logging.INFO)  
    logger.info('Active log')
    signal.signal(signal.SIGTERM, ShudownGracefully)
    logger.info('Register ShudownGracefully')  
 
def app(environ, start_response):
    lCurrentThread = threading.currentThread()
    logger.info('Start request')
    response_body = 'PID: %d\n' % os.getpid()
    response_body += 'TID: %d\n' % lCurrentThread.ident
    response_body += 'Thread name: %s\n' % lCurrentThread.name
    response_body += '\n'.join(['%s: %s' % (key, value) for key, value in sorted(environ.items())])
 
    # Response_body has now more than one string
    response_body = ['The Beggining\n',
                   '*' * 30 + '\n',
                   response_body,
                   '\n' + '*' * 30 ,
                   '\nThe End']
 
    # So the content-lenght is the sum of all string's lengths
    content_length = 0
    for s in response_body:
        content_length += len(s)
 
    status = '200 OK'
    response_headers = [('Content-Type', 'text/plain'), ('Content-Length', str(content_length))]
    start_response(status, response_headers)
 
    logger.info('Finish request')  
    return response_body
 
logger.info('Loaded test.py')
 
if __name__ == "__main__":
    from WSGIMultiThreadServer import make_multithread_server 
    # specify your 
    lWSGIMultiThreadServer = make_multithread_server('', 8000, app)
    # Respond to requests until process is killed
    lWSGIMultiThreadServer.serve_forever()
Ça donne un résultation du type :
Code : Sélectionner tout - Visualiser dans une fenêtre à part
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
The Beggining
******************************
PID: 81942
TID: 4342730752
Thread name: WSGIThreadWorker-5
APP_ICON_77698: ../Resources/aptana.icns
Apple_PubSub_Socket_Render: /tmp/launch-vbK50x/Render
COMMAND_MODE: unix2003
CONTENT_LENGTH: 
CONTENT_TYPE: text/plain
DBUS_LAUNCHD_SESSION_BUS_SOCKET: /tmp/launch-G71IJL/unix_domain_listener
DISPLAY: /tmp/launch-iLtHZj/org.x:0
DJANGO_SETTINGS_MODULE: TestFCGI.settings
GATEWAY_INTERFACE: CGI/1.1
HOME: /Users/me
HTTP_ACCEPT: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8
HTTP_ACCEPT_CHARSET: ISO-8859-1,utf-8;q=0.7,*;q=0.7
HTTP_ACCEPT_ENCODING: gzip, deflate
HTTP_ACCEPT_LANGUAGE: en-us,en;q=0.5
HTTP_CACHE_CONTROL: max-age=0
HTTP_CONNECTION: keep-alive
HTTP_HOST: localhost:8000
HTTP_KEEP_ALIVE: 115
HTTP_USER_AGENT: Mozilla/5.0 (Macintosh; Intel Mac OS X 10.6; rv:2.0.1) Gecko/20100101 Firefox/4.0.1
LOGNAME: me
PATH: /usr/bin:/bin:/usr/sbin:/sbin:/usr/local/bin:/usr/X11/bin:/opt/local/bin:/usr/local/git/bin
PATH_INFO: /
PYDEV_COMPLETER_PYTHONPATH: /Applications/Aptana Studio 3/plugins/org.python.pydev_2.0.0.2011040403/PySrc
PYDEV_CONSOLE_ENCODING: UTF-8
PYTHONIOENCODING: UTF-8
PYTHONPATH: 
QUERY_STRING: 
REMOTE_ADDR: 127.0.0.1
REMOTE_HOST: localhost
REQUEST_METHOD: GET
SCRIPT_NAME: 
SERVER_NAME: 106.1.168.192.in-addr.arpa
SERVER_PORT: 8000
SERVER_PROTOCOL: HTTP/1.1
SERVER_SOFTWARE: WSGIServer/0.1 Python/2.6.6
SHELL: /bin/bash
SSH_AUTH_SOCK: /tmp/launch-fOgohQ/Listeners
TMPDIR: /var/folders/80/80vq-Sn2FEqhl0B0FlfM4E+++TI/-Tmp-/
USER: me
__CF_USER_TEXT_ENCODING: 0x1F5:0:0
com.apple.java.jvmTask: JNI
wsgi.errors: <open file '<stderr>', mode 'w' at 0x1002601e0>
wsgi.file_wrapper: wsgiref.util.FileWrapper
wsgi.input: <socket._fileobject object at 0x1011df0d0>
wsgi.multiprocess: False
wsgi.multithread: True
wsgi.run_once: False
wsgi.url_scheme: http
wsgi.version: (1, 0)
******************************
The End
Le paramètre wsgi.multithread: True est cette fois ci réel !

J'ai bricolé ça rapidement le code peut sans aucun doute être grandement améliorer.

Si ça peut être utile à certain d'entre vous tant mieux...