async def close(self): """ Closes a NATS streaming subscription. """ await self._nc.unsubscribe(self.sid) try: # Stop the processing task for the subscription. sub = self._sc._sub_map[self.inbox] sub._msgs_task.cancel() del self._sc._sub_map[self.inbox] except KeyError: pass req = protocol.UnsubscribeRequest() req.clientID = self._sc._client_id req.subject = self.subject req.inbox = self.ack_inbox if self.durable_name is not None: req.durableName = self.durable_name msg = await self._nc.timed_request( self._sc._sub_close_req_subject, req.SerializeToString(), self._sc._connect_timeout, ) resp = protocol.SubscriptionResponse() resp.ParseFromString(msg.data) if resp.error != "": raise StanError(resp.error)
async def subscribe( self, subject, cb=None, start_at=None, deliver_all_available=False, sequence=None, time=None, manual_acks=False, queue=None, ack_wait=DEFAULT_ACK_WAIT, max_inflight=DEFAULT_MAX_INFLIGHT, durable_name=None, pending_limits=DEFAULT_PENDING_LIMIT, ): """ :param subject: Subject for the NATS Streaming subscription. :param cb: Callback which will be dispatched the :param start_at: One of the following options: - 'new_only' (default) - 'first' - 'sequence' - 'last_received' - 'time' :param deliver_all_available: Signals to receive all messages. :param sequence: Start sequence position from which we will be receiving the messages. :param time: Unix timestamp after which the messages will be delivered. :param manual_acks: Disables auto ack functionality in the subscription callback so that it is implemented by the user instead. :param ack_wait: How long to wait for an ack before being redelivered previous messages. :param durable_name: Name of the durable subscription. :param: pending_limits: Max number of messages to await in subscription before it becomes a slow consumer. """ sub = Subscription( subject=subject, queue=queue, cb=cb, manual_acks=manual_acks, stan=self, ) self._sub_map[sub.inbox] = sub # Have the message processing queue ready before making the subscription. sub._msgs_queue = asyncio.Queue(maxsize=pending_limits, loop=self._loop) # Helper coroutine which will just put messages in to the queue, # whenever the NATS client receives a message. async def cb(raw_msg): nonlocal sub await sub._msgs_queue.put(raw_msg) # Should create the NATS Subscription before making the request. sid = await self._nc.subscribe(sub.inbox, cb=cb) sub.sid = sid req = protocol.SubscriptionRequest() req.clientID = self._client_id req.maxInFlight = max_inflight req.ackWaitInSecs = ack_wait if queue is not None: req.qGroup = queue if durable_name is not None: req.durableName = durable_name # Normalize start position options. if deliver_all_available: req.startPosition = protocol.First elif start_at is None or start_at == 'new_only': req.startPosition = protocol.NewOnly elif start_at == 'last_received': req.startPosition = protocol.LastReceived elif start_at == 'time': req.startPosition = protocol.TimeDeltaStart req.startTimeDelta = int(now() - time) * 1000000000 elif start_at == 'sequence': req.startPosition = protocol.SequenceStart req.startSequence = sequence elif start_at == 'first': req.startPosition = protocol.First # Set STAN subject and NATS inbox where we will be awaiting # for the messages to be delivered. req.subject = subject req.inbox = sub.inbox msg = await self._nc.timed_request( self._sub_req_subject, req.SerializeToString(), self._connect_timeout, ) resp = protocol.SubscriptionResponse() resp.ParseFromString(msg.data) # If there is an error here, then rollback the # subscription which we have sent already. if resp.error != "": try: await self._nc.unsubscribe(sid) except: pass raise StanError(resp.error) sub.ack_inbox = resp.ackInbox sub._msgs_task = self._loop.create_task(self._process_msg(sub)) return sub