Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Run pika ioloop in background or use custom ioloop

I have a feeling that this should really not be all that difficult, yet I have had little success so far.

Say I have a class called PikaClass that wraps pika and provides some business methods.

def PikaClass(object):
  def __init__(self):
     # connect to the broker
     self.connection = pika.SelectConnection(<connection parameters>, self.on_connect)
     # ..other init stuff..

  def on_connect(self, connection):
     # called when the connection has been established 
     # ..open a channel, declare some queues, etc.

  def start(self):
     # start the polling loop 
     self.connection.ioloop.start()

  def foo(self, **kwargs):
     # do some business logic, e.g., send messages to particular queues

Intuitively, this is what I would like to achieve: a user creates an instance of PikaClass, sets the loop going in the background, and then interacts with the object by calling certain business methods

p = PikaClass()
p.start()
bar = p.foo(..)

The problem is that p.start() blocks and prevents the main code from interacting with the object once start() has been called. My first thought was to wrap the call in a thread:

Thread(target=p.start()).start()
bar = p.foo(..)

But that still blocks and you never get to p.foo(..). The docs mention that you shouldn't share a connection between threads so that may cause a problem somewhere.

I have also tried using AsyncoreConnection instead of SelectConnection, and calling _connect() directly (instead of using the ioloop) but that does not have any effect (nothing happens).

So how can I run the ioloop in the background, or at least run my own ioloop?

Note: This is Python 2.6 on win64 (xp) with the latest pika 0.9.4

like image 543
dgorissen Avatar asked Mar 11 '11 13:03

dgorissen


2 Answers

You are calling 'p.start' instead of passing it as a parameter. The code should be:

Thread(target=p.start).start()

Thread will call p.start when Thread.start is executed.

I'm not sure if this will solve your problem, but it may help you reach the solution.

like image 187
dieresys Avatar answered Nov 05 '22 00:11

dieresys


The GIL is not a problem here, because the ioloop spends nearly all of its time in the select(2) system call, during which time the GIL is released and other Python threads can run off and perform other tasks.

The simplest approach would be to set up and tear down a queue connection for every request. You might think this would be too expensive — since it requires re-authentication and (possibly) repeat SSL negotiation with every connection — but it should be the simplest and most robust and easiest to write, which should be your controlling factors unless you know that setup and teardown will in fact hurt performance in your application overall (which is best measured by testing).

Another approach would be to only start() the ioloop once you had a message to send, and have the method that receives the reply stop the ioloop so that your program gets control again. You can make the ioloop return early with:

    connection.ioloop.poller.open = False

and then remembering to set it back to True before you call start() again to wait for another reply.

like image 6
Brandon Rhodes Avatar answered Nov 05 '22 01:11

Brandon Rhodes