Tornado timeout block all services - python

I create a simple test app to check timeout in tornado
import tornado.ioloop
import tornado.web
class LoopHandler(tornado.web.RequestHandler):
def get(self):
while (True):
print ("in loop")
self.write("Loop, Handler")
class MainHandler(tornado.web.RequestHandler):
def get(self):
self.write("Hello, world")
def make_app():
return tornado.web.Application([
(r"/", MainHandler),
(r"/loop", LoopHandler),
])
if __name__ == "__main__":
app = make_app()
app.listen(8888)
tornado.ioloop.IOLoop.current().start()
Then I call http://localhost:8888/loop the endpoint never response because the infinite loop the problems is that http://localhost:8888/ not responding either. the question is why this happened and how can solve this?
EDIT
Update code that solve the problemn
import tornado.ioloop
import tornado.web
#unblock
class LoopHandler(tornado.web.RequestHandler):
def get(self):
while (True):
print ("in loop")
return "Loop, Handler"
#unblock
class MainHandler(tornado.web.RequestHandler):
def get(self):
return "Hello, world"
def make_app():
return tornado.web.Application([
(r"/", MainHandler),
(r"/loop", LoopHandler),
])
if __name__ == "__main__":
app = make_app()
app.listen(8888)
tornado.ioloop.IOLoop.current().start()
#unblock.py
import tornado.web
import tornado.ioloop
from concurrent.futures import ThreadPoolExecutor
from functools import partial, wraps
EXECUTOR = ThreadPoolExecutor(max_workers=4)
def unblock(f):
#tornado.web.asynchronous
#wraps(f)
def wrapper(*args, **kwargs):
self = args[0]
def callback(future):
self.write(future.result())
self.finish()
EXECUTOR.submit(
partial(f, *args, **kwargs)
).add_done_callback(
lambda future: tornado.ioloop.IOLoop.instance().add_callback(
partial(callback, future)))
return wrapper

These are basics of async programming. To point you in the right direction take a look at the reactor pattern and especially at the event loop.
The reactor pattern is one implementation technique of event-driven
architecture. In simple terms, it uses a single threaded event loop
blocking on resource-emitting events and dispatches them to
corresponding handlers and callbacks.
Both functions LoopHandler and MainHandler are processed in the same event loop, therefore MainHandler gets queued but never executed since the event loop is busy executing LoopHandler.
One of the challenges (at least for me) in async programming is to be careful about blocking calls like database operations with for example SQLAlchemy, file operations, expensive calculations, etc. There are some interesting approaches using thread pools to solve this but you won't need them to get you started.
Ah, and in case you stumbling over the first sentence of the wiki article I have linked take a look here to understand the difference between parallel and concurrent. It helped me a lot.

Related

python tornado - how to return real-time data

I am using the tornado library in python. I have a queue where data gets added in. I have to keep connection open so that when client requests I send out items from queue. Here is a simple implementation of mine. The problem I face is when I add new elements to queue, I don't see it being it returned. Infact, I don't see any code executed below IOLoop.current().start() line.
from tornado.ioloop import IOLoop
from tornado.web import RequestHandler, Application, url,asynchronous
from Queue import Queue
import json
q=Queue()
q.put("one")
q.put("two")
class HelloHandler(RequestHandler):
def get(self):
data=q.get()
self.write(data)
def make_app():
return Application([
url(r"/", HelloHandler),
])
def main():
app = make_app()
app.listen(8888)
IOLoop.current().start() # stops here
time.sleep(2)
q.put("three")
print q
if __name__=='__main__':
main()
first time on this :
http://localhost:8888/
returns "one"
second time on this:
http://localhost:8888/
return "two"
Third time on this"
http://localhost:8888/
blocking
The problem you have is that calling IOLoop.current().start() transfers control to Tornado. It loops until IOLoop.stop() is called.
If you need to do something after the IOLoop has started, then you can use one of the callbacks. For example, here is your code modified to use IOLoop.call_later(). You could also use IOLoop.add_timeout() if you are using an earlier (<4.0) version of Tornado.
from tornado.ioloop import IOLoop
from tornado.web import RequestHandler, Application, url,asynchronous
from Queue import Queue
import json
q=Queue()
q.put("one")
q.put("two")
class HelloHandler(RequestHandler):
def get(self):
data=q.get()
self.write(data)
def make_app():
return Application([
url(r"/", HelloHandler),
])
def main():
app = make_app()
app.listen(8888)
IOLoop.current().call_later(2, q.put, "three")
IOLoop.current().start()
if __name__=='__main__':
main()

Why tornado doesn't go concurrently?

The following is my test code. I am using Python2.7, with futures installed using:
pip install futures
The following is my demo code:
import tornado.ioloop
import tornado.web
from tornado.gen import coroutine, Task
from tornado.concurrent import Future
import time
class MainHandler(tornado.web.RequestHandler):
#coroutine
def get(self):
print "in"
res = yield Task(self._work)
self.write(res)
def _work(self, callback):
time.sleep(10)
callback("hello world!")
if __name__ == "__main__":
application = tornado.web.Application([
(r"/", MainHandler),
])
application.listen(8888)
tornado.ioloop.IOLoop.instance().start()
This code should go concurrently, shouldn't it? However, it just doesn't.
I tested with Firefox and IE. I think I made some mistakes. It would be nice for you to point out my error.
only one request at a time(http://localhost:8888/:
import tornado.ioloop
import tornado.web
from tornado.gen import coroutine, Return, Task
from tornado.process import Subprocess
from tornado.concurrent import Future
from threading import Thread
import time
#coroutine
def async_sleep(timeout):
""" Sleep without blocking the IOLoop. """
yield Task(tornado.ioloop.IOLoop.instance().add_timeout, time.time() + timeout)
class MainHandler(tornado.web.RequestHandler):
#coroutine
def get(self):
print "in"
res = yield self._work()
self.write(res)
#coroutine
def _work(self):
yield async_sleep(5)
raise Return("hello world!")
if __name__ == "__main__":
application = tornado.web.Application([
(r"/", MainHandler),
])
application.listen(8888)
ioloop=tornado.ioloop.IOLoop.instance()
Thread(target=ioloop.start).start()
Since you indicated in the comments you want to run a subprocess via tornado, here's an example illustrating how to do that. I also fixed a logic error where you were creating a Task when calling _work, which wasn't going to work the way you intended:
import tornado.ioloop
import tornado.web
from tornado.gen import coroutine, Return
from tornado.process import Subprocess
from tornado.concurrent import Future
class MainHandler(tornado.web.RequestHandler):
#coroutine
def get(self):
print "in"
res = yield self._work()
self.write(res)
#coroutine
def _work(self):
p = Subprocess(['sleep', '10'])
f = Future()
p.set_exit_callback(f.set_result)
yield f
raise Return("hello world!")
if __name__ == "__main__":
application = tornado.web.Application([
(r"/", MainHandler),
])
application.listen(8888)
tornado.ioloop.IOLoop.instance().start()
As you can see, I made _work a coroutine, and then used tornado's built-in Subprocess class to execute a command. I created a Future object, and instructed the Subprocess to call Future.set_result(return_code_of_subprocess) when it completed. Then I called yield on the Future instance. That makes the code wait until the subprocess completes, without blocking the I/O loop.
The time.sleep in your code is a blocking method.
You need tornado.gen.sleep(a non-blocking method, new in tornado 4.1) here to instead.

Does twisted, cyclone or tornado do SMP multicore out of the box

I'd like to use any one of the 3 mentioned non-blocking servers on an AWS Linux server with 8 cores. It's not clear in any of the documentation whether SMP is implemented under the covers in the respective helloworld or any other examples.
For example, this cyclone helloworld mentions nothing about cores or SMP or threads per core.
import cyclone.web
class MainHandler(cyclone.web.RequestHandler):
def get(self):
self.write("Hello, world")
class Application(cyclone.web.Application):
def __init__(self):
cyclone.web.Application.__init__(self, [(r"/", MainHandler)],
xheaders=False)
Or this Twisted one:
from twisted.web import server, resource
from twisted.internet import reactor
class HelloResource(resource.Resource):
isLeaf = True
numberRequests = 0
def render_GET(self, request):
self.numberRequests += 1
request.setHeader("content-type", "text/plain")
return "I am request #" + str(self.numberRequests) + "\n"
reactor.listenTCP(8080, server.Site(HelloResource()))
reactor.run()
Or tornado...
import tornado.ioloop
import tornado.web
class MainHandler(tornado.web.RequestHandler):
def get(self):
self.write("Hello, world")
application = tornado.web.Application([
(r"/", MainHandler),
])
if __name__ == "__main__":
application.listen(8888)
tornado.ioloop.IOLoop.instance().start()
In fact, its difficult to determine whether those are non-blocking or not.
Tornado's HTTPServer supports a multi-process mode, using the bind(port) and start(num_procs) methods.
http://www.tornadoweb.org/en/stable/tcpserver.html#tornado.tcpserver.TCPServer.start
The CPython process uses a Global Interpreter Lock, and thus cannot take real advantage of multiple threads available in hardware if only a single Python process is running.

How to make tornado call itself asynchronously

Below is the simple hello world app. How can I have tornado call '/' in two threads with a 1 sec sleep. Thus the page will be called 2 times per second. I will need later to extend to a redis call but want to start simple now since I am new to this logic. I need to build a web page that is called repeatably asynchronously.
import tornado.ioloop
import tornado.web
class MainHandler(tornado.web.RequestHandler):
def get(self):
self.write("Hello, world")
application = tornado.web.Application([
(r"/", MainHandler),
])
if __name__ == "__main__":
application.listen(8880)
tornado.ioloop.IOLoop.instance().start()
If you are new to tornado, check out tornadogist. They have a lot of useful code snippets. Here is one adapted to your need:
from time import sleep
from tornado.httpserver import HTTPServer
from tornado.ioloop import IOLoop
from tornado.web import Application, asynchronous, RequestHandler
from multiprocessing.pool import ThreadPool
# 2 threads
_workers = ThreadPool(2)
def run_background(func, callback, args=(), kwds={}):
def _callback(result):
IOLoop.instance().add_callback(lambda: callback(result))
_workers.apply_async(func, args, kwds, _callback)
# blocking task like querying to MySQL
def blocking_task(n):
sleep(n)
return n
class MainHandler(RequestHandler):
#asynchronous
def get(self):
run_background(blocking_task, self.on_complete, (1,))
def on_complete(self, res):
self.write("Test {0}<br/>".format(res))
self.finish()
HTTPServer(Application([("/", MainHandler)],debug=True)).listen(8888)
IOLoop.instance().start()

python Tornado websockets how to send message every X seconds?

I am trying to cobble together a test which allows websockets clients to connect to a Tornado server and I want the Tornado server to send out a message to all clients every X seconds.
The reason I am doing this is because wbesockets connections are being silently dropped somewhere and I am wondering of periodic "pings" sent by the websockets server will maintain the connection.
I'm afraid it's a pretty noobish question and the code below is rather a mess. I just don't have my head wrapped around Tornado and scope enough to make it work.
import tornado.httpserver
import tornado.websocket
import tornado.ioloop
import tornado.web
import tornado.gen
import time
from tornado import gen
class WSHandler(tornado.websocket.WebSocketHandler):
def open(self):
print 'http://mailapp.crowdwave.com/girlthumb.jpg'
self.write_message("http://www.example.com/girlthumb.jpg")
def on_message(self, message):
print 'Incoming message:', message
self.write_message("http://www.example.com/girlthumb.jpg")
def on_close(self):
print 'Connection was closed...'
#gen.engine
def f():
yield gen.Task(tornado.ioloop.IOLoop.instance().add_timeout, time.time() + 8)
self.write_message("http://www.example.com/x.png")
print 'x'
#gen.engine
def g():
yield gen.Task(tornado.ioloop.IOLoop.instance().add_timeout, time.time() + 4)
self.write_message("http://www.example.com/y.jpg")
print 'y'
application = tornado.web.Application([
(r'/ws', WSHandler),
])
if __name__ == "__main__":
tornado.ioloop.IOLoop.instance().add_callback(f)
tornado.ioloop.IOLoop.instance().add_callback(g)
http_server = tornado.httpserver.HTTPServer(application)
http_server.listen(8888)
tornado.ioloop.IOLoop.instance().start()
Why don't you try write a scheduler inside it? :)
def schedule_func():
#DO SOMETHING#
#milliseconds
interval_ms = 15
main_loop = tornado.ioloop.IOLoop.instance()
sched = tornado.ioloop.PeriodicCallback(schedule_func,interval_ms, io_loop = main_loop)
#start your period timer
sched.start()
#start your loop
main_loop.start()
Found that the accepted answer for this is almost exactly what I want:
How to run functions outside websocket loop in python (tornado)
With a slight modification, the accepted answer at the above link continually sends out ping messages. Here is the mod:
Change:
def test(self):
self.write_message("scheduled!")
to:
def test(self):
self.write_message("scheduled!")
tornado.ioloop.IOLoop.instance().add_timeout(datetime.timedelta(seconds=5), self.test)

Categories

Resources