CIS 192: Lecture 12 Deploying Apps and Concurrency
Total Page:16
File Type:pdf, Size:1020Kb
CIS 192: Lecture 12 Deploying Apps and Concurrency Lili Dworkin University of Pennsylvania Good Question from Way Back I All HTTP requests have 1) URL, 2) headers, 3) body I GET requests: parameters sent in URL I POST requests: parameters sent in body Can GET requests have a body? StackOverflow's response: \Yes, you can send a request body with GET but it should not have any meaning. If you give it meaning by parsing it on the server and changing your response based on its contents you're violating the HTTP/1.1 spec." Good Question from Last Week What is the difference between jsonify and json.dumps? def jsonify(*args, **kwargs): if __debug__: _assert_have_json() return current_app.response_class(json.dumps(dict(* args, **kwargs), indent=None if request.is_xhr else 2), mimetype='application/json') I jsonify returns a Response object I jsonify automatically sets content-type header I jsonify also sets the indentation Review Find a partner! Deploying Apps I We've been running Flask apps locally on a builtin development server I When you're ready to go public, you need to deploy to a production server I Easiest option: use one hosted by someone else! I We'll use Heroku, a platform as a service (PaaS) that makes it easy to deploy apps in a variety of languages Heroku Prerequisites: I Virtualenv (creates standalone Python environments) I Heroku toolbox I Heroku command-line client I Git (for version control and pushing to Heroku) Virtualenv I Allows us to create a virtual Python environment I Unique, isolated environment for each project I Use case: different versions of packages for different projects Virtualenv How to use it? prompt$ pip install virtualenv Now navigate to your project directory: prompt$ virtualenv --no-site-packages venv prompt$ source venv/bin/activate (<name>)prompt$ pip install Flask gunicorn (<name>)prompt$ deactivate prompt% Heroku Toolbox Once you make a Heroku account, install the Heroku toolbox. Then login: prompt$ heroku login Enter your Heroku credentials. Email: [email protected] Password: Authentication successful. Procfile I Use a Procfile to declare what command should be executed to start server I One might hope to write \web: python hello.py" I Instead: \web: gunicorn hello:app" Flask I Heroku recognizes Python applications by the existence of a requirements.txt file I Used to specify the required external modules I prompt% pip freeze > requirements.txt I Now anyone can install our dependencies by using pip install -r requirements.txt Git Create a new git repository and save our changes: prompt$ git init prompt$ echo venv > .gitignore prompt$ git add . prompt$ git commit -m"init" Deploy Push the application's repository to Heroku: prompt% heroku create Creating stormy-sands-4409... http://stormy-sands-4409.herokuapp.com/ Git remote heroku added prompt% git push heroku master Concurrency I A process is a running program I Heavy weight I Separate address spaces I Interact through inter-process communication mechanisms I A process can have multiple threads which allow it to do several things at once I Light weight I Share the same address space I Can interact via shared memory Threads I Each thread has its own local state I All threads share the same global state I In Python, support is provided by the thread (lower level) and threading (higher level) modules I Python threads are pre-emptive { may be interrupted at arbitrary times Threading Module I Create an instance of a Thread object I Provide a callable object, i.e. function or method I What does this mean? I Magic method __call__ is defined! I Provide a tuple of arguments I To start a thread object executing, invoke start() Threads >>> from threading import Thread >>> def add(x,y): ... print x+y ... >>> thread = Thread(target=add, args=(5,6)) >>> thread.start() 11 Ok, but one thread isn't very interesting ... let's take a look at basic_threads.py. Threads I How to explain the behavior we just saw? I The \main" thread (our top-level program) finished before the other threads I We should use join() { when called on a thread object T, this will cause the calling thread (typically \main") to block until T finishes Practical Example I When to use threads in the \real world"? I One use case: HTTP requests! I web.py Problems with Threads I What happens when a thread gets interrupted in the middle of modifying shared memory? I threads.py I The line var[0] += 1 does not execute atomically I Thread first has to read var[0] I Then compute var[0] + 1 I Then set var[0] to what it just computed I The thread could be interrupted between any of those actions, and var[0] could be changed without it knowing Locks I In order to use shared state, we need to prevent threads from stepping all over each other I Mutual exclusion refers to making sure that only some number of threads (often just 1) are doing something at the same time I Multiple ways to do this in Python { we will use the Lock class Locks lock = Lock() def inc_var(): lock.acquire() var[0] += 1 lock.release() Locks I Can use with statements with locks! I Then acquire() and release() get called automatically lock = Lock() def inc_var(): with lock: var[0] += 1 Dining Philosophers I Between each pair of philosophers is a single chopstick I Philosophers alternate between thinking and eating I Before eating, first take left chopstick, then take right I What if every philosopher takes left chopstick at the same time? Global Interpreter Lock (GIL) The Unwritten Rules of Python: 1. You do not talk about the GIL. 2. You do NOT talk about the GIL. 3. Don't even mention the GIL. No, seriously. { David Beazley Global Interpreter Lock (GIL) I Only one thread runs in the interpreter at once I Simplies many low-level details (memory management, callouts to C extensions, etc.) I But then why did we see a speedup? I When a python thread does something IO related (reading or writing a file, sending a network request, waiting for a response, etc.), it releases the lock.