Upgrade to Pro — share decks privately, control downloads, hide ads and more …

How to make your python web application run faster

How to make your python web application run faster

Slides for my talk at Python Nigeria's October Meetup


Obi Uchenna David

October 07, 2017


  1. How to make your Python web application run fast. An

    analysis of Python WSGI By Obi Uchenna David Obi Uchenna David (othreecodes) Software Developer at Tuteria Ingressive Campus Ambassador KnowIT CTO and Tutor Part time Poet
  2. Friends Share Knowledge.

  3. What We’ll Cover • What is WSGI and why we

    need it. • Different WSGI servers • Benchmarks ? • How do I make the app even faster
  4. What is WSGI and why do we need it? WSGI

    (pronounced “whiz-gee” with a hard “g” or “whiskey”) Its a specification for simple and universal interface between web servers and web applications or frameworks for the Python programming language. (PEP333)
  5. What is WSGI and why do we need it ?

    (Contd.) • Portability • Its Simple • Many Python components for web application development are standardizing on it • Web Frameworks are not made to process thousands of request. NB: WSGI is not a server, a python module, a framework, an API or any kind of software. It is just an interface specification by which server and application communicate.

  7. Friends don’t let friends use CGI.

  8. Different WSGI servers A WSGI server (meaning WSGI compliant) only

    receives the request from the client, pass it to the application and then send the response returned by the application to the client. It does nothing else.
  9. Different WSGI servers GUNICORN - Pure HTTP WSGI Server -

    Simple to configure - HTTP/1.0 and HTTP/1.1 - pip install gunicorn gunicorn --daemon --workers 3 --bind unix:/root/thecovfefe/covfefe.sock thecovfefe.wsgi gunicorn --daemon --workers 3 thecovefefe.wsgi
  10. Different WSGI servers UWSGI - It is written in C.

    - A lot of configuration options - HTTP/1.0 and HTTP/1.1 - pip install uwsgi [uwsgi] http-socket = :$(PORT) master = true processes = 4 die-on-term = true module = thecovefefe.wsgi:application memory-report = true
  11. Different WSGI servers BJOERN - It is written in C.

    - lightwieght, less than 600kb in size - HTTP/1.0 - pip install bjoern # Bind to TCP host/port pair: bjoern.run(wsgi_application, host, port) # TCP host/port pair, enabling SO_REUSEPORT if available. bjoern.run(wsgi_application, host, port, reuse_port=True) # Bind to Unix socket: bjoern.run(wsgi_application, 'unix:/path/to/socket') # Bind to abstract Unix socket: (Linux only) bjoern.run(wsgi_application, 'unix:@socket_name')
  12. Friends don’t let friends use raw WSGI.

  13. None
  14. server { # the port your site will be served

    on listen 80; # the domain name it will serve for server_name ;# www.thecovfefe.com.ng thecovfefe.com.ng; # substitute by your FQDN and machine's IP address charset utf-8; #Max upload size client_max_body_size 75M; # adjust to taste error_log /var/log/nginx/error.log; access_log /var/log/nginx/access.log; # Django media location /media { alias /root/thecovfefe/thecovfefe/media/; # your Django project's media files } location /static { alias /root/thecovfefe/thecovfefe/static/; # your Django project's static files }# Finally, send all non-media requests to the Django server. location / { include proxy_params; proxy_pass http://unix:/root/thecovfefe/covfefe.sock; } } NGINX
  15. Benchmarks ? • Do not evaluate what is going on

    within the WSGI server • Do not show if the best achievable config was used • Ain’t nobody deploying “Hello World” applications • All requests are not the same • Don’t Trust benchmarks !
  16. Way Out • Find the best achievable X config for

    Y WSGI server running on U processes that have V threads • Configuring your web server will be different for different applications • There is not a single wsgi configuration to give your web application because your web app is going to be unique • Monitoring - Performance monitoring helps Identify bottlenecks in your apps. Eg New Relic, Appdynamics, Data Dogs etc
  17. Friends don’t use friends’ app on IE7 or Opera mini

  18. Building fast web applications • Avoid premature optimization • Do

    the minimum amount of work to solve the problem • Defer the work you don't need to do immediately • Use cache when you can • Understand and avoid the N+1 query problem with relational databases
  19. CTO: “David what did you do yesterday?” ME: “I converted

    all single quotes in the code base to double quotes and wrote tests for it” CTO: • This very often happens because such developers don't really know what are the critical parts of their code or the ones that need to be optimized more • Write the first version of your code without worrying much about performance • use a profiler to instrument your code and see where the bottlenecks are • Optimize NB: This is not an Excuse to write shitty code
  20. Do the minimum amount of work to solve the problem

    • Do what just needs to be done to make your app work. • Dependency Injection • Lazy loading
  21. Defer the work you don't need to do immediately •

    Make use of job queues and Asynchronous tasks. • A simple queue system can be easily done with any kind of data store (very often Redis or MongoDB are used) or a message broker like RabbitMQ or ActiveMQ.
  22. Use cache when you can • Stores recently used information

    so that it can be quickly accessed at a later time. • Byte Code Cache • Application Cache eg Redis • HTTP Cache: using HTTP Cache headers like `Etag` and `Cache-control` • Proxy Cache eg Nginx
  23. Understand and avoid the N+1 query problem with relational databases

    all_active_user_books = {} users = User.object.filter(is_active=True): all_active_user_books = [for x in Books.objects.filter(owner=user) for user in users ]
  24. SELECT * FROM Users; --n results SELECT * FROM Books

    WHERE user_id = 1; SELECT * FROM Books WHERE user_id = 2; SELECT * FROM Books WHERE user_id = 3; SELECT * FROM Books WHERE user_id = 4; SELECT * FROM Books WHERE user_id = 5; SELECT * FROM Books WHERE user_id = 6; SELECT * FROM Books WHERE user_id = 7; . . SELECT * FROM Books WHERE user_id = n; • Use SELECT for only the data you need • Use JOIN Syntax • Use UNION • Use LIMIT Or their equivalent in whatever ORM you use.
  25. Final point This is me saying something thought Provoking.

  26. Summing Up • Don’t choose your deployment stack based on

    benchmarks • Don’t trust the defaults of any server • Write efficient code • Monitor your live production systems
  27. Friends don’t ask friends hard questions.

  28. Useful Links and Resources • https://www.digitalocean.com/community/tutorials/a-comparison-of-web-servers-for-python-based-web- applications • https://www.nginx.com/blog/maximizing-python-performance-with-nginx-parti-web-serving-and-caching/ •

    https://www.slideshare.net/GrahamDumpleton/pycon-au-2015-using-benchmarks-to-understand-how-w sgi-servers-work?qid=846707df-38a5-4dbd-aed7-2cc55adcccfa&v=&b=&from_search=17 • https://www.slideshare.net/GrahamDumpleton/pycon-us-2012-web-server-bottlenecks-and-performanc e-tuning?qid=846707df-38a5-4dbd-aed7-2cc55adcccfa&v=&b=&from_search=11 • https://blog.appdynamics.com/engineering/an-introduction-to-python-wsgi-servers-part-1/ • https://medium.freecodecamp.org/a-guide-to-asynchronous-programming-in-python-with-asyncio-232e2 afa44f6 • Search for gunicorn , uwsgi and bjoern on github
  29. Thanks! Contact me at: daviduchenna@outlook.com http://davidmadethis.com