Continuously monitor a socket in Tornado Python framework - python

I am using Tornado and I want to monitor a socket continuously for notifications from a DB server. My application so far looks like this:
import functools
import tornado
import tornado.httpserver
from tornado.ioloop import IOLoop
class Application(tornado.web.Application):
def __init__(self):
handlers = [(r"/", MyHandler),]
super(Application, self).__init__(handlers)
fd = get_socket_file_descriptor()
callback = functools.partial(self.my_callback)
io_loop = IOLoop.current()
io_loop.add_handler(fd, callback, io_loop.READ)
def my_callback(self, fd, events):
# do something
pass
if __name__ == '__main__':
http_server = tornado.httpserver.HTTPServer(Application())
http_server.listen(8888)
tornado.ioloop.IOLoop.instance().start()
My problem is this as soon as there is activity on the socket, the callback is called infinitely. I want the IOLoop to handle the call back and go back to listening on to the file descriptor.

The IOLoop will call your handler repeatedly as long as there is data to be read. Your callback must consume all the data in the socket to allow it to become idle again.

Your callback must call io_loop.remove_handler(fd). But consider connecting an IOStream to the file descriptor for a much more convenient and higher-level interface. You'd attach a callback to IOStream.read_bytes:
http://tornado.readthedocs.org/en/latest/iostream.html#tornado.iostream.BaseIOStream.read_bytes
Consider passing partial=True or a streaming_callback if you don't know what message length to expect, or use read_bytes with a length argument, or read_until_regex if you have some knowledge of when the end of the message will be.

Related

How to make an HTTP server listen on multiple ports? [duplicate]

I'm writing a small web server in Python, using BaseHTTPServer and a custom subclass of BaseHTTPServer.BaseHTTPRequestHandler. Is it possible to make this listen on more than one port?
What I'm doing now:
class MyRequestHandler(BaseHTTPServer.BaseHTTPRequestHandler):
def doGET
[...]
class ThreadingHTTPServer(ThreadingMixIn, HTTPServer):
pass
server = ThreadingHTTPServer(('localhost', 80), MyRequestHandler)
server.serve_forever()
Sure; just start two different servers on two different ports in two different threads that each use the same handler. Here's a complete, working example that I just wrote and tested. If you run this code then you'll be able to get a Hello World webpage at both http://localhost:1111/ and http://localhost:2222/
from threading import Thread
from SocketServer import ThreadingMixIn
from BaseHTTPServer import HTTPServer, BaseHTTPRequestHandler
class Handler(BaseHTTPRequestHandler):
def do_GET(self):
self.send_response(200)
self.send_header("Content-type", "text/plain")
self.end_headers()
self.wfile.write("Hello World!")
class ThreadingHTTPServer(ThreadingMixIn, HTTPServer):
daemon_threads = True
def serve_on_port(port):
server = ThreadingHTTPServer(("localhost",port), Handler)
server.serve_forever()
Thread(target=serve_on_port, args=[1111]).start()
serve_on_port(2222)
update:
This also works with Python 3 but three lines need to be slightly changed:
from socketserver import ThreadingMixIn
from http.server import HTTPServer, BaseHTTPRequestHandler
and
self.wfile.write(bytes("Hello World!", "utf-8"))
Not easily. You could have two ThreadingHTTPServer instances, write your own serve_forever() function (don't worry it's not a complicated function).
The existing function:
def serve_forever(self, poll_interval=0.5):
"""Handle one request at a time until shutdown.
Polls for shutdown every poll_interval seconds. Ignores
self.timeout. If you need to do periodic tasks, do them in
another thread.
"""
self.__serving = True
self.__is_shut_down.clear()
while self.__serving:
# XXX: Consider using another file descriptor or
# connecting to the socket to wake this up instead of
# polling. Polling reduces our responsiveness to a
# shutdown request and wastes cpu at all other times.
r, w, e = select.select([self], [], [], poll_interval)
if r:
self._handle_request_noblock()
self.__is_shut_down.set()
So our replacement would be something like:
def serve_forever(server1,server2):
while True:
r,w,e = select.select([server1,server2],[],[],0)
if server1 in r:
server1.handle_request()
if server2 in r:
server2.handle_request()
I would say that threading for something this simple is overkill. You're better off using some form of asynchronous programming.
Here is an example using Twisted:
from twisted.internet import reactor
from twisted.web import resource, server
class MyResource(resource.Resource):
isLeaf = True
def render_GET(self, request):
return 'gotten'
site = server.Site(MyResource())
reactor.listenTCP(8000, site)
reactor.listenTCP(8001, site)
reactor.run()
I also thinks it looks a lot cleaner to have each port be handled in the same way, instead of having the main thread handle one port and an additional thread handle the other. Arguably that can be fixed in the thread example, but then you're using three threads.

How to make a Tornado websocket client recieve server notifications?

I'm trying to make an application that normally sends a request to a server and receives a response. If it would have been only that, I'd go with HTTP and call it a deal. But some requests to the server make a change for other clients, so I want the server to send all the affected clients a message that they should update.
For that, I've chosen WebSockets protocol and the Tornado library to work with it using Python. The simple message exchange was pretty straightforward, despite the asynchrony. However, the WebSocket client is really not that configurable and I've been struggling to make a client listen for incoming notifications without this interrupting the main message exchange.
The server part is represented by the tornado.websocket.WebSocketHandler, which has an on_message method:
from tornado.websocket import WebSocketHandler
class MyHandler(WebSocketHandler):
def on_message(self, message):
print('message:', message)
And I'd like something like that in the client part, which is only represented by a function tornado.websocket.websocket_connect (source). This function initiates a tornado.websocket.WebSocketClientConnection (source) object, which has an on_message method, but due to the entangled asynchronous structure, I haven't been able to override it properly, without breaking the main message exchange.
Another way I tried to go was the on_message_callback. This sounded like something I could use, but I couldn't figure out how to use it with read_message. This was my best attempt:
import tornado.websocket
import tornado.ioloop
ioloop = tornado.ioloop.IOLoop.current()
def clbk(message):
print('received', message)
async def main():
url = 'server_url_here'
conn = await tornado.websocket.websocket_connect(url, io_loop = ioloop, on_message_callback=clbk)
while True:
print(await conn.read_message()) # The execution hangs here
st = input()
conn.write_message(st)
ioloop.run_sync(main)
With this being the server code:
import tornado.ioloop
import tornado.web
import tornado.websocket
import os
class EchoWebSocket(tornado.websocket.WebSocketHandler):
def open(self):
self.write_message('hello')
def on_message(self, message):
self.write_message(message)
self.write_message('notification')
if __name__ == "__main__":
app = tornado.web.Application([(r"/", EchoWebSocket)])
app.listen(os.getenv('PORT', 8080))
tornado.ioloop.IOLoop.current().start()
I don't know what's going on here. Am I even going in the right direction with this?
There are two issues here:
Use on_message_callback or loop on await read_message(), but not both. If you give a callback the messages will only be passed to that callback and not saved for use by read_message.
input is blocking and doesn't play well with Tornado. It's fine in this little toy demo but if you want to do something like this in production you'll probably want to do something like wrap a PipeIOStream around sys.stdin and use stream.read_until('\n').

tornado - WebSocketClientConnection - How to catch and handle connection failures?

I am a Tornado and also Websocket newbie. There are many resources how to implement a websocket server application with Tornado. However, I haven't found a complete example that contains a websocket client application built on top of Tornado.
Server Application (server.py)
#!/usr/bin/env python
# -*- coding: utf-8 -*-
import logging
import tornado.web
import tornado.websocket
import tornado.ioloop
import tornado.options
from tornado.options import define, options
define("port", default=3000, help="run on the given port", type=int)
class Application(tornado.web.Application):
def __init__(self):
handlers = [(r"/", MainHandler)]
settings = dict(debug=True)
tornado.web.Application.__init__(self, handlers, **settings)
class MainHandler(tornado.websocket.WebSocketHandler):
def check_origin(self, origin):
return True
def open(self):
logging.info("A client connected.")
def on_close(self):
logging.info("A device disconnected")
def main():
tornado.options.parse_command_line()
app = Application()
app.listen(options.port)
tornado.ioloop.IOLoop.instance().start()
if __name__ == "__main__":
main()
Client Application (client.py):
#!/usr/bin/env python
# -*- coding: utf-8 -*-
from tornado.ioloop import IOLoop
from tornado.websocket import websocket_connect
if __name__ == "__main__":
ws = websocket_connect("ws://localhost:3000")
IOLoop.instance().start()
Problems & Questions
I don't have any problems with server application. However, I can't say same thing for client application. Why?
If server goes down, client application doesn't react. It does not say "Hey, server is down buddy.". I write my own WebSocketClientConnection class (inherited by the original) and also websocket_connect function (same function, but this one uses my WebSocketClientConnection class). I override on_connection_close method in my WebSocketClientConnection class, so I can catch "server down on middle of the connection" failure. However, I cannot make it reconnect. How do I make it reconnect to server?
If server is already down before the connection, client application does not raise anything and seems perfectly. I don't know how to catch that failure. How do I catch this connection failure?
My WebSocketClientConnection class:
class MyWebSocketClientConnection(tornado.websocket.WebSocketClientConnection):
def on_connection_close(self):
super(MyWebSocketClientConnection, self).on_connection_close()
print "connection closed"
Thanks.
By the way, after I figured out, I did write an example Tornado WebSocket client/server pair to demonstrate how to do it.
https://github.com/ilkerkesen/tornado-websocket-client-example
I hope it helps someone.
websocket_connect returns a Future; to see whether it succeeded or failed you must examine the Future (probably by yielding it in a coroutine). Furthermore, after establishing the connection you should go into a read_message loop. Even if you don't expect the client to send any messages, you should still call read_message: this is how you will connections that are closed after being established (read_message will return None).
#gen.coroutine
def my_websocket_client(url):
ws = yield websocket_connect(url)
while True:
msg = yield ws.read_message()
if msg is None: break
# do stuff with msg

Where to join threads created in an asynchronous tornado request handler?

This one has me a bit baffled. Fairly new to tornado and threading in python, so I could be completely off the mark with what I'm trying to do here.
Probably best to start with some simplified code:
class Handler(tornado.web.RequestHandler):
def perform(self):
#do something cuz hey, we're in a thread!
def initialize(self):
self.thread = None
#tornado.web.asynchronous
def post(self):
self.thread = threading.Thread(target=self.perform)
self.thread.start()
self.write('In the request')
self.finish()
def on_connection_close(self):
logging.info('In on_connection_close()')
if self.thread:
logging.info('Joining thread: %s' % (self.thread.name))
self.thread.join()
My problem is that on_connection_close is never getting called, requests are getting handled just fine. Secondly, am I doing something terrible introducing threading in this manner?
I believe Thread.join() will block until the thread finishes, probably something you want to avoid. Rather than joining, you can have the thread callback to the handler.
When using threads, be aware that tornado isn't thread-safe, so you can't use any RequestHandler (for example) methods from threads.
This works for me:
import functools
import time
import threading
import logging
import tornado.web
import tornado.websocket
import tornado.locale
import tornado.ioloop
class Handler(tornado.web.RequestHandler):
def perform(self, callback):
#do something cuz hey, we're in a thread!
time.sleep(5)
output = 'foo'
tornado.ioloop.IOLoop.instance().add_callback(functools.partial(callback, output))
def initialize(self):
self.thread = None
#tornado.web.asynchronous
def get(self):
self.thread = threading.Thread(target=self.perform, args=(self.on_callback,))
self.thread.start()
self.write('In the request')
self.flush()
def on_callback(self, output):
logging.info('In on_callback()')
self.write("Thread output: %s" % output)
self.finish()
application = tornado.web.Application([
(r"/", Handler),
])
if __name__ == "__main__":
application.listen(8888)
tornado.ioloop.IOLoop.instance().start()
You can test it with curl --no-buffer localhost:8888. Some browsers (Safari) seem to wait for the connection to close before displaying any output, which threw me off for a while.
AFAIK, on_connection_close is only called only when the client terminates the connection, which may explain your problem.
Regarding threading, I don't know what you want to do, but I can't see why you would want to create a thread in a Tornado request as one of the advantages of Tornado is exactly that you don't have to use threading.
If I were to add a join to your example I would put it just before self.finish(), however, you can probably just omit it... that will depend on what you want to do with the thread, but remember that Tornado is single-threaded and the whole process will block if the thread is not finished by the time join() comes.

How do I stop Tornado web server? [duplicate]

This question already has answers here:
Stopping a tornado application
(3 answers)
Closed 1 year ago.
I've been playing around a bit with the Tornado web server and have come to a point where I want to stop the web server (for example during unit testing). The following simple example exists on the Tornado web page:
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()
Once tornado.ioloop.IOLoop.instance().start() is called, it blocks the program (or current thread). Reading the source code for the IOLoop object gives this example in the documentation for the stop function:
To use asynchronous methods from otherwise-synchronous code (such as
unit tests), you can start and stop the event loop like this:
ioloop = IOLoop()
async_method(ioloop=ioloop, callback=ioloop.stop)
ioloop.start()
ioloop.start() will return after async_method has run its callback,
whether that callback was invoked before or after ioloop.start.
However, I have no idea how to integrate this into my program. I actually have a class that encapsulates the web server (having it's own start and stop functions), but as soon as I call start, the program (or tests) will of course block anyway.
I've tried to start the web server in another process (using the multiprocessing package). This is the class that is wrapping the web server:
class Server:
def __init__(self, port=8888):
self.application = tornado.web.Application([ (r"/", Handler) ])
def server_thread(application, port):
http_server = tornado.httpserver.HTTPServer(application)
http_server.listen(port)
tornado.ioloop.IOLoop.instance().start()
self.process = Process(target=server_thread,
args=(self.application, port,))
def start(self):
self.process.start()
def stop(self):
ioloop = tornado.ioloop.IOLoop.instance()
ioloop.add_callback(ioloop.stop)
However, stop does not seem to entirely stop the web server since it is still running in the next test, even with this test setup:
def setup_method(self, _function):
self.server = Server()
self.server.start()
time.sleep(0.5) # Wait for web server to start
def teardown_method(self, _function):
self.kstore.stop()
time.sleep(0.5)
How can I start and stop a Tornado web server from within a Python program?
I just ran into this and found this issue myself, and using info from this thread came up with the following. I simply took my working stand alone Tornado code (copied from all the examples) and moved the actual starting code into a function. I then called the function as a threading thread. My case different as the threading call was done from my existing code where I just imported the startTornado and stopTornado routines.
The suggestion above seemed to work great, so I figured I would supply the missing example code. I tested this code under Linux on a FC16 system (and fixed my initial type-o).
import tornado.ioloop, tornado.web
class Handler(tornado.web.RequestHandler):
def get(self):
self.write("Hello, world")
application = tornado.web.Application([ (r"/", Handler) ])
def startTornado():
application.listen(8888)
tornado.ioloop.IOLoop.instance().start()
def stopTornado():
tornado.ioloop.IOLoop.instance().stop()
if __name__ == "__main__":
import time, threading
threading.Thread(target=startTornado).start()
print "Your web server will self destruct in 2 minutes"
time.sleep(120)
stopTornado()
Hope this helps the next person.
Here is the solution how to stop Torando from another thread. Schildmeijer provided a good hint, but it took me a while to actually figure the final example that works.
Please see below:
import threading
import tornado.ioloop
import tornado.web
import time
class MainHandler(tornado.web.RequestHandler):
def get(self):
self.write("Hello, world!\n")
def start_tornado(*args, **kwargs):
application = tornado.web.Application([
(r"/", MainHandler),
])
application.listen(8888)
print "Starting Torando"
tornado.ioloop.IOLoop.instance().start()
print "Tornado finished"
def stop_tornado():
ioloop = tornado.ioloop.IOLoop.instance()
ioloop.add_callback(ioloop.stop)
print "Asked Tornado to exit"
def main():
t = threading.Thread(target=start_tornado)
t.start()
time.sleep(5)
stop_tornado()
t.join()
if __name__ == "__main__":
main()
In case you do no want to bother with threads, you could catch a keyboard interrupt signal :
try:
tornado.ioloop.IOLoop.instance().start()
# signal : CTRL + BREAK on windows or CTRL + C on linux
except KeyboardInterrupt:
tornado.ioloop.IOLoop.instance().stop()
There is a problem with Zaar Hai's solution, namely that it leaves the socket open. The reason I was looking for a solution to stop Tornado is I'm running unit tests against my app server and I needed a way to start/stop the server between tests to have a clear state (empty session, etc.). By leaving the socket open, the second test always ran into an Address already in use error. So I came up with the following:
import logging as log
from time import sleep
from threading import Thread
import tornado
from tornado.httpserver import HTTPServer
server = None
thread = None
def start_app():
def start():
global server
server = HTTPServer(create_app())
server.listen(TEST_PORT, TEST_HOST)
tornado.ioloop.IOLoop.instance().start()
global thread
thread = Thread(target=start)
thread.start()
# wait for the server to fully initialize
sleep(0.5)
def stop_app():
server.stop()
# silence StreamClosedError Tornado is throwing after it is stopped
log.getLogger().setLevel(log.FATAL)
ioloop = tornado.ioloop.IOLoop.instance()
ioloop.add_callback(ioloop.stop)
thread.join()
So the main idea here is to keep a reference to the HTTPServer instance and call its stop() method. And create_app() just returns an Application instance configured with handlers. Now you can use these methods in your unit tests like this:
class FoobarTest(unittest.TestCase):
def setUp(self):
start_app()
def tearDown(self):
stop_app()
def test_foobar(self):
# here the server is up and running, so you can make requests to it
pass
To stop the entire ioloop you simply invoke the ioloop.stop method when you have finished the unit test. (Remember that the only (documented) thread safe method is ioloop.add_callback, ie. if the unit tests is executed by another thread, you could wrap the stop invocation in a callback)
If its enough to stop the http web server you invoke the httpserver.stop() method
If you need this behavior for unit testing, take a look at tornado.testing.AsyncTestCase.
By default, a new IOLoop is constructed for each test and is available as self.io_loop. This IOLoop should be used in the construction of HTTP clients/servers, etc. If the code being tested requires a global IOLoop, subclasses should override get_new_ioloop to return it.
If you need to start and stop an IOLoop for some other purpose and can't call ioloop.stop() from a callback for some reason, a multi-threaded implementation is possible. To avoid race conditions, however, you need to synchronize access to the ioloop, which is actually impossible. Something like the following will result in deadlock:
Thread 1:
with lock:
ioloop.start()
Thread 2:
with lock:
ioloop.stop()
because thread 1 will never release the lock (start() is blocking) and thread 2 will wait till the lock is released to stop the ioloop.
The only way to do it is for thread 2 to call ioloop.add_callback(ioloop.stop), which will call stop() on thread 1 in the event loop's next iteration. Rest assured, ioloop.add_callback() is thread-safe.
Tornado's IOloop.instance() has trouble stopping from an external signal when run under multiprocessing.Process.
The only solution I came up with that works consistently, is by using Process.terminate():
import tornado.ioloop, tornado.web
import time
import multiprocessing
class Handler(tornado.web.RequestHandler):
def get(self):
self.write("Hello, world")
application = tornado.web.Application([ (r"/", Handler) ])
class TornadoStop(Exception):
pass
def stop():
raise TornadoStop
class worker(multiprocessing.Process):
def __init__(self):
multiprocessing.Process.__init__(self)
application.listen(8888)
self.ioloop = tornado.ioloop.IOLoop.instance()
def run(self):
self.ioloop.start()
def stop(self, timeout = 0):
self.ioloop.stop()
time.sleep(timeout)
self.terminate()
if __name__ == "__main__":
w = worker()
print 'starting server'
w.start()
t = 2
print 'waiting {} seconds before stopping'.format(t)
for i in range(t):
time.sleep(1)
print i
print 'stopping'
w.stop(1)
print 'stopped'
We want to use a multiprocessing.Process with a tornado.ioloop.IOLoop to work around the cPython GIL for performance and independency. To get access to the IOLoop we need to use Queue to pass the shutdown signal through.
Here is a minimalistic example:
class Server(BokehServer)
def start(self, signal=None):
logger.info('Starting server on http://localhost:%d'
% (self.port))
if signal is not None:
def shutdown():
if not signal.empty():
self.stop()
tornado.ioloop.PeriodicCallback(shutdown, 1000).start()
BokehServer.start(self)
self.ioloop.start()
def stop(self, *args, **kwargs): # args important for signals
logger.info('Stopping server...')
BokehServer.stop(self)
self.ioloop.stop()
The Process
import multiprocessing as mp
import signal
from server import Server # noqa
class ServerProcess(mp.Process):
def __init__(self, *args, **kwargs):
self.server = Server(*args, **kwargs)
self.shutdown_signal = _mp.Queue(1)
mp.Process.__init__(self)
signal.signal(signal.SIGTERM, self.server.stop)
signal.signal(signal.SIGINT, self.server.stop)
def run(self):
self.server.start(signal=self.shutdown_signal)
def stop(self):
self.shutdown_signal.put(True)
if __name__ == '__main__':
p = ServerProcess()
p.start()
Cheers!
Just add this before the start():
IOLoop.instance().add_timeout(10,IOLoop.instance().stop)
It will register the stop function as a callback in the loop and lauch it 10 second after the start

Categories