Pour debugger sous Aptana avec PyDev j'utilisais la commande
Il y 2 problèmes avec cette commande :

1. Dans les variables d'environnement wsgi.multithread est indiqué comme étant à True. Alors que c'est pas vrai.

2. Toutes les requêtes sont toutes exécutées dans le même thread alors qu'en production si vous utilisez mod_fcgi avec apache elles seront exécutés par plusieurs threads.

Tant que vous n'avez pas besoin de thread supplémentaires dans votre projet ça n'a pas vraiment d'impact. Dans le cas contraire vous allez vous retrouver avec de méchants problèmes en production que vous ne verrez pas en développement. Genre blocage aléatoire ou plantage.

Pour éviter ça j'ai d'abord cherché du coté de Django et j'ai trouvé la commande runfcgi. Cette commande permet bien de lancer Django en multithread mais chez moi flup (package utilisé dans ce contexte) se bloque. Ça ne traite donc jamais les requêtes que j'envoie.

Dans mon projet j'ai vraiment besoin que l'environnement de développement et l'environnement de prod fonctionnent de la même manière. Mais je n'ai pas envie pour autant de monter un serveur apache sur mon poste de développement. Surtout qu'avec Apache c'est beaucoup moins pratique à debugger. J'ai donc commencé à regarder comment il serait possible de lancer une application WSGI en mode multithread. Avant de m'attaquer à Django j'ai regardé du coté de wsgiref qui est fourni Python. Après quelque essaies j'ai réussi à me monter un version multithread basé sur wsigref (voir post wsgiref version multithread ).
Il ne me restait plus ensuite qu'à adapter la même approche à Django.

Voici le résultat :

DjangoStandALoneMultithreadServer.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
58
59
60
61
62
from threading import Thread
from Queue import Queue as ThreadQueue, Empty as QueueEmpty, Full as QueueFull
from django.core.servers.basehttp import WSGIServer, WSGIRequestHandler
 
# ------------------------------------------------------------------------------
# Support multithread 
# ------------------------------------------------------------------------------
 
class WSGIThreadWorker(Thread):
 
    def __init__(self, pServer, pQueue, pGroup=None, pName=None, pVerbose=None):
        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 QueueEmpty:
                # Doing something if it's the queue is empty
                pass
            except QueueFull:
                # Doing something if it's the queue is full
                pass
 
class WSGIThreadedServer(WSGIServer):
 
    def __init__(self, pServerAddress, pRequestHandlerClass, pMaxThread = 5, pIPV6 = False, pBindAndActivate=True):
        WSGIServer.__init__(self, server_address = pServerAddress, RequestHandlerClass = pRequestHandlerClass, ipv6 = pIPV6, bind_and_activate = pBindAndActivate)
        self.__maxThread = pMaxThread
        self.__queue = ThreadQueue()
        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 CreateDjangoStandAloneMultiThreadServer(pHost, pPort, pMaxThread = 5, pServerClass=WSGIThreadedServer, pHandlerClass=WSGIRequestHandler):
    import settings
    from django.core.management import setup_environ
    from django.core.handlers.wsgi import WSGIHandler
    setup_environ(settings)
    server = pServerClass((pHost, pPort), pHandlerClass, pMaxThread = pMaxThread, pIPV6 = False)
    server.set_app(WSGIHandler())
    return server
 
if __name__ == "__main__":
    lWSGIMultiThreadServer = CreateDjangoStandAloneMultiThreadServer('', 8080)
    lWSGIMultiThreadServer.serve_forever()
Pour l'utiliser c'est assez simple :
Vous insérer DjangoStandALoneMultithreadServer.py dans la racine de votre projet Django

Pour lancer votre projet dans un serveur multithread en local avec Aptana :
- Vous allez sur ce fichier cliquez sur le bouton droit
- Sélectionnez l'option "Debug As"
- Option "Python Run"

Cela lancera votre projet Django en locale sur le port 8080 avec 5 threads. Les threads sont à l'écoute d'une queue. Dès que le serveur dépose une requête dans la queue un des threads la traitera. Comme pour le module mod_fcgi d'apache vous ne pouvez pas savoir quel thread traitera votre requête.

DjangoStandALoneMultithreadServer.py s'appuie sur la version 1.3 de Django. Je n'ai modifié aucun fichier ni dans Django ni dans mon projet pour le faire fonctionner. Vous devriez donc pouvoir l'utiliser tel quel dans vos projets.