Newbie to twisted and cyclone - How to get redis callbacks to work for a simple get request

1k Views Asked by At

Below is my code for trying to redis in a non blocking method from a get request to write to redis. Also below is the the error I get. It a 500 error. I just dont understand the documenation for txredisapi that shipped with cyclone. It did write to redis but the app

import cyclone.web
import sys
from twisted.internet import reactor
from twisted.python import log
import cyclone.redis as redis
from twisted.internet import defer
from twisted.internet import reactor

@defer.inlineCallbacks
def redisPixelWrite(remote_ip):
    rc = yield redis.Connection()
    yield rc.set("foo", 'itwodasdfred')
    v = yield rc.get("foo")
    print "foo:", v
    yield rc.disconnect()

class Application(cyclone.web.Application):
    def __init__(self):
        handlers = [
            (r"/", MainHandler),
        ]
        settings = {"xheaders": True,"static_path": "./static"}
        cyclone.web.Application.__init__(self, handlers, **settings)       

class MainHandler(cyclone.web.RequestHandler):
    def get(self):
        remote_ip = self.request.remote_ip
        redisPixelWrite(remote_ip).addcallback()
        self.set_header('Content-Type', 'text/html')
        self.write(remote_ip)

def main():
    log.startLogging(sys.stdout)
    reactor.listenTCP(8051, Application(), interface="127.0.0.1")
    reactor.run()

if __name__ == "__main__":
    main()


2012-05-07 21:12:10+0800 [-] Log opened.
2012-05-07 21:12:10+0800 [-] Application starting on 8051
2012-05-07 21:12:10+0800 [-] Starting factory <__main__.Application instance at 0x228af38>
2012-05-07 21:12:14+0800 [HTTPConnection,0,127.0.0.1] Starting factory <cyclone.redis.RedisFactory instance at 0x261a680>
2012-05-07 21:12:14+0800 [HTTPConnection,0,127.0.0.1] Uncaught exception GET / (127.0.0.1) :: HTTPRequest(protocol='http', host='127.0.0.1:8051', method='GET', uri='/', version='HTTP/1.1', remote_ip='127.0.0.1', body='', headers={'Connection': 'keep-alive', 'Accept-Language': 'en-us,en;q=0.5', 'Accept-Encoding': 'gzip, deflate', 'Cache-Control': 'max-age=0', 'Host': '127.0.0.1:8051', 'Accept': 'text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8', 'User-Agent': 'Mozilla/5.0 (X11; Ubuntu; Linux x86_64; rv:11.0) Gecko/20100101 Firefox/11.0'})
2012-05-07 21:12:14+0800 [HTTPConnection,0,127.0.0.1] 500 GET / (127.0.0.1) 2.07ms
2012-05-07 21:12:14+0800 [RedisProtocol,client] foo: itwodasdfred
2012-05-07 21:12:14+0800 [RedisProtocol,client] Stopping factory <cyclone.redis.RedisFactory instance at 0x261a680>
2

There are 2 best solutions below

0
On

This line:

redisPixelWrite(remote_ip).addcallback()

raise AttributeError. You should probably file a bug report against Cyclone, as it should have been reporting this somewhere (alternatively, make sure you're actually looking at the log where exceptions would be reported).

redisPixelWrite returns a Deferred because it is decorated with inlineCallbacks. There is no addcallback method on Deferred. There is an addCallback method, and I suppose that may be what you wanted to use. It takes at least one argument, though, not zero, so you need to fix that as well.

0
On

You missed some things in the code.

  1. The get function must be decorated with inlineCallbacks, and it should yield redisPixelWrite(remote_ip)
  2. You probably don't want to make a new connection to redis on every request. It's more efficient to set up a persistent connection pool, and reuse it on new requests.

This is probably what you're looking for:

import cyclone.redis
import cyclone.web
import sys

from twisted.python import log
from twisted.internet import defer
from twisted.internet import reactor


class RedisMixin(object):
    redis = None

    @classmethod
    def setup(self):
        RedisMixin.redis = cyclone.redis.lazyConnectionPool()


class Application(cyclone.web.Application):
    def __init__(self):
        handlers = [(r"/", MainHandler)]
        RedisMixin.setup()
        settings = {"debug": True, "xheaders": True, "static_path": "./static"}
        cyclone.web.Application.__init__(self, handlers, **settings)


class MainHandler(cyclone.web.RequestHandler, RedisMixin):
    @defer.inlineCallbacks
    def get(self):
        try:
            yield self.redis.set("foo", self.request.remote_ip)
            ip = yield self.redis.get("foo")
        except:
            raise cyclone.web.HTTPError(503)  # Service Unavailable
        else:
            self.set_header('Content-Type', 'text/html')
            self.write(ip)


def main():
    log.startLogging(sys.stdout)
    reactor.listenTCP(8051, Application(), interface="127.0.0.1")
    reactor.run()

if __name__ == "__main__":
    main()

Note that the default behaviour of all Connection methods is to auto reconnect should redis be stopped, or restarted. It'll respond with HTTP 503 (Service Unavailable) until the connection is re-established and communication with redis is resumed.