def __init__(self, delegate, vhost, spec, heartbeat=0): FrameReceiver.__init__(self, spec) self.delegate = delegate # XXX Cyclic dependency self.delegate.client = self self.vhost = vhost self.channelFactory = type("Channel%s" % self.spec.klass.__name__, (self.channelClass, self.spec.klass), {}) self.channels = {} self.channelLock = defer.DeferredLock() self.outgoing = defer.DeferredQueue() self.work = defer.DeferredQueue() self.started = TwistedEvent() self.queueLock = defer.DeferredLock() self.basic_return_queue = TimeoutDeferredQueue() self.queues = {} self.outgoing.get().addCallback(self.writer) self.work.get().addCallback(self.worker) self.heartbeatInterval = heartbeat if self.heartbeatInterval > 0: self.checkHB = reactor.callLater(self.heartbeatInterval * self.MAX_UNSEEN_HEARTBEAT, self.checkHeartbeat) self.sendHB = LoopingCall(self.sendHeartbeat) d = self.started.wait() d.addCallback(lambda _: self.reschedule_sendHB()) d.addCallback(lambda _: self.reschedule_checkHB())
class AMQClient(FrameReceiver): channelClass = AMQChannel # Max unreceived heartbeat frames. The AMQP standard says it's 3. MAX_UNSEEN_HEARTBEAT = 3 def __init__(self, delegate, vhost, spec, heartbeat=0): FrameReceiver.__init__(self, spec) self.delegate = delegate # XXX Cyclic dependency self.delegate.client = self self.vhost = vhost self.channelFactory = type("Channel%s" % self.spec.klass.__name__, (self.channelClass, self.spec.klass), {}) self.channels = {} self.channelLock = defer.DeferredLock() self.outgoing = defer.DeferredQueue() self.work = defer.DeferredQueue() self.started = TwistedEvent() self.queueLock = defer.DeferredLock() self.basic_return_queue = TimeoutDeferredQueue() self.queues = {} self.outgoing.get().addCallback(self.writer) self.work.get().addCallback(self.worker) self.heartbeatInterval = heartbeat if self.heartbeatInterval > 0: self.checkHB = reactor.callLater(self.heartbeatInterval * self.MAX_UNSEEN_HEARTBEAT, self.checkHeartbeat) self.sendHB = LoopingCall(self.sendHeartbeat) d = self.started.wait() d.addCallback(lambda _: self.reschedule_sendHB()) d.addCallback(lambda _: self.reschedule_checkHB()) def reschedule_sendHB(self): if self.heartbeatInterval > 0: if self.sendHB.running: self.sendHB.stop() self.sendHB.start(self.heartbeatInterval, now=False) def reschedule_checkHB(self): if self.checkHB.active(): self.checkHB.cancel() self.checkHB = reactor.callLater(self.heartbeatInterval * self.MAX_UNSEEN_HEARTBEAT, self.checkHeartbeat) def check_0_8(self): return (self.spec.minor, self.spec.major) == (0, 8) @defer.inlineCallbacks def channel(self, id): yield self.channelLock.acquire() try: try: ch = self.channels[id] except KeyError: ch = self.channelFactory(id, self.outgoing) self.channels[id] = ch finally: self.channelLock.release() defer.returnValue(ch) @defer.inlineCallbacks def queue(self, key): yield self.queueLock.acquire() try: try: q = self.queues[key] except KeyError: q = TimeoutDeferredQueue() self.queues[key] = q finally: self.queueLock.release() defer.returnValue(q) def close(self, reason): for ch in self.channels.values(): ch.close(reason) for q in self.queues.values(): q.close() self.delegate.close(reason) def writer(self, frame): self.sendFrame(frame) self.outgoing.get().addCallback(self.writer) def worker(self, queue): d = self.dispatch(queue) def cb(ign): self.work.get().addCallback(self.worker) d.addCallback(cb) d.addErrback(self.close) @defer.inlineCallbacks def dispatch(self, queue): frame = yield queue.get() channel = yield self.channel(frame.channel) payload = frame.payload if payload.method.content: content = yield readContent(queue) else: content = None # Let the caller deal with exceptions thrown here. message = Message(payload.method, payload.args, content) self.delegate.dispatch(channel, message) # As soon as we connect to the target AMQP broker, send the init string def connectionMade(self): self.sendInitString() self.setFrameMode() def frameReceived(self, frame): self.processFrame(frame) def sendFrame(self, frame): if frame.payload.type != Frame.HEARTBEAT: self.reschedule_sendHB() FrameReceiver.sendFrame(self, frame) @defer.inlineCallbacks def processFrame(self, frame): ch = yield self.channel(frame.channel) if frame.payload.type == Frame.HEARTBEAT: self.lastHBReceived = time() else: ch.dispatch(frame, self.work) if self.heartbeatInterval > 0: self.reschedule_checkHB() @defer.inlineCallbacks def authenticate(self, username, password, mechanism='AMQPLAIN', locale='en_US'): if self.check_0_8(): response = {"LOGIN": username, "PASSWORD": password} else: response = "\0" + username + "\0" + password yield self.start(response, mechanism, locale) @defer.inlineCallbacks def start(self, response, mechanism='AMQPLAIN', locale='en_US'): self.response = response self.mechanism = mechanism self.locale = locale yield self.started.wait() channel0 = yield self.channel(0) yield channel0.connection_open(self.vhost) def sendHeartbeat(self): self.sendFrame(Frame(0, Heartbeat())) self.lastHBSent = time() def checkHeartbeat(self): if self.checkHB.active(): self.checkHB.cancel() self.transport.connectionLost(failure.Failure(error.ConnectionLost())) def connectionLost(self, reason): if self.heartbeatInterval > 0: if self.sendHB.running: self.sendHB.stop() if self.checkHB.active(): self.checkHB.cancel() self.close(reason)