简体   繁体   中英

How to handle simultaneous requests in Django?

I have a standard function-based view in Django which receives some parameters via POST after the user has clicked a button, computes something and then returns a template with context.

@csrf_exempt
def myview(request, param1, param2):

   if request.method == 'POST':
      return HttpResponseRedirect(reverse("app1:view_name", args=[param1, param2]))

   '''Calculate and database r/w'''

   template = loader.get_template('showData.html')
   return HttpResponse(template.render(context, request))

It works with no problem as long as one request is processed at the time (tested both with runserver and in an Apache server).

However, when I use two devices and click on the button simultaneously in each, both requests are mixed up, run simultaneously, and the website ends up trowing a 500 error , or 404 or sometimes success but cannot GET static files.. (again, tested both with runserver and Apache).

How can I force Django to finish the execution of the current request before starting the next? Or is there a better way to tackle this?

Any light on this will be appreciated. Thanks!

To coordinate threads within a single server process, use

from threading import RLock

lock = RLock()

and then within myview :

    lock.acquire()
    ...  # get template, render it
    lock.release()

You might start your server with $ uwsgi --processes 1 --threads 2...

Django web server on local machine is not for production environment. So it processes one request at a time. In production, you need to use WSGI server, like uwsgi. With that your app can be set up to serve more than one request at a time. Check https://docs.djangoproject.com/en/2.1/howto/deployment/wsgi/uwsgi/

I post my solution in case its of any help to other.

Finally I configured Apache with a pre-forking to isolate requests from each other. According to the documentation the pre-forking is advised for sites using non-thread-safe libraries (my case, apparently).

With this fix Apache can handle well simultaneous requests. However I will still be glad to hear if someone else has other suggestions!

There should be ways to rewrite the code such, that things do not get mixed up. (At least in many cases this is possible)

One of the pre-requirements (if your server uses threading) is to write thread safe code This means not using global variables (which is bad practice anyway) (or protecting them with Lock s) and using no calls to functions that aren't thread safe. (or protect them with Lock s)

As you don't provide any details we cannot help with this. (this = finding a way to not make the whole request blocking, but keep data integrity)

Otherwise you could use a mutex / Lock, that works across multiple processes.

you could for example try to access a locked file https://pypi.org/project/filelock/ and block until the file is unlocked by the other view.

example code (after pip installing filelock)

from filelock import FileLock
lock = FileLock("my.lock")
with lock:
    if request.method == 'POST':
        return HttpResponseRedirect(reverse("app1:view_name", args=[param1, param2]))

    '''Calculate and database r/w'''

    template = loader.get_template('showData.html')
    return HttpResponse(template.render(context, request))

If you use uwsgi, then you could look at the uwsgi implementation of locks:

https://uwsgi-docs.readthedocs.io/en/latest/Locks.html

Here the example code from the uwsgi documentation:

def use_lock_zero_for_important_things():
    uwsgi.lock() # Implicit parameter 0
    # Critical section
    uwsgi.unlock() # Implicit parameter 0

def use_another_lock():
    uwsgi.lock(1)
    time.sleep(1) # Take that, performance! Ha!
    uwsgi.unlock(1)

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM