Synchronous v/s Asynchronous

后端 未结 1 1214
生来不讨喜
生来不讨喜 2020-12-23 15:20

I am trying to understand the basic example provided on the introduction page of tornado documentation. It has 2 blocks of code. The Synchronous one is fine for me, and I do

相关标签:
1条回答
  • 2020-12-23 16:11

    The idea of asynchronous calls is something that works pretty much the same in many web related programming... "stuff" (frameworks, servers, libraries...) Is not only a concept from the Tornado web server.

    The basic idea is:

    • On a s̲y̲n̲c̲h̲r̲o̲n̲o̲u̲s̲ request, you make the request and stop executing your program until you get a response from the HTTP server (or an error if the server can't be reached, or a timeout if the sever is taking way, way too long to reply) The interpreter is blocked until the request is completed (until you got a definitive answer of what happened with the request: did it go well? was there an error? a timeout?... ).
    • On a̲s̲y̲n̲c̲h̲r̲o̲n̲o̲u̲s̲ requests, you "launch" the request, and you kind of "forget about it", meaning: The interpreter continues executing the code after the request is made without waiting for the request to be completed.

      This seems... rather pointless, right? You send the request "to the void of space", and continue executing as usual? What happens when the server sends you its response? I made a request, and I wanna know what happened to it! Otherwise, I wouldn't have typed that in my code to begin with!!

      Well, here's where the callback comes in. You launch the request "to the void of space" BUT you provide a callback function so when the HTTP server on the other end sends you its response, that function is run with said response as the first argument.

    Let's see it with a en example.

    I've created a very simple Tornado server (using Python 2.7 and Tornado 4.2) with only one handler. On a GET, it takes 5 seconds to return. I've done that with a time.sleep, but in real life, it could be a very time consuming process (access a database, perform some calculations... who knows?...)

    Here's the server file (based on the example provided in the Tornado documentation):

    SampleServer.py:

    #!/usr/bin/env python2.7
    import time
    import tornado.ioloop
    import tornado.web
    
    
    class MainHandler(tornado.web.RequestHandler):
        def get(self):
            print "Someone is GET'ing me"
            time.sleep(5)
            self.write("Hello, world")
    
    application = tornado.web.Application([
            (r"/", MainHandler),
        ])
    
    if __name__ == "__main__":
        application.listen(8888)
        print "Starting sample server."
        tornado.ioloop.IOLoop.current().start()
    

    Open a terminal and run that code to have a server. You'll get a Tornado listening on port 8888 of your local machine.

    Now, let's create another script (which you'll have to run in another terminal) that GETs http://localhost:8888 in two ways: First synchronously and then asynchronously.

    SampleFetcher.py:

    #!/usr/bin/env python2.7
    import datetime
    import tornado.ioloop
    from tornado.httpclient import HTTPClient, AsyncHTTPClient
    
    
    def HUMAN_DT_NOW():
        return datetime.datetime.now().strftime("%Y/%m/%d %H:%M:%S")
    
    
    def synchronous_fetch(url):
        http_client = HTTPClient()
        start_dt = datetime.datetime.now()
        response = http_client.fetch(url)
        end_dt = datetime.datetime.now()
        print ("The synchronous fetch took %s seconds."
               % (end_dt - start_dt).total_seconds())
        print "(Sync) Server said: \"%s\"" % response.body
    
    
    def asynchronous_fetch(url):
        http_client = AsyncHTTPClient()
    
        def handle_response(response):
            print ""
            print "Yawwza... Finally!!!."
            print "The time now is %s" % HUMAN_DT_NOW()
            print "(Async) Server said: \"%s\"" % response.body
        print "Gonna launch a 'fetch' to the universe at %s..." % HUMAN_DT_NOW()
        http_client.fetch(url, callback=handle_response)
    
    if __name__ == "__main__":
        print " ------ Synchronous ------ "
        print ("Starting synchronous fetch at %s."
               " The program will block for about 5 secs." % HUMAN_DT_NOW())
        synchronous_fetch('http://localhost:8888')
        print "Pfew! That was a lot of wait time!!. I got bored watching my terminal"
        print ""
        print "Aight, let's see what Asynchronous can do"
        print " ------ Asynchronous ------ "
        asynchronous_fetch('http://localhost:8888')
        print "You're gonna see this line before the \"Yawwza...\" one"
        print "This one too. Now is %s" % HUMAN_DT_NOW()
        # The IOLoop below is required to prevent the script from closing ahead
        # of time, but allowing Asynchronous interactions
        tornado.ioloop.IOLoop.current().start()
    

    This will output:

    Starting synchronous fetch at 2015/07/04 13:25:47. The program will block for about 5 secs.
    The synchronous fetch took 5.009597 seconds.
    (Sync) Server said: "Hello, world"
    Pfew! That was a lot of wait time!!. I got bored watching my terminal
    
    Aight, let's see what Asynchronous can do
     ------ Asynchronous ------ 
    Gonna launch a 'fetch' to the universe at 2015/07/04 13:25:52...
    You're gonna see this line before the "Yawwza..." one
    This one too. Now is 2015/07/04 13:25:52
    
    Yawwza... Finally!!!.
    The time now is 2015/07/04 13:25:57
    (Async) Server said: "Hello, world"
    

    Let's focus on the asynchronous part, here:

     ------ Asynchronous ------ 
    Gonna launch a 'fetch' to the universe at 2015/07/04 13:25:52...
    You're gonna see this line before the "Yawwza..." one
    This one too. Now is 2015/07/04 13:25:52
    
    Yawwza... Finally!!!.
    The time now is 2015/07/04 13:25:57
    (Async) Server said: "Hello, world"
    

    If you see, the script made the asynchronous_fetch at 13:25:52, but immediately (in the same second), the interpreter continued executing, and run the next statements after the request was made (the lines that print You're gonna see this line before the "Yawwza..." one and This one too. Now is 2015/07/04 13:25:52 ).

    Then, around 5 seconds later, the server responded, and the callback function (which was handle_response) was executed, and that's when you see

    Yawwza... Finally!!!.
    The time now is 2015/07/04 13:25:57
    

    I hope this helped understanding the idea a bit. It's a very useful concept, and it doesn't only apply to Tornado.

    Feel free to play with the two sample scripts provided, change stuff, increase the times it gets for the server to reply...

    Further recommended reading:

    • What is the Asynchronous Web, and How is it Revolutionary?
    • Asynchronous vs. synchronous calls
    • Asynchronous vs synchronous execution, what does it really mean?
    • Tornado's Futures
    0 讨论(0)
提交回复
热议问题