Exemplo n.º 1
0
    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)
Exemplo n.º 2
0
    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