
Welcome!
Everything is fine.
Requests, Threads and Flask¶
Plan¶
- Announcements
- Discuss Ajax assignment
- Recap Requests
- Recap Threads
- Quiz Questions
- Breakout session activities (Ajax)
Announcements¶
- I would like to meet with entire teams on Wednesdays; please attend
Requests¶
The client might not just be a browser. Most of the time it will
be, but it might not. The requests
module allows us to use a Python
program to do so. You will not need to write such a module in this
course. I'm teaching it because I hope:
- It will clarify what's going on with the Ajax assignment, by allow you to focus on the back end.
- It will broaden your understanding of web database applications.
- It will help you in internships, research projects, and the like.
The essentials are pretty easy:
The requests module allows us
- to make GET requests
- to find out the response status code
- to get the returned web page
- to get the data in JSON format
- to make POST requests
- to use sessions by saving a cookie and sending it in a later request
Examples:
resp = requests.get(url)
print(resp.status_code)
page = resp.text
data = resp.json()
resp = requests.post(url, data=payload_dictionary)
session = resp.cookies
resp = requests.get(url, cookies=session)
I forgot to send a quiz on requests; what are your questions?
Demo from last week. Here's the server:
cd ~/cs304/likes/
source ~/cs304/venv/bin/activate
python app.py
And the client
cd ~/cs304/likes/
source ~/cs304/venv/bin/activate
python req.py
Threads¶
What are the important points for today?
- Threading is an important concept and strategy
- Flask code has to be thread-safe, also called re-entrant.
- Local variables and parameters are always thread-safe.
- Instance variables, class variables, and global variables may not be thread-safe (but they might be).
- Some uses of shared variables can be thread-safe; others require locks
- Nevertheless, threads and shared variables can be useful.
Thread Concepts¶
- Threads are (relatively) cheap to create.
- Threads share code and heap, but have their own stack.
- Inter-thread communication is easy via shared variables and data structures. This can be a big advantage.
- However, modifications of shared data structures might need to be atomic, requiring locks or other synchronization
A multi-threaded Flask app:
So, our two threads are sharing the code and heap from our Flask app.
Race to 50¶
Here's the race to 50 and race to 50 fixed examples:
python raceto50.py
python raceto50fixed.py
diff raceto50.py raceto50fixed.py
Practical Project Implications¶
- Avoid global variables. Global constants are okay.
- If you need a global variable, make sure your code is thread-safe.
- Make sure every request uses a separate connection
- However, each request can share a single connection
- Bask in the knowledge that you're writing multi-threaded code. 😁
You probably won't need locking. But you should know about it just in case.
Questions¶
I'll answer your quiz questions.
Configuring Apache¶
FYI, here's how Apache can be configured. This is pretty standard.
# worker MPM
# StartServers: initial number of server processes to start
# MaxClients: maximum number of simultaneous client connections
# MinSpareThreads: minimum number of worker threads which are kept spare
# MaxSpareThreads: maximum number of worker threads which are kept spare
# ThreadsPerChild: constant number of worker threads in each server process
# MaxRequestsPerChild: maximum number of requests a server process serves
<IfModule worker.c>
StartServers 4
MaxClients 300
MinSpareThreads 25
MaxSpareThreads 75
ThreadsPerChild 25
MaxRequestsPerChild 0
</IfModule>
Activity¶
- Work on what you need to work on
Summary¶
- Threads are cool and powerful, but you have to be careful.
- Don't put stuff in a shared variable that belongs just to one request
- lock or synchronize access to shared data.
- Each request should have its own database connection, to avoid getting wires crossed.
Group Work¶
- I'll display the results/charts
- While things aren't perfect (they never are), they're mostly pretty good.
- Lookup/CRUD biggest issue is finding time. I tried to pair people that had time in common, but ...
- Project work is mostly going well
- Advice
- Remember that your team is counting on you.
- Ask for help (from teammates, from me) if you need it.
- Communicate
- If you are frustrated with a teammate, you can talk to me. Don't suffer in silence.
Polling Example¶
I have a cool activity in which you would build a "poll" or "voting" app that kept the counters in memory instead of needing a database at all. But access to those counters probably ought to be synchronized.
The activity is in pub/downloads/threads
:
cd ~/cs304/
source ~/cs304/venv/bin/activate
cp -r ~cs304/pub/downloads/threads/ threads
cd threads/
I'll set up a SSH tunnel, run the app and demo:
python voteLock.py
Actually, I have an improved version as vl2.py
:
python vl2.py
We'll vote on a simple resolution.
Here's the crucial part of the old code:
@app.route('/vote/', methods=["GET", "POST"])
def vote():
global resolutionText, numYes, numNo
text = resolutionText
state = 'none' if (text is None) else 'voting'
# result is only used when voting stops
result = ''
if request.method == 'POST':
try:
action = request.form.get('submit')
if action == START:
voteLock.acquire()
if resolutionText is None:
resolutionText = request.form.get('resolution')
text = resolutionText
numYes = 0
numNo = 0
state = 'voting'
msg = ('started resolution: '+text)
else:
msg = ('''error: can't start resolution;
one is in progress: '''
+resolutionText)
voteLock.release()
flash(msg)
elif action == AYE or action == NAY:
voteLock.acquire()
if resolutionText is None:
text = ''
msg = ('no vote is in progress')
else:
text = resolutionText
if action == AYE:
msg = ('you voted YES on '+text)
numYes += 1
else:
msg = ('you voted NO on '+text)
numNo += 1
voteLock.release()
flash(msg)
elif action == STOP:
voteLock.acquire()
text = resolutionText # copy the text
if resolutionText is None:
# someone else ended the vote
tmpl = '''vote already ended;
motion unknown:
{yeas} yes votes
and {nays} no votes'''
elif numYes > numNo:
tmpl = '{motion} passes: {yeas} > {nays}'
elif numNo > numYes:
tmpl = '{motion} fails: {nays} > {yeas}'
else:
tmpl = '''{motion} tied! {nays} == {yeas}.
Broward County begins recount ....'''
resolutionText = None
voteLock.release()
state = 'done'
result = tmpl.format(motion=text,
yeas=numYes,
nays=numNo)
flash(result)
else:
So, if we know about Locks and such, we can have apps that don't need databases and can keep data in memory!