Chapter "Parallel Requests" has been updated.
If you want to read more, please like the book or follow the author ;-)
The following is an excerpt of the contents of the book.
Python reads the source code in order from the top, and when the processing of one line is completed, it executes the next line, and so on.
Therefore, with the current source code,
--Accept connections from clients (. accept ()
)
--Handle requests from clients (.handle_client ()
)
-End communication with the client (after handling exceptions if necessary) (.close ()
)
--Accept connections from clients (. accept ()
)
Be sure to follow this order.
In other words, ** "The next request will not be accepted until all the processing of one request is completed" ** It means that.
This becomes a big problem as the number of requests increases.
For example, suppose 10 requests come in at the same time.
At this point, the server starts processing the first request that comes first-come-first-served. And if this first request is a very time consuming process, the next nine requests will have to wait until the process is complete.
A common case is to throw a query with complex search criteria to a DB server and wait 30 seconds for a response. Overall performance will be much better if the machine handles the other 9 requests for 30 seconds waiting for a response from the DB.
In this way, while a certain program is performing a certain process, executing another program behind the scenes is called ** parallel processing ** or ** parallel processing **.
This parallel processing is implemented as a matter of course on a general Web server, so let's implement it as well.
Let me explain the difference between "parallel processing" and "parallel processing".
In general, processing multiple monogoto at the same time is called "parallel processing".
In the computer world, the CPU is responsible for all the actual processing, but one CPU can always do only one job at a time. Therefore, in a strict sense, "parallel processing" cannot be realized with one CPU.
However, from a human sense, CPU processing is so fast that
" If you do this job and become "waiting", switch to another job, and if you become "waiting" again, do another job. "
By switching the processes to be tackled one after another, it can be seen by the human eye as if they are doing tasks at the same time.
(Depending on the OS, the CPU switches jobs one after another in about 0.2 seconds or less)
In the old days, it was natural for one computer to have one CPU, so in the computer world, there was no particular problem in reading this "pseudo-parallel processing" simply as "parallel processing". However, around 2000, with the release / spread of dual-core CPUs, two or four CPUs (more accurately, CPU cores) came to be installed in one computer, and by operating these CPUs at the same time, "pseudo" It has become possible to perform "real parallel processing" instead of "parallel processing".
Therefore, programmers call "pseudo-parallel processing" "** parallel ** processing" in order to distinguish between "pseudo-parallel processing" and "true parallel processing", which were previously read as "parallel processing". It came to be.
It is extremely difficult for humans to distinguish between "parallel processing" and "parallel processing" on the surface, and there is almost no need to distinguish between the two in programming until machine performance tuning is required. Therefore, in this document, these are unified and referred to as "parallel processing" without any particular distinction.
It's a very complicated story about when parallel processing and parallel processing are used properly, and how they affect performance. I will omit it in this book. There is a great deal of information available on the internet on this topic, so if you are interested, check it out.
Here is the source code that has been improved to perform parallel processing. Please note that there are two files.
study/WebServer.py
https://github.com/bigen1925/introduction-to-web-application-with-python/blob/main/codes/chapter13/WebServer2.py
study/WorkerThread.py
https://github.com/bigen1925/introduction-to-web-application-with-python/blob/main/codes/chapter13/WebServer2.py
Common
Since the files are separated, the characters Server:
and Worker:
are output to the log showing the processing record, respectively.
Example)
print("=== Server:Start the server===")
print("=== Worker:End communication with the client===")
WebServer.py
#Create a thread to handle the client
thread = WorkerThread(client_socket)
#Run thread
thread.start()
Create a ** thread ** that processes the client that established the connection, and start processing the thread.
The processing that was done by the method .handle_client ()
until the last time and the exception handling before and after have all been moved to the processing in this thread.
A ** thread ** is a processing sequence that allows a computer to perform processing in parallel, and will be explained in detail later.
WorkerThread.py
class WorkerThread(Thread):
#Directory with executable files
BASE_DIR = os.path.dirname(os.path.abspath(__file__))
#Directory to put files for static distribution
DOCUMENT_ROOT = os.path.join(BASE_DIR, "static")
def __init__(self, client_socket: socket):
super().__init__()
self.client_socket = client_socket
def run(self) -> None:
"""
Receives a socket connected to the client as an argument
Process the request and send the response
"""
#Processing corresponding to the client...
Thread
is a class included in the threading
module, which is a built-in library of python, and is a base class for easily creating threads.
When using Thread
, create a class that inherits Thread
and override the .run ()
method.
An instance of this class creates a new thread by calling the .start ()
method and starts the processing of the .run ()
method.
The CPU cannot process programs executed in one thread in parallel, but programs executed in multiple threads can be processed in parallel.
Previously, the WebServer
class handled all client responses sequentially within a single thread.
However, from this time, although the WebServer
class can establish a connection with the client by using a thread, the response to the request content is processed in parallel by another thread.
This avoids the situation where one request is being processed for a long time and no other request is accepted.
Basically, the processing of different threads runs as separate programs, so even if an exception occurs in one thread, it will not be propagated to another thread.
For example, in the past, requests were processed in the same thread, so if an exception occurred during request processing, the main processing (request reception processing) would have ended unless exception handling was performed. From this time, requests are processed in separate threads, so even if an exception occurs while processing a request in a certain thread, if there is no exception handling, that thread will just terminate and the main thread will be affected. there is not.
At first glance, it may seem grateful, but it shouldn't continue in the event of an anomaly that affects the entire server.
Be sensitive to exception handling when using threads.
Also, if you branch threads more and more, the process will not be infinitely fast.
There is a limit to the amount that the CPU can process at the same time.
In a situation where most of the processes executed in parallel use a large amount of CPU, there are cases where the amount of CPU processing reaches the limit and parallel execution is delayed, and the performance does not improve or decreases.
Processing that causes the CPU performance to become a bottleneck and reaches the limit of processing speed is called CPU bound processing
.
For Web services that have a lot of CPU-bound processing, if you increase the number of threads unnecessarily, the amount of CPU processing may reach its limit and affect the execution speed of other programs. This is to prevent many web servers from setting limits on the number of threads and processes.
Be careful about how many thread branches your program does in the worst case.
In this book as well, you should originally set an upper limit on the number of threads that can be branched, and python provides a class called ThreadPoolExecutor
for that purpose.
If you are interested, please check it out.
Let's actually move it.
Recommended Posts