user3422533
user3422533

Reputation: 337

Python HTTP-Server and Threading

I tried to create a HTTP-server in Python using Threading:

from socketserver import ThreadingMixIn
from http.server import HTTPServer, BaseHTTPRequestHandler
import time, threading

class ThreadingServer(ThreadingMixIn, HTTPServer):
    pass
class Handler(BaseHTTPRequestHandler):
    def do_GET(self):
        print("do")
        time.sleep(10)
        message =  threading.currentThread().getName()
        self.wfile.write(message)
        self.wfile.write('\n')

if __name__ == "__main__":
    httpd = ThreadingServer( (host, port), Handler)
    httpd.serve_forever()

The server works well, but if two request are same time, they are executed sequentially. So the second request not executed until the first is finished.

Upvotes: 2

Views: 10052

Answers (5)

MagMax
MagMax

Reputation: 2683

Just use http.server.ThreadingHTTPServer instead HTTPServer:

import time, threading
from http.server import ThreadingHTTPServer, BaseHTTPRequestHandler

class Handler(BaseHTTPRequestHandler):
    def do_GET(self):
        print("do")
        time.sleep(10)
        message =  threading.current_thread().name
        self.send_response(200)
        self.end_headers()
        self.wfile.write(message.encode())
        self.wfile.write(b'\n')

if __name__ == "__main__":
    host, port = '', 8001
    httpd = ThreadingHTTPServer( (host, port), Handler)
    httpd.serve_forever()

Note: I've changed some methods and transformed strings into bytes to avoid warnings and make it to work.

You can test it with:

for i in {1..10}; do curl localhost:8001/$i& ; done

Upvotes: 0

personal_cloud
personal_cloud

Reputation: 4484

That is absolutely right: ThreadingMixIn will make your entire handler sequential.

Instead, you need something like this:

import time, socket, threading

sock = socket.socket (socket.AF_INET, socket.SOCK_STREAM)
host = socket.gethostname()
port = 8000

sock.bind((host, port))
sock.listen(1)

HTTP = "HTTP/1.1 200 OK\nContent-Type: text/html; charset=UTF-8\n\n"

class Listener(threading.Thread):

    def __init__(self):
        threading.Thread.__init__(self)
        self.daemon = True # stop Python from biting ctrl-C
        self.start()

    def run(self):
        conn, addr = sock.accept()
        conn.send(HTTP)

        # serve up an infinite stream
        i = 0
        while True:
            conn.send("%i " % i)
            time.sleep(0.1)
            i += 1

[Listener() for i in range(100)]
time.sleep(9e9)

You can even use the above approach without writing your own HTTP server:

Python 2.7: streaming HTTP server supporting multiple connections on one port

Upvotes: 1

tnissi
tnissi

Reputation: 1005

I bumped into the same problem and found this oldish question. Turned out that for me the problem was that I was using Google Chrome, which seems to serialize the requests to the same URL. So the second request is not sent before the first one has had the response, and thus it seemed that the server was handling requests sequentially. Using curl -i <your URL here> worked much better.

Upvotes: 0

Liam M
Liam M

Reputation: 5432

Python is designed around a construct called the Global Interpreter Lock (GIL), which ensures that only one python instruction is run at any given time. This means that Python cannot run code in parallel, which explains why your code is being run in series. You can read more about the GIL here.

If you want to sidestep the GIL, then there is an option available to you: check out the multiprocessing package.

Upvotes: 0

Pritam Baral
Pritam Baral

Reputation: 475

Your code is written perfectly for multithreading in python. I see no reason why this would be sequential. You might wanna try random sleep intervals.

BTW: Python threads are not the same as OS threads. A normal python script runs in a single-process, single-threaded VM, as such only one python thread can be running at any time. (If you want to know more about this: look up GIL)

You can try using PyPy for true multithreading

Upvotes: 0

Related Questions