Why asynchronous function in tornado is blocking? - python

Why incoming request are not being processed while another request is in the "waiting" state?
If you look at the code below, function "get" has a tornado task which is executed with "yield" keyword, which would mean "wait for a callback to be executed". In my code, the callback is never executed. If you run request second time, while first is on hold, the second request is not processed. If you run any other requests, they are being processed just fine.
So, my actions:
1. Start application
2. GET localhost:8080/
- Application is printing output "incoming call"
3. GET localhost:8080/anotherrequest
- Application is printing output "another request"
4. GET localhost:8080/
- Application is not printing any output while I'm expecting it to print "incoming call". Why?
So, why this piece of code gets blocking? Code sample is attached.
I was using tornado 2.1 and python 2.7 to run this sample.
Thank you
import tornado
import tornado.web
from tornado import gen
class AnotherHandler(tornado.web.RequestHandler):
#tornado.web.asynchronous
def get(self):
print 'another request'
self.finish()
class MainHandler(tornado.web.RequestHandler):
def printStuff(*args, **kwargs):
print 'incoming call'
#tornado.web.asynchronous
#tornado.gen.engine
def get(self):
result = yield tornado.gen.Task(self.printStuff);
application = tornado.web.Application([
(r"/", MainHandler),
(r"/anotherrequest", AnotherHandler)
])
if __name__ == "__main__":
application.listen(8080)
tornado.ioloop.IOLoop.instance().start()

Each new request to "localhost:8080/" will, in fact, cause your application to print "incoming call." However, requests to "localhost:8080/" will never finish. In order for the yield statement to be used, printStuff has to accept a callback and execute it. Also, an asynchronous get function must call self.finish:
class MainHandler(tornado.web.RequestHandler):
def printStuff(self, callback):
print 'incoming call'
callback()
#tornado.web.asynchronous
#tornado.gen.engine
def get(self):
result = yield tornado.gen.Task(self.printStuff)
self.finish()
It's easier to use Tornado's modern "coroutine" interface instead of gen.Task and gen.engine:
class MainHandler(tornado.web.RequestHandler):
#gen.coroutine
def printStuff(self):
print 'incoming call'
#gen.coroutine
def get(self):
result = yield self.printStuff()
self.finish()

Found the problem, it's actually happening when requests are made from the browser. With "curl" everything works as expected. Sorry for inconvenience.

Related

How to reach the aim of asynchronous request in the Tornado web service

Question: i set up a web service based on tonardo for my model. And as the request Concurrency increases, the web will easily turn down.
What i have tried: i have already tried to read some relevant code like the asynchronous framework in tornado, but i can't get the effective ideas.
The web service code as follows:
class mainhandler(tornado.web.RequestHandler):
def get(self):
self.write('hello')
def post(self):
in_data = json.loads(self.request.body.decode('utf-8'))
res = predict(in_data)
def set_default_headers(self):
self.set_header('content_type', 'application/json;charset=utf-8')
application = tornado.web.Application([(r'/', mainhandler)])
if __name__ == "__main__":
application.listen(port=5000)
tornado.ioloop.IOloop.current().start()
In Tornado, anything that is slow must be called in a coroutine with await. If predict does some I/O of its own, it should be made into a coroutine so it can do that I/O asynchronously. If it is pure computation, you should run it on a thread pool:
async def post(self):
in_data = json.loads(self.request.body.decode('utf-8'))
res = await IOLoop.current().run_in_executor(None, predict, in_data)

Twisted making a function non-blocking

I am using twisted for making an async webserver (to learn the idea behind even based asynchronous programming). consider this scenario , server when gets a GET request on some endpoint, ex http:localhost:8000/api/v1/calc_fact?num=1000 calculates the factorial of 1000 and return back the result. this part is easy to get. I am also aware of deferred API. how can i define my function calc_factorial() so that it return a deferred and the overall result is non-blocking.
How do I solve the problem?
I have done something similar.
In your resource you need to return a server.NOT_DONE_YET and add the calc_factorial deferred callback like this
def render_GET(self, request):
d = Deferred()
reactor.callLater(1, d.callback, None)
d.addCallback(self.calc_factorial, request)
d.addErrback(rror_handler, request)
return server.NOT_DONE_YET
Then inside the calc_factorial you write into the request:
def calc_factorial(self, request):
# something something
request.write("factorial calc done")
request.finish()
Once you write request finish it will trigger the NOT_DONE_YET

how to debug Tornado async operation

I am new to Tornado framework, and according to the link Asynchronous and non-Blocking I/O, I wrote some demo code as below. Unfortunately, the sync http client works, but async http client not. It looks like, the callback function that I passed to AsyncHTTPClient.fetch never has the chance to run. So my question is:
Why tornado's async API not work for me?
How should I debug this kind of problem? Set a break-point to my callback function is useless because it never has chance to run.
Any help is great appreciated. Below is my demo code:
from tornado.httpclient import AsyncHTTPClient
from tornado.httpclient import HTTPClient
import time
myUrl = 'a http url serving RESTful service'
def async_fetch(url, callback):
http_client = AsyncHTTPClient()
def handle_test(response):
callback(response.body)
http_client.fetch(url, handle_test)
def sync_fetch(url):
http_client = HTTPClient()
response = http_client.fetch(url)
return response.body
def printResponse(data):
print("response is:" + data)
def main():
sync_fetch(myUrl) #this works
async_fetch(myUrl, printResponse) #this not work
if __name__ == '__main__':
main()
print("begin of sleep!")
time.sleep(2)
print("end of sleep!")
You need to start the IOLoop, otherwise your asynchronous task never makes progress:
from tornado.ioloop import IOLoop
def printResponse(data):
print("response is:" + data)
IOLoop.current().stop()
def main():
sync_fetch(myUrl) #this works
async_fetch(myUrl, printResponse)
IOLoop.current().start()
In this example I stop the loop at the bottom of printResponse. In a real web server application you might never explicitly stop the loop.

Can't get raise Return to work when using coroutines

I've been experimenting with Python 2.7 and Tornado 3.2. I've been trying to get a simple coroutine example to work, but without much luck:
import tornado.web
from tornado.gen import coroutine
from tornado.httpclient import AsyncHTTPClient
from tornado.gen import Return
class MainHandler(tornado.web.RequestHandler):
# Tried with and without #asynchronous
#tornado.web.asynchronous
def get(self):
data = MainService().get_google_data()
self.write(data)
class MainService:
#coroutine
def get_google_data(self):
response = yield AsyncHTTPClient().fetch("http://www.google.com")
raise Return(value = 'hello')
I expected this would write out 'hello' when cURLing the URL. Instead, I get:
...
File "/vagrant/venv/lib/python2.7/site-packages/tornado/web.py", line 656, in write
raise TypeError("write() only accepts bytes, unicode, and dict objects")
TypeError: write() only accepts bytes, unicode, and dict objects
Apparently, a Future is being returned, but calling result() on the future throws another exception: DummyFuture does not support blocking for results
The Tornado docs say that in order to return a value from a coroutine, you raise a Return exception. Looking at the source code, that indeed seems to be what's expected. Yet, when I run it, it doesn't seem to work.
Appreciate any insights on this!
You need to yield the call to get_google_data():
class MainHandler(tornado.web.RequestHandler):
#coroutine
def get(self):
data = yield MainService().get_google_data()
self.write(data)
Tornado coroutines always return a Future. You wait for the result of that Future by calling yield on it. Without the yield, you just end up getting the Future back immediately, without waiting for the coroutine to complete. You should also use the #coroutine decorator on the get method, in addition to get_google_data. The #asynchronous decorator is generally used if you want to use callbacks, rather than coroutines.

Simple function and tornado gen.engine

I am making a web application using Python + Tornado. Wrote a simple handler for example:
class AdminHandler(BaseHandler):
#tornado.web.authenticated
#tornado.web.asynchronous
#gen.engine
def get(self):
response = yield gen.Task(self.acync_func_test, 'my')
print response
self.render('admin/index.html')
def acync_func_test(self, argument, callback):
for i in xrange(1,59999000):
i**2+2-12
callback(argument)
But function is not performed asynchronously. Other clients are waiting until the query is executed for the first. How to make non-blocking execution?
Update:
Added decorator '#gen.engine' to your async_func_test() function, but still blocked((
Add decorator '#gen.engine' to your async_func_test() function

Categories

Resources