There are multi-process, multi-thread, and non-blocking methods to execute another process before one process is completed. A process is a processing unit that has its own memory such as virtual memory, and memory is not shared between processes. Since a thread is a processing unit within a process, threads in the same process share memory. Non-blocking can respond to multiple requests with one thread.
There are two communication methods for socket communication, "blocking communication" and "non-blocking communication". Blocking is a communication method that waits for the completion of transmission / reception and then starts other processing, so it is often used when performing sequential processing. On the other hand, non-blocking is a communication method that can start other processing even if the communication is not completed, so it is often used when performing asynchronous processing.
From Python 3.4, a module called ʻasyncio has been added to the standard library to enable non-blocking processing ([Reference](http://qiita.com/icoxfog417/items/07cbf5110ca82629aca0)). About ʻasyncio
, it is very easy to understand and is organized in here, and many samples are posted.
Starting with ʻuWSGI 1.9, non-blocking mode is supported as described in [here](http://uwsgi-docs.readthedocs.io/en/latest/Async.html). Also, since 2.0.4, ʻasyncio
is supported (Reference).
Below, execute in the environment where Python 3.5 is installed.
greenlet
$ pip3 install greenlet
with ʻasyncio
support
First, find the directory where greenlet
is installed.$ find / -name greenlet -type d
In the environment of Mac OS
, the result was /Users/xxx/.pyenv/versions/3.5.0/include/python3.5m/greenlet
, so execute the following.
$ CFLAGS="-I/Users/xxx/.pyenv/versions/3.5.0/include/python3.5m" UWSGI_PROFILE="asyncio" pip3 install uwsgi
Perform simple operation verification with blocking and non-blocking. In the sample code, the file is read, but the following is used for the file.
numbers.txt
zero
one
two
three
four
five
Also, in the verification, ʻApache Bench` was used, but when using local, [here](https://stackoverflow.com/questions/7938869/ab-is-erroring-out-with-apr-socket- It is necessary to be careful as described in recv-connection-refused-61), and this time it is executed with the following command.
$ ab -n 10000 -c 100 http://127.0.0.1:9090/
with
pip3 install uwsgi. If you have already installed in non-blocking mode, uninstall ʻuWSGI
once below.$ pip3 unistall uwsgi
Log the contents of the read file.
def application(environ, start_response):
def my_generator(name):
with open(name) as lines:
yield from lines
g = my_generator("numbers.txt")
for k, v in enumerate(g):
print("%s:%s" % (k, v), end="")
start_response('200 OK', [('Content-Type','text/html')])
return [b"Hello World"]
Execute with the number of processes and threads specified as 1.
$ uwsgi --http-socket :9090 --processes 1 --threads 1 --logto uwsgi.log --wsgi-file webapp.py
ʻApache Bench` results (partial only).
Requests per second: 2694.06 [#/sec](mean)
Time per request: 37.119 [ms](mean)
Time per request: 0.371 [ms](mean, across all concurrent requests)
Transfer rate: 144.70 [Kbytes/sec] received
Log output of the contents of the file read asynchronously after 5 seconds.
import asyncio
@asyncio.coroutine
def my_generator(name):
with open(name) as lines:
yield from lines
def read():
g = my_generator("numbers.txt")
for k, v in enumerate(g):
print("%s:%s" % (k, v), end="")
def application(environ, start_response):
asyncio.get_event_loop().call_later(5, read)
start_response('200 OK', [('Content-Type','text/html')])
return [b"Hello World"]
Execute by specifying 1 for the number of processes and threads as in the case of blocking.
$ uwsgi --asyncio 2 --http-socket :9090 --greenlet --processes 1 --threads 1 --logto uwsgi.log --wsgi-file webapp.py
Here, 2
is specified for --asyncio
, but when it is set to 1
, the following is displayed and it does not start.
the greenlet suspend engine requires async mode
Looking into ʻasync mode`, as described below, there is a memory structure called a core that stores data for each request (defined as a core because the concept of threads is confusing).
Technically, cores are simple memory structures holding request’s data, but to give the user
the illusion of a multithreaded system we use that term.
These cores need to be switched, and it's probably only two or more.
Each core can manage a single request, so the more core you spawn, more requests you will be
able to manage (and more memory you will use). The job of the suspend/resume engines is to stop
the current request management, move to another core, and eventually come back to the old one
(and so on).
Compared to blocking, this sample is slightly slower in terms of performance. It seems better to compare with other samples.
equests per second: 2336.16 [#/sec](mean)
Time per request: 42.805 [ms](mean)
Time per request: 0.428 [ms](mean, across all concurrent requests)
Transfer rate: 125.48 [Kbytes/sec] received
Since I am using 2 cores in non-blocking mode, I reinstalled ʻuWSGI` and checked the number of threads with 2. The result is below. It may be a Mac environment problem.
Requests per second: 2691.94 [#/sec](mean)
Time per request: 37.148 [ms](mean)
Time per request: 0.371 [ms](mean, across all concurrent requests)
Transfer rate: 144.59 [Kbytes/sec] received
I would like to study ʻasyncio a little more, introduce a framework such as
Flask`, and continue verification that also serves as research under certain conditions such as using Nginx. Also, try other languages such as comparing with Golang.
Here says ʻIf you are in doubt, do not use async mode.`, but it is still at a practical level. Is it just not reached? Or is there a way to do it? Let's dig deeper.
Recommended Posts