Ways to subscribe topic data published to mosquitto broker - mqtt

Device side mqtt publishes data to mosquitto broker installed on Raspberry Pi to a topic.
how to subscribe the data from the mosquitto broker in 2 ways.
Normal google example search gives me code samples. But what i want is 2 ways i thought one is MQTT API to subscribe providing topic which listens every time checking whether data has arrived or not which reduces the CPU performance and speed.
So, Other is if a message has arrived in the topic then it should call back saying message arrived instead of me going and looking into topic everytime checking is cumbersome. The second way increases CPU performance.
MemoryPersistence memoryPersistence = new MemoryPersistence();
MqttConnectOptions conOpt = new MqttConnectOptions();
conOpt.setUserName("mqttuser");
conOpt.setPassword(new String("mqttpassword").toCharArray());
conOpt.setCleanSession(true);
try {
MqttAsyncClient mqttAsyncClient = new MqttAsyncClient("tcp://localhost:1883", "1883", memoryPersistence);
mqttAsyncClient.setCallback(new MqttConnectionCallback());
if (!mqttAsyncClient.isConnected()) {
IMqttToken token = mqttAsyncClient.connect(conOpt);
logger.info();
System.out.println("Connected");
}
} catch (MqttException e) {
e.printStackTrace();
System.out.println("Unable to set up client: "+e.toString());
System.exit(1);
}
this is my listener class overridden method
#Override
public void messageArrived(String topic, MqttMessage msgData) throws Exception {
String time = new Timestamp(System.currentTimeMillis()).toString();
System.out.println("Time:\t" +time +
" Topic:\t" + topic +
" Message:\t" + new String(msgData.getPayload()) +
" QoS:\t" + msgData.getQos());
}
Need some clearance from coding side how this is accompolished in asynchronous ways.
Please correct if my understanding is wrong, specifically on how listener listenes on topicname and how the binding is done.

You need to use the subscribe() method on the mqttAsyncClient object (after connection has completed)
You can call this multiple times to subscribe to multiple topics.
Messages will be delivered to the messageArrived() callback and you can use the topic argument to determine which subscription the message arrived from.

Related

MQTT shared subscription

With a MQTT shared subscription, the message on the subscirbed topic would only be sent to one of the subscribing clients. Then, how the other clients of the group receive the message as they also subscribe to the same topic?
With a MQTT shared subscription, the message on the subscribed topic would only be sent to one of the subscribing clients.
Correct. With a normal (non‑shared) subscription the messages are sent to ALL subscribers; with shared subscriptions they are sent to ONE subscriber.
Prior to the introduction of shared subscriptions it was difficult to handle situations where you want multiple servers (for fault tolerance, load balancing etc) but only want to process each message once. Shared subscriptions provide a simple way of accomplishing this.
Then, how the other clients of the group receive the message as they also subscribe to the same topic?
If they are subscribing to the same shared subscription (with the same ShareName) then only one will receive the message; this is by design. If you want them all to receive the message then don't use a shared subscription. Alternatively you can establish multiple subscriptions (so all subscribers receive the message but only one processes it - but note the "could") as per the spec:
If a Client has a Shared Subscription and a Non‑shared Subscription and a message matches both of them, the Client will receive a copy of the message by virtue of it having the Non‑shared Subscription. A second copy of the message will be delivered to one of the subscribers to the Shared Subscription, and this could result in a second copy being sent to this Client.
There is an interesting bug in Java Paho (1.2.5) client that prevents working with shared topics that contains wildcards (#, +) https://github.com/eclipse/paho.mqtt.java/issues/827
Long story short, this will not work:
mqttClient.subscribe("$shared/group/some_topic/#", 1, (topic, message) -> System.out.println(topic));
instead it's required to use callbacks:
mqttClient.subscribe("$shared/group/some_topic/#", 1);
mqttClient.setCallback(new MqttCallback() {
#Override
public void connectionLost(final Throwable cause) {
}
#Override
public void messageArrived(final String topic, final MqttMessage message) throws Exception {
System.out.println(topic);
}
#Override
public void deliveryComplete(final IMqttDeliveryToken token) {
}
});

Solace - How to create ISubscription instance to be used by Session.CreateFlow method

I am trying to create a subscriber for my durable topic endpoint in solace via .NET APIs.
I have the below method where in I am trying to create a flow for my durable topic endpoint. I don't understand what is the need for ISubscription instance in the Session.CreateFlow method. (https://docs.solace.com/API-Developer-Online-Ref-Documentation/net/html/a548a98a-9134-c167-2517-192a26ceed77.htm)
How do I create an instance of ISubscription and what should it be?
public void Start()
{
//Create a instance of a durable topic endpoint
topic = ContextFactory.Instance.CreateDurableTopicEndpointEx(topicName);
FlowProperties flowProps = new FlowProperties();
flowProps.FlowStartState = false;
if (selector != null)
{
flowProps.Selector = selector;
}
flowProps.AckMode = ackMode == AckMode.ClientAcknowledge ? MessageAckMode.ClientAck : MessageAckMode.AutoAck;
if (windowSize.HasValue)
{
flowProps.WindowSize = windowSize.Value;
}
flow = session.CreateFlow(flowProps, topic, null, HandleFlowMessageEvent, HandleFlowEvent);
flow.Start();
}
I am currently passing it as null and I get the error
subscription must be non-null when endpoint is of type ITopicEndpoint
Secondly, I have a message handler for my flow event and session event, so when a message comes through which handler would it invoke. I would expect that I handle the message in the FlowMessageHandler as I am connecting to a durable topic endpoint. Please can someone shed more light on this?
//session message event handler
session = context.CreateSession(sessionProperties, HandleSessionMessageEvent, HandleSessionEvent);
//Flow message event handler
flow = session.CreateFlow(flowProps, topic, null, HandleFlowMessageEvent, HandleFlowEvent);
For a durable topic endpoint, if you have multiple subscribers then each would have to register with a unique subscription name (you can give any name as you like) so that the broker can maintain a durable connection with the specific subscriber. Even if the subscriber is down for a while and reconnects, it will not lose out on messages in the interim. The broker will push the messages to the subscriber. You can create a subscriber via the Solace Admin client or via code. Solace Admin -> Select the VPN -> Choose Endpoint tab -> select Durable Topic Endpoint -> click the + sign to create a subscriber -> ensure that consume is chosen for 'All other Permissions'.
The messages are sent to the session handler first and then to the flow handler. So you can access the messages from both the handlers.

communication between service worker instances

I'm trying to establish a communication channel between an installing service worker and an activated service worker.
I've tried to do the following:
on the installing service worker:
if ((self.registration.active == null) ||
(self.registration.active.state != "activated")) {
return;
}
var messageChannel = new MessageChannel();
messageChannel.port1.onmessage = function(event){
if (event.data.error) {
console.log("got error from active");
}
else {
console.log("got answer from active");
}
};
self.registration.active.postMessage({message: 'hey', port: messageChannel.port2}, [messageChannel.port2]);
on the active service worker:
self.addEventListener('message', function(event) {
console.log('received message');
});
This isn't working, I'm getting nothing...
Ideas?
Here's how I ended up implementing this.
Each serviceWorker at startup (code at the worker's global scope) connects to a broadcast channel like so:
var comChannel = new BroadcastChannel('SWCom');
comChannel.addEventListener('message', event => {
handleMessageEvent(event);
});
This channel is shared only between service workers.
To post a message to other SW, a SW can just broadcast on the channel comChannel.postMessage('hey there'); and any registered listener will be invoked.
One complication I had, not really related to the channel, is that I had a hard time identifying each SW life cycle state. If I want to communicate between SW it can't really serve any purpose if I don't know who's whom within each one of them. A SW cannot currently get a ref to its own ServiveWorker object, there's an open issue about it in the standard - https://github.com/w3c/ServiceWorker/issues/1077
In my usecase, I bypassed this limitation by performing the communication upon install (fits my needs...), like so:
self.addEventListener('install', function(event) {
if (self.registration.active != null) {
// if we got here it means this is a new SW that's
// starting install and there's an active one running.
// So we can send a message on the broadcast channel,
// whomever answers should be the active SW.
}
// ..
// installation code
// ..
}
One thing to note - I'm not sure this is well implemented.
I believe there are other states a SW can be at (redundant, deleted?, others?), so maybe there can be more then two ServiceWorkers alive, and then the assumption on the identity of the answering side on the channel might be wrong...
Jake provides some excellent examples of messaging that you may be able to derive a solution from. https://gist.github.com/jakearchibald/a1ca502713f41dfb77172be4523a9a4c
You may need to use the page itself as a proxy for sending/receiving messages between Service workers:
[SW1] <== message ==> [Page JS] <== message ==> [SW2]

Problems subscribing to a room (socket) with Socket.IO-Client-Swift and Swift, Sails.js on the Server

On Swift, I use
socket.on("test") {data, ack in
print(data)
}
In order to subscribe to a room (socket) on my Sails.js API.
When I broadcast a message from the server, with
sails.sockets.broadcast('test', { text : 'ok' })
the socket.on handler is never called.
However, if I set "log" TRUE to config when connecting my socket.io client from swift, in Socket-IO logs the message arrives.
What's wrong?
Eventually, I found my mistake:
The whole process I did is right:
(The request to join the room is done by the server, with sails.sockets.join)
Wrong thing was using socket.on with the ROOM NAME parameter.
I will explain it better, for others having same problem:
From Swift you should subscribe by making a websocket request to an endpoint on the server that accepts websocket requests (GET, POST, PUT). For example, you can make a POST request, passing in the room name into the body.
socket.emitWithAck("post", [
"room": "testroom",
"url": "/api/v1.0/roomsubscribing"
]).timingOut(after: 0) {data in
print("Server responded with \(data)")
}
On server side, inside the room-subscribing endpoint, you should have the following code:
roomSubscribing: function(req, res) {
if (!req.isSocket) {
return res.badRequest();
}
sails.sockets.join(req, req.params('room'), function(err) {
if (err) {
return res.serverError(err);
}
});
}
When the server want to broadcast some data to subscribers of the "testroom" room, the following code must be used:
sails.sockets.broadcast('testroom', { message: 'testmessage' }
Now on the swift's side you must use:
socket.on("message") { data, ack in
print(data)
}
in order to get the message handler to work. I thought you should use room name, instead you should use the KEY of the KEY/VALUE entry you used in your server when you broadcasted the data (in this case, "message").
I only have a small amount of experience with sockets, but in case nobody else answers...
I think you are missing step one of the three step socket process:
A client sends a message to the server asking to subscribe to a particular room.
The client sets up a socket.on to handle particular events from that room.
The server broadcasts an event in a particular room. All subscribers/clients with a .on for that particular event will react.
I could be wrong, but it sounds from your description like you missed step one. Your client has to send a message with io.socket, something like here, then your server has to use the socket request to have them join the room, something like in the example here.
(the presence of log data without the socket.on firing would seem to confirm that the event was broadcast in the room, but that client was not subscribed)
Good luck!

How to explicitly acknowledge/fail Amazon SQS FIFO queue from the listener without throwing an exception?

My application only listens to a certain queue, the producer is the 3rd party application. I receive the messages but sometimes based on some logic I need to send fail message to the producer so that the message is resend to my listener again until I decide to consume it and acknowledge it. My current implementation of this process is just throwing some custom exception. But this is not a clean solution, therefore can any one help me to send FAIL to producer without throwing exception.
My JMS Listener Factory settings:
#Bean
public DefaultJmsListenerContainerFactory jmsListenerContainerFactoryForQexpress(SQSErrorHandler errorHandler) {
SQSConnectionFactory connectionFactory = SQSConnectionFactory.builder()
.withRegion(RegionUtils.getRegion(StaticSystemConstants.getQexpressSqsRegion()))
.withAWSCredentialsProvider(new ClasspathPropertiesFileCredentialsProvider(StaticSystemConstants.getQexpressSqsCredentials()))
.build();
DefaultJmsListenerContainerFactory factory = new DefaultJmsListenerContainerFactory();
factory.setConnectionFactory(connectionFactory);
factory.setDestinationResolver(new DynamicDestinationResolver());
factory.setConcurrency("3-10");
factory.setSessionAcknowledgeMode(Session.CLIENT_ACKNOWLEDGE);
factory.setErrorHandler(errorHandler);
return factory;
}
My Listener Settings:
#JmsListener(destination = StaticSystemConstants.QUEXPRESS_ORDER_STATUS_QUEUE, containerFactory = "jmsListenerContainerFactoryForQexpress")
public void receiveQExpressOrderStatusQueue(String text) throws JSONException {
LOG.debug("Consumed QExpress status {}", text);
//here i need to decide either acknowlege or fail
...
if (success) {
updateStatus();
} else {
//todo I need to replace this with explicit FAIL message
throw new CustomException("Not right time to update status");
}
}
Please, share your experience on this. Thank you!
SQS -- internally speaking -- is fully asynchronous and completely decouples the producer from the consumer.
Once the producer successfully hands off a message to SQS and receives the message-id in response, the producer only knows that SQS has received and committed the message to its internal storage and that the message will be delivered to a consumer at least once.¹ There is no further feedback to the producer.
A consumer can "snooze" a message for later retry by simply not deleting it (see setSessionAcknowledgeMode docs) or by actively resetting the visibility timeout on the message instead of deleting it, which triggers SQS to leave the message in the in flight status until the timer expires, at which point it will again deliver the message for the consumer to retry.
Note, too, that a single SQS queue can have multiple producers and/or multiple consumers, as long as all the producers ask for and consumers provide identical services, but there is no intrinsic concept of which consumer or which producer. There is no consumer-to-producer backwards communication channel, and no mechanism for a producer to inquire about the status of an earlier message -- the design assumption is that once SQS has received a message, it will be delivered,² so no such mechanism should be needed.
¹at least once. Unless the queue is a FIFO queue, SQS will typically deliver the message exactly once, but there is not an absolute guarantee that the message will not be delivered more than once. Because SQS is a massive, distributed system that stores redundant copies of messages, it is possible in some edge case conditions for messages to be delivered more than once. FIFO queues avoid this possibility by leveraging stronger internal consistency guarantees, at a cost of reduced throughput of 300 TPS.
²it will be delivered assuming of course that you actually have a consumer running. SQS does not block the producer, and will allow you to enqueue an unbounded number of messages waiting for a consumer to arrive. It accepts messages from producers regardless of whether there are currently any consumers listening. The messages are held until consumed or until the MessageRetentionPeriod (default 4 days, max 14 days) timer expires for each message, whichever comes first.

Resources