2

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.

Alireza Mazochi
  • 897
  • 1
  • 15
  • 22
user3422533
  • 305
  • 1
  • 6
  • 11
  • When you say "they are executed sequentially": if you send two requests (quickly after one-another), does it take ~20s for both to return? – Pritam Baral Mar 15 '14 at 08:00
  • The problem is that `ThreadingMixIn` is sequencing your handler. Unfortunately a moderator deleted my answer, even though your question is not an exact duplicate. I will try posting again. – personal_cloud Sep 14 '17 at 07:07

4 Answers4

1

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

personal_cloud
  • 3,943
  • 3
  • 28
  • 38
  • That's crucial information that the `ThreadingMixin` makes handler sequential. Can you add some reference info on that? It's amazing to me that this core class would be so hobbled. It explains a lot with my issues on python web servers. – WestCoastProjects Apr 03 '20 at 12:47
  • @javadba. I (and evidently others too) have spent some time trying to understand `ThreadingMixIn`... but, in software, at some point, you need to know what the original authors were thinking, and they didn't document HTTP streaming application... To me it seems some features of `SocketServer` are over-abstracted and therefore too much work to set up properly. IMO we've spent enough time on that, and effectively, for the time being, the answer is to just wrap `BaseHTTPServer` in your own thread launcher as I [suggested](https://stackoverflow.com/a/46228009/5896591). – personal_cloud Apr 05 '20 at 21:15
0

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

Pritam Baral
  • 475
  • 2
  • 7
  • Thanks for your answer! But is there no other way to make a webserver in Python, which is able to response more then one request in same time. In the way I used in my example, each Handler-Object is running in a new thread. I hoped when they are running in two threads they can be execute the same time. (I hope you unserstand my english :| ) – user3422533 Mar 15 '14 at 07:44
  • Your code seems fine for multithreading *in python*, and is exactly how it should be done; I do think something is going wrong that this exhibits sequential behaviour. – Pritam Baral Mar 15 '14 at 07:49
0

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.

Liam M
  • 5,306
  • 4
  • 39
  • 55
  • 1
    Just because python is single-processing doesn't mean it can't have multiple asynchronous threads. A blocking I/O call will cause python to switch threads. The OP knows this and is right to use threading for that. – personal_cloud Sep 14 '17 at 07:23
0

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.

tnissi
  • 907
  • 1
  • 8
  • 12