"Refresh" a request using the Periodic Callback

1,304 views
Skip to first unread message

Irene Bontà

unread,
Mar 25, 2013, 6:50:08 AM3/25/13
to python-...@googlegroups.com

Hi all I have this part of code:

self.lock_tables("write", ['confcommands'])
self.db.execute("INSERT INTO confcommands (network_id, ntype, timestamp, command) \
                 VALUES (%s,%s,%s,%s)", nid, mynet_type, ts, cmdjson)
self.unlock_tables();

time.sleep(5)

como_url = "".join(['http://', options.como_address, ':', options.como_port, 
                            '/ztc_config?netid=0&opcode_group=0&opcode=0&start=-20s&end=-1s'])

http_client = AsyncHTTPClient()
request = tornado.httpclient.HTTPRequest(url=como_url, connect_timeout=5.0, request_timeout=5.0)
response = yield tornado.gen.Task(http_client.fetch, request)

I write a command to send to a sensor into the DB. Then, I make an http request to another DB to read the response of the sensor. There's a time between the reading of the command by the sensor and its response with the elaborations.

How can I use Periodic Callback to periodically resend the request without I have to put a blocking time.sleep in my code? Help me please, I don't know where to begin...

A. Jesse Jiryu Davis

unread,
Mar 25, 2013, 1:48:21 PM3/25/13
to python-...@googlegroups.com
You can use PeriodicCallback like:

from tornado import gen, ioloop

loop = ioloop.IOLoop.instance()

@gen.engine
def f():
    print 'go!'

# Every 1000 ms
periodic = ioloop.PeriodicCallback(f, 1000)
periodic.start()
loop.start()

f needn't be decorated with gen.engine in this example, of course, but in your code you'll probably want to do some I/O in f so it should be a generator, thus I've used gen.engine. You could also just use a for loop:

import datetime

@gen.engine
def f():
    while True:
        # Pause 1 second while other tasks can run on the loop
        yield gen.Task(loop.add_timeout, datetime.timedelta(seconds=1))
        print 'go!'

f()
loop.start()


--
You received this message because you are subscribed to the Google Groups "Tornado Web Server" group.
To unsubscribe from this group and stop receiving emails from it, send an email to python-tornad...@googlegroups.com.
For more options, visit https://groups.google.com/groups/opt_out.
 
 

Ben Darnell

unread,
Mar 26, 2013, 10:52:51 PM3/26/13
to Tornado Mailing List
Even though it's possible to stop a PeriodicCallback after it's been started, it's kind of cumbersome to start and stop them.  I recommend only using PeriodicCallbacks for things that you want to run in the background forever, and in other cases use add_timeout directly.  You can generally replace "time.sleep(x)" with "yield gen.Task(IOLoop.instance().add_timeout, datetime.timedelta(seconds=x))"  (and in Tornado 3.0 you can use IOLoop.current() instead of IOLoop.instance()).

-Ben

Irene Bontà

unread,
Mar 28, 2013, 10:54:51 AM3/28/13
to python-...@googlegroups.com, b...@bendarnell.com
Yes Ben, thank you. I would use a PeriodicCallback in the background forever. But I don't know how to do.

I want to start the periodic callback in the main of my Tornado server. The periodic callback call a function that uses into itself methods that are defined in a Base Handler class... I don't know how to put this function because if I put the function as static in the server, she don't "see" methods are in the class. If I put the function in the class, the main don't know the function when the server starts....

Irene

A. Jesse Jiryu Davis

unread,
Mar 28, 2013, 11:00:13 AM3/28/13
to python-...@googlegroups.com
Irene, if you have methods that are executed periodically forever, then I don't think those methods should be defined in a RequestHandler. A RequestHandler handles HTTP requests. But your periodically executed methods aren't responding to an HTTP request. So take the methods out of the BaseHandler and define them at the regular module scope.

Irene Bontà

unread,
Mar 28, 2013, 11:18:23 AM3/28/13
to python-...@googlegroups.com
Yes I know...

And insted if I write something like this:

    class SensorSendHandler(BaseHandler):

    @tornado.web.authenticated
    @tornado.web.asynchronous
    @tornado.gen.engine
    def get(self, nid, sid):
........
.................
periodic = tornado.ioloop.PeriodicCallback(SensorSendHandler.check_commands_response(), 5000)
        periodic.start()

.....................

    @staticmethod
    def check_commands_response():
        print("ciao")
        nets = self.db.query("SELECT DISTINCT netid from networks")

        
        for net in nets:
            got_commands_response(net)
    
    @staticmethod
    def got_commands_response(netid):
        como_url = "".join("http://131.114.52.207:44444/ztc_config?netid=" \
                   + netid + "&opcode_group=0&opcode=0&start=-10s&end=-1s")
        
        http_client = AsyncHTTPClient()
        #asynchronous alternative to time.sleep
        yield tornado.gen.Task(tornado.ioloop.IOLoop.instance().add_timeout, time.time() + 5)
        response = yield tornado.gen.Task(http_client.fetch, como_url)
        
        print response



How can I call the check_commands_response function passing "self" to it?

A. Jesse Jiryu Davis

unread,
Mar 28, 2013, 11:26:13 AM3/28/13
to python-...@googlegroups.com
I think you should move both check_commands_response and got_commands_response *out* of BaseHandler. They don't belong in a handler. I don't think they belong in any class at all—they're just functions. Then these methods won't require a "self" parameter.

Both methods must be decorated with @gen.engine. See my blog post about calling one gen.engine-decorated function from another.

Also, you should pass check_commands_response, not check_commands_response(), to
PeriodicCallback.

Irene Bontà

unread,
Mar 28, 2013, 11:40:02 AM3/28/13
to python-...@googlegroups.com
So, I've put the PeriodicCallback call in the SensorSendHandler like this:

        periodic = tornado.ioloop.PeriodicCallback(check_commands_response, 5000)
        periodic.start()


And in the global space I've written like this:

@tornado.gen.engine
def check_commands_response():
    print("ciao")
    nets = db.query("SELECT DISTINCT netid from networks")

    
    for net in nets:
        got_commands_response(net)

@tornado.gen.engine
def got_commands_response(netid):
               + netid + "&opcode_group=0&opcode=0&start=-10s&end=-1s")
    
    http_client = AsyncHTTPClient()
    #asynchronous alternative to time.sleep
    yield tornado.gen.Task(tornado.ioloop.IOLoop.instance().add_timeout, time.time() + 5)
    response = yield tornado.gen.Task(http_client.fetch, como_url)
    
    print response

without the self reference....

But obviously now I have this error:
NameError: global name 'db' is not defined

A. Jesse Jiryu Davis

unread,
Mar 28, 2013, 11:53:26 AM3/28/13
to python-...@googlegroups.com
Yup. How do you connect to your database? You need some globally available reference to the db.

Irene Bontà

unread,
Mar 28, 2013, 12:04:15 PM3/28/13
to python-...@googlegroups.com
Yes. But it's defined in the Application class...

class Application(tornado.web.Application):
    def __init__(self):
        # define Nodes for the Application graph
        handlers = [ 
            (r"/*", HomeHandler),
            #(r"/auth/login", AuthLoginHandler),
            (r"/auth/login", FakeAuthLoginHandler),
            (r"/auth/logout", AuthLogoutHandler),
            (r"/networks/*", NetworksHandler),
            (r"/staticpage/([a-z]+)/*", StaticPageHandler),
            (r"/networks/edit", NetworkEditHandler),
            (r"/networks/delete", NetworkDeleteHandler),
            (r"/networks/([0-9]+)/status/*", NetworkStatusHandler),
            (r"/networks/([0-9]+)/config/*", NetworkConfHandler),
            (r"/networks/([0-9]+)/sensors/*", NetworkSensorsHandler),
            (r"/networks/([0-9]+)/sensors/([0-9]+)/*", SensorHandler),
            (r"/networks/([0-9]+)/send/*", NetworkSendHandler),
            (r"/networks/([0-9]+)/sensors/([0-9]+)/send/*", SensorSendHandler),
            (r"/networks/([0-9]+)/sensors/edit/*", SensorEditHandler),
            (r"/networks/([0-9]+)/sensors/delete/*", SensorDeleteHandler),
            (r"/networks/([0-9]+)/sensors/([0-9]+)/data/*", SensorDataHandler),
            (r"/networks/([0-9]+)/unreg", NetworkUnregSensorsHandler),
            (r"/networks/types", NetworksTypesHandler),
            (r"/networks/([0-9]+)/rights", NetworkRightsHandler),
            (r"/networks/([0-9]+)/sensors/([0-9]+)/rights", SensorRightsHandler),
            (r"/networks/([0-9]+)/all*", NetworkAllRightsHandler),
            (r"/networks/([0-9]+)/sensors/([0-9]+)/all*", SensorAllRightsHandler)
        ]

        # Define Tornado configuration
        settings = dict(
            title=u"MetroPower",
            template_path=os.path.join(os.path.dirname(__file__), "templates"),
            static_path=os.path.join(os.path.dirname(__file__), "static"),
            xsrf_cookies=True, # Use Cross-Site attack secured cookies?
            ui_modules={"SensorGraphSparkline": SensorGraphSparklineModule},
            # TODO: generate a new cookie secret
            cookie_secret="11oETzKXQAGaYdkL5gEmGeJJFuYh7EQnp2XdTP1o/Vo=",
            login_url="/auth/login", # Default login url
            autoescape=None, # Do not automatically esacape everything when
                               # rendering templates
            debug=True, # Automatically reload on source file saving + no cache 
        )

        # Initialize the Web Application
        tornado.web.Application.__init__(self, handlers, **settings)

        # Open DB connection. Shared among all handlers.
        self.db = tornado.database.Connection(
                host=options.mysql_host, database=options.mysql_database,
                user=options.mysql_user, password=options.mysql_password)

A. Jesse Jiryu Davis

unread,
Mar 28, 2013, 1:31:34 PM3/28/13
to python-...@googlegroups.com
So do that in the global scope instead! Or you could define your check_commands_response function as a method of application, then:

        periodic = tornado.ioloop.PeriodicCallback(app.check_commands_response, 5000)
Reply all
Reply to author
Forward
0 new messages