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
Related
Using python/tornado I wanted to set up a little "trampoline" server that allows two devices to communicate with each other in a RESTish manner. There's probably vastly superior/simpler "off the shelf" ways to do this. I'd welcome those suggestions, but I still feel it would be educational to figure out how to do my own using tornado.
Basically, the idea was that I would have the device in the role of server doing a longpoll with a GET. The client device would POST to the server, at which point the POST body would be transferred as the response of the blocked GET. Before the POST responded, it would block. The server side then does a PUT with the response, which is transferred to the blocked POST and return to the device. I thought maybe I could do this with tornado.queues. But that appears to not have worked out. My code:
import tornado
import tornado.web
import tornado.httpserver
import tornado.queues
ToServerQueue = tornado.queues.Queue()
ToClientQueue = tornado.queues.Queue()
class Query(tornado.web.RequestHandler):
def get(self):
toServer = ToServerQueue.get()
self.write(toServer)
def post(self):
toServer = self.request.body
ToServerQueue.put(toServer)
toClient = ToClientQueue.get()
self.write(toClient)
def put(self):
ToClientQueue.put(self.request.body)
self.write(bytes())
services = tornado.web.Application([(r'/query', Query)], debug=True)
services.listen(49009)
tornado.ioloop.IOLoop.instance().start()
Unfortunately, the ToServerQueue.get() does not actually block until the queue has an item, but rather returns a tornado.concurrent.Future. Which is not a legal value to pass to the self.write() call.
I guess my general question is twofold:
1) How can one HTTP verb invocation (e.g. get, put, post, etc) block and then be signaled by another HTTP verb invocation.
2) How can I share data from one invocation to another?
I've only really scratched the simple/straightforward use cases of making little REST servers with tornado. I wonder if the coroutine stuff is what I need, but haven't found a good tutorial/example of that to help me see the light, if that's indeed the way to go.
1) How can one HTTP verb invocation (e.g. get, put, post,u ne etc) block and then be signaled by another HTTP verb invocation.
2) How can I share data from one invocation to another?
The new RequestHandler object is created for every request. So you need some coordinator e.g. queues or locks with state object (in your case it would be re-implementing queue).
tornado.queues are queues for coroutines. Queue.get, Queue.put, Queue.join return Future objects, that need to be "resolved" - scheduled task done either with success or exception. To wait until future is resolved you should yielded it (just like in the doc examples of tornado.queues). The verbs method also need to be decorated with tornado.gen.coroutine.
import tornado.gen
class Query(tornado.web.RequestHandler):
#tornado.gen.coroutine
def get(self):
toServer = yield ToServerQueue.get()
self.write(toServer)
#tornado.gen.coroutine
def post(self):
toServer = self.request.body
yield ToServerQueue.put(toServer)
toClient = yield ToClientQueue.get()
self.write(toClient)
#tornado.gen.coroutine
def put(self):
yield ToClientQueue.put(self.request.body)
self.write(bytes())
The GET request will last (wait in non-blocking manner) until something will be available on the queue (or timeout that can be defined as Queue.get arg).
tornado.queues.Queue provides also get_nowait (there is put_nowait as well) that don't have to be yielded - returns immediately item from queue or throws exception.
I'm using coto boilerplate, which extends the dispatch() method to start the session store and save all sessions at the end of a request, following webapp2 doc:
class BaseHandler(webapp2.RequestHandler):
def dispatch(self):
# Get a session store for this request.
self.session_store = sessions.get_store(request=self.request)
try:
# Dispatch the request.
webapp2.RequestHandler.dispatch(self)
finally:
# Save all sessions.
self.session_store.save_sessions(self.response)
#webapp2.cached_property
def messages(self):
return self.session.get_flashes(key='_messages')
def add_message(self, message, level=None):
self.session.add_flash(message, level, key='_messages')
My problem is that flash messages get lost during a redirect_to.
This line resets self.session and looses the flash messages:
self.session_store = sessions.get_store(request=self.request)
Any idea how to fix this?
I, too, found that redirect_to loses flash messages on the way. My quick fix was to use
self.redirect(self.request.referer)
all is good ;)
This is an old question, but I would like to add an answer:
redirect_to() and redirect() do not completely halt code execution for the current request (see here and here for reference). It's mostly unnoticeable but the response for that request is still processed, even though the user has been redirected and is shown the new response. I was able to fix this by adding a return underneath the redirect.
self.add_message('Your changes have been saved.', 'info')
self.redirect_to('home')
return
redirect_to() and redirect() also support an abort parameter, which raises an exception to perform the redirect. I personally just use return instead.
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
Server.Transfer is sort of like a Redirect except instead of requesting the browser to do another page fetch, it triggers an internal request that makes the request handler "go to" another request handler.
Is there a Python equivalent to this in Google App Engine?
Edit: webapp2
With most Python frameworks the request handler is simply a function: I should imagine you can just import the actual handler function you want to use and pass it the parameters you received in the current handler function.
In Django (for example): you usually have a function that takes at least 1 parameter, the request object. You should be able to simply import the next handler and then return the result of executing it. Something like:
def actual_update_app_queue_settings(request):
return HttpResponse()
def update_app_queue_settings(request):
return actual_update_app_queue_settings(request):
For the framework you've mentioned, probably something like this:
class ProductHandler(webapp2.RequestHandler):
def get(self, product_id):
self.response.write('You requested product %r.' % product_id)
class ProductHandler2(webapp2.RequestHandler):
def get(self, product_id):
nph = ProductHandler()
nph.initialize(request, response)
nph.get(product_id)
I'm fudging that by looking at http://webapp-improved.appspot.com/guide/handlers.html: it looks reasonable. If you're using route annotations I'm honestly not sure what you do, but that might do it.
Usually, you just have to call the corresponding method.
For being more specific... Which flavour of AppEngine are you using? Java, Python, Go... Php?
If you are using java/servlet, then the "forward" is
protected void doGet(HttpServletRequest request, HttpServletResponse response){
request.getRequestDispatcher("/newurl").forward(request, response);
}
Since nobody provided a solution to this post plus the fact that I desperately need a workaround, here is my situation and some abstract solutions/ideas for debate.
My stack:
Tornado
Celery
MongoDB
Redis
RabbitMQ
My problem: Find a way for Tornado to dispatch a celery task ( solved ) and then asynchronously gather the result ( any ideas? ).
Scenario 1: (request/response hack plus webhook)
Tornado receives a (user)request, then saves in local memory (or in Redis) a { jobID : (user)request} to remember where to propagate the response, and fires a celery task with jobID
When celery completes the task, it performs a webhook at some url and tells tornado that this jobID has finished ( plus the results )
Tornado retrieves the (user)request and forwards a response to the (user)
Can this happen? Does it have any logic?
Scenario 2: (tornado plus long-polling)
Tornado dispatches the celery task and returns some primary json data to the client (jQuery)
jQuery does some long-polling upon receipt of the primary json, say, every x microseconds, and tornado replies according to some database flag. When the celery task completes, this database flag is set to True, then jQuery "loop" is finished.
Is this efficient?
Any other ideas/schemas?
My solution involves polling from tornado to celery:
class CeleryHandler(tornado.web.RequestHandlerr):
#tornado.web.asynchronous
def get(self):
task = yourCeleryTask.delay(**kwargs)
def check_celery_task():
if task.ready():
self.write({'success':True} )
self.set_header("Content-Type", "application/json")
self.finish()
else:
tornado.ioloop.IOLoop.instance().add_timeout(datetime.timedelta(0.00001), check_celery_task)
tornado.ioloop.IOLoop.instance().add_timeout(datetime.timedelta(0.00001), check_celery_task)
Here is post about it.
Here is our solution to the problem. Since we look for result in several handlers in our application we made the celery lookup a mixin class.
This also makes code more readable with the tornado.gen pattern.
from functools import partial
class CeleryResultMixin(object):
"""
Adds a callback function which could wait for the result asynchronously
"""
def wait_for_result(self, task, callback):
if task.ready():
callback(task.result)
else:
# TODO: Is this going to be too demanding on the result backend ?
# Probably there should be a timeout before each add_callback
tornado.ioloop.IOLoop.instance().add_callback(
partial(self.wait_for_result, task, callback)
)
class ARemoteTaskHandler(CeleryResultMixin, tornado.web.RequestHandler):
"""Execute a task asynchronously over a celery worker.
Wait for the result without blocking
When the result is available send it back
"""
#tornado.web.asynchronous
#tornado.web.authenticated
#tornado.gen.engine
def post(self):
"""Test the provided Magento connection
"""
task = expensive_task.delay(
self.get_argument('somearg'),
)
result = yield tornado.gen.Task(self.wait_for_result, task)
self.write({
'success': True,
'result': result.some_value
})
self.finish()
I stumbled upon this question and hitting the results backend repeatedly did not look optimal to me. So I implemented a Mixin similar to your Scenario 1 using Unix Sockets.
It notifies Tornado as soon as the task finishes (to be accurate, as soon as next task in chain runs) and only hits results backend once. Here is the link.
Now, https://github.com/mher/tornado-celery comes to rescue...
class GenAsyncHandler(web.RequestHandler):
#asynchronous
#gen.coroutine
def get(self):
response = yield gen.Task(tasks.sleep.apply_async, args=[3])
self.write(str(response.result))
self.finish()