I'm trying to make sure that Cloud Pub/Sub will redeliver my message if there is no ack/nack sent. It doesn't seem to do this even though I wait for more than 10 minutes, which should be the maximum time for the Acknowledgement Deadline.
I'm using the example here as a starting point:
https://cloud.google.com/pubsub/docs/quickstart-py-mac
Essentially, I commented the line in the callback function that ack's the message. I used two terminals, one for publishing the message and one for receiving it as a subscriber. Since no ack's are being sent, I expected Cloud Pub/Sub to attempt to redeliver the message to the subscriber sometime within the ack deadline, but it doesn't.
The docs here
https://godoc.org/cloud.google.com/go/pubsub#hdr-Deadlines
say the the "ACK deadlines are extended periodically by the client... up to 10 minutes", so I waited 10 minutes in case the ack deadline was extended to that maximum but I still didn't receive the redelivered message.
Here is the edited callback method that I used. This was the only change I made to the example code.
def callback(message):
print('Received message {} of message ID {}'.format(
message, message.message_id))
# Acknowledge the message. Unack'ed messages will be redelivered.
# message.ack()
print('Acknowledged message of message ID {}\n'.format(
message.message_id))
If I kill the subscriber (sub.py) and restart it, the message is redelivered. Am I doing something wrong? Also, when I send a Nack instead of not sending anything at all, the message is quickly redelivered.
EDIT:
It seems like similar questions have been asked on
https://github.com/googleapis/google-cloud-python/issues/5005
https://github.com/googleapis/google-cloud-python/issues/5044
Things I wanted to confirm:
The Ack Deadline set in the subscription is not always the value used. It's extended as Pub/Sub deems necessary.
The 10 minute max Ack Deadline is not actually the maximum time that can elapse before a message is redelivered
This maximum time is determined by the flow_control.max_lease_duration variable (default is 2 hours)
The ackDeadline is the maximum amount of time a message can be outstanding to a subscriber without ack, nack, or modAckDeadline. Once this time passes, the message becomes a candidate to be redelivered, though redelivery may not be immediate. The maximum value for this field is ten minutes.
The Cloud Pub/Sub client libraries, upon receiving a message, automatically sends modAckDeadline requests for the message until it is acked, nacked, or the flow_control.max_lease_duration period passes. Essentially, it extends the ack deadline. The goal is for the client library to keep track of ack deadlines and extend messages as necessary itself so the user doesn't have to do it.
This is why you see the discrepancy between the client library behavior and the ackDeadline configured on the subscription. When using the client libraries, you should set the max_lease_duration to the maximum amount of time you want a message to be outstanding to a subscriber.
Related
I am trying to implement a way to know when a specific device has stopped publishing messages to an mqtt broker and in that case send an email to myself. I want to do this to be able to know if there is a problem with the device that is publishing the messages so I can check it and turn it back on. To try to accomplish this I created a mqtt client that subscribes to the topic that the device publishes e.g. test/device_1 and then set as last will and testament for that device status/device_1 where I put as payload="Offline". Ideally, I want to be able to do this for more than 1 device, but let's assume I just want it for the simple case of one device.
I created another script that implements another client that is subscribed to the topic status/device_1 and then on the on_message function it checks if it gets the payload="Offline" and if it does get it then I send an email to myself.
This approach however doesn't work as when I turn off my device, the mqtt client that is subscribed to the topic test/device_1 keeps listening but gets no messages. In other words, it doesn't send its last will even when the topic is empty. However, it seems to work when I stop the script that is subscribed to the topic test/device_1.
Am I missing something or is it not possible to accomplish what I am trying to do? Any advice is appreciated!
First LWT messages are only published by the broker if the client goes offline unexpectedly, which means if it shuts down cleanly then it will not get published, you have to manually publish a message as part of shut down process.
You can set the retained flag on the LWT so that any client that subscribes after the device has gone offline it will still see the state message.
You should also clear that message on when the device starts up, either by publishing something like Online or a null payload to clear the retained message.
You can use last timestamp when device published the messages as heartbeat and in your another script, if (current_time - last_timestamp) > 30 then you can email yourself that your device is offline. this way you can have threshold of time when you want to get email of device offline.
script 1:
mqttCient.on_message(msg):
last_timestamp = time.time()
script 2:
cur_time = time.time()
if ((cur_time - last_timestamp) > 30):
# email yourself
use Json file or database as buffer for storing and accessing last_timestamp
Currently developing something like "smart home" and I have few different devices in my home. All of them connected to OpenHab via MQTT. I'm using Paho MQTT library (Python) for my purposes.
Generally, MQTT has "keepalive" property. This property describes how much time my client will be connected (AFAIK it sends the ping to the server) to MQTT server when there are no updates on the subscribed topic.
But here I have a huge problem. Needed topic could be updated once per hour or even once per few days/months. Let's say that this is indoor alarm.
How can I avoid that keepalive timeout or ignore that field? Could it be unlimited?
You have miss understood what the keepalive value represents.
MQTT clients can stay connected indefinitely even if they do not publish or receive any messages. But the broker needs to keep track of which clients are still connected so it knows when to send the Last Will and Testament (LWT) message for the client. In order to do this it uses the keepalive time.
Every time a message is sent or received by the client, the broker resets a timer, if this timer exceeds 1.5 times the value of the keepalive time then the broker marks the client as disconnected and processes the LWT. To prevent clients with very low messages rates from being disconnected, such a client can send a PINGREQ packet at any time (most likely on timeout of the keepalive value) to the server/broker. The server receives the PINGREQ, answers with a PINGRESP packet and it will reset the keepalive timer to zero and leave the client in the connected state.
See Keep Alive section of the MQTT standard: (http://docs.oasis-open.org/mqtt/mqtt/v3.1.1/os/mqtt-v3.1.1-os.html#_Toc385349238)
The Client can send PINGREQ at any time, irrespective of the Keep Alive value, and use the PINGRESP to determine that the network and the Server are working. If the Keep Alive value is non-zero and the Server does not receive a Control Packet from the Client within one and a half times the Keep Alive time period, it MUST disconnect the Network Connection to the Client as if the network had failed
When sending the initial MQTT CONNECT message from a client, you can supply an optional "keep-alive" value. This value is a time interval, measured in seconds, during which the broker expects a client to send a message, such as a PUBLISH message. If no message is sent from the client to the broker during the interval, the broker automatically closes the connection. Note that the keep-alive value you specify is multiplied by 1.5, so setting a 10-minute keep-alive actually results in a 15 minute interval.
Have a look at the Keep Alive section of the MQTT specification:
A Keep Alive value of 0 has the effect of turning off the Keep Alive mechanism. If Keep Alive is 0 the Client is not obliged to send MQTT Control Packets on any particular schedule. v5 spec source
Therefore, set the keep alive to 0, and then the client doesn't have to send a keep alive signal as often. The server should respect that this connection with a client (e.g. from last year) should still be connected, but it won't be guaranteed (The client might be disconnected when the server is shut down).
I am trying to send a message using publish.single and then receive it and act upon the data received. Hence, I can't proceed unless I receive something from the server, so is there a way to write a statement that will wait for a message from MQTT before proceeding?
Example code:
publish.single("$topic", data, ip_address)
#can't do anything here
receive(data_from_broker) #or anythin that looks like it!
#continue with the program here
The short answer is you don't.
At least not in the way you describe. MQTT is an asynchronous protocol, there is no sense of sending a message and waiting for a response, a publishing client has no way to know if the is 0, 1 or n subscribing clients listening on the topic the message is published.
You will need to build something called a state machine to keep track of where in the program flow you are when messages are received.
e.g.
Application published message and sets flag in the state machine to say the message was sent
Remote client receives message and publishes a response
New message is received by the first client, it checks the state machine to determine that the message should be treated as a response to the original message.
To subscribe to a topic you will have to move on from using publish.single to the full MQTT client so you can setup the onMessage callback to handle the incoming messages.
I am subscribing to EWS push notifications for calendar events using python. https://msdn.microsoft.com/en-us/library/office/dd636171(v=exchg.80).aspx
When doing the same thing with google, you get an "expires" date in the first request, so you know when you need to renew the subscription.
How do I know when the EWS subscription is going to expire so I can renew?
There is no real timeout property for push notifications. Exchange push notifications works in a slightly different way, as alluded to by the time-out section in the documentation:
If the server does not receive a response to a push notification or
status ping, it retries sending the notification several times before
it stops sending the notifications.
So you do not subscribe for a set amount of time. You will get notifications as long as you respond to the server and acknowledge that you received the notification. The server will stop sending notifications to you when you stop sending status pings to every push notification.
How many times the Exchange server should retry and ask you for a status ping can be specified in the initial subscription request, with the StatusFrequency-element. You simply specify for how many minutes the server should ask you for an acknowledgement before giving up and stop pushing notifications to you.
Webdav101's article on the topic is really good, and he states that it is up to you to have some fallback code for when subscriptions stops for some reason. That could be handled by simply issuing a new push subscription if you have not received a notification for a while.
I am working on a simple web server that sends push notification to my iPhone app.
I've tried using existing Python packages, but they all suffer from this problem:
When an invalid token is sent, Apple will send back a error message and close the socket. I need to send a large number of push notifications, but if one of them has an invalid token, the rest won't be delivered to Apple.
So currently, my code looks something like this:
for m in messages:
socket.send(m)
But when the first of the messages has invalid token, so Apple closes the socket as soon as it receives the first message, socket still sends all the messages without any error.
I can't afford to do a recv after each message with a timeout, since that will take too long. What can I do to know when the socket is closed and open a new one?
If the socket had been closed on the other end a recv on it should return 0 or result in an error.
You could check the number of bytes sent by send.
Update: You might like to read here on another possibilty to monitor if a socket connection is still alive.
Update-1: Another approach might by to run a constantly reading thread on the socket in question in parallel to the write operation ... as you mention: time is your limit.
I learned that there is no way at TCP level that I can find out what packets were sent. I decided to just send all the messages and then wait (with timeout) for any error messages from Apple. If there is any error, repeat sending the ones that failed.
I decided to go with this, since getting an invalid token should be a rare event, and most of the time all the notifications will go through.