How to get the QoS of a client in HiveMQ Client? - java

I'm working with HiveMQ Client and I wanted to know if there was a way to get the quality of service (QoS) that a client is subscribing with (in terms of a specific topic or in general)? I would be looking for a method I could invoke on a client like so:
Mqtt5BlockingClient subscriber = Mqtt5Client.builder()
.identifier(UUID.randomUUID().toString()) // the unique identifier of the MQTT client
.serverHost("localhost")
.serverPort(1883)
.buildBlocking();
subscriber.getQoS("topic") // returns the QoS of the subscriber is subscribing to the given topic
I would just want this information so I could print in to the console.

I think you have to read more about MQTT concepts.
The Quality of Service (QoS) level is an agreement between a sender and receiver of a message regarding the guarantees of delivering a message.
Therefore, the QoS is used in the publish() and subscribe() methods not the connect().
This is the scenario:
1. Connect: You have to connect your client to any broker with username/password. Every mqtt library has a connect() method. In this step, you have not specified qos yet.
After successful connection (every mqtt library has a callback for the connect method) and you can publish or subscribe to any desired (or allowed) topics.
Example:
Eclipse Paho library:
IMqttToken token = clientPhone.connect();
HiveMQ Library:
client.connect();
//or
client.connectWith().keepAlive(10).send();
//or
Mqtt5Connect connectMessage = Mqtt5Connect.builder().keepAlive(10).build();
client.connect(connectMessage);
2. Publish:
When you want to publish() a message, you have to specify a qos, so that the broker will respond to the client according with this qos:
Qos=0:
Client ---- Publish method ----> broker
Qos=1:
Client ---- Publish method ----> broker
Client <---- PubAck callback ---- broker
Qos=2:
Client ---- Publish method ----> broker
Client <---- PubRec callback ---- broker
Client ---- PubRel method ----> broker
Client <---- PubComp callback ---- broker
Example:
Eclipse Paho library:
IMqttDeliveryToken tokenPub = clientPhone.publish(topicPub, message);
HiveMQ Library:
client.publishWith()
.topic("test/topic")
.qos(MqttQos.AT_LEAST_ONCE)
.payload("payload".getBytes())
.send();
//or:
Mqtt5Publish publishMessage = Mqtt5Publish.builder()
.topic("test/topic")
.qos(MqttQos.AT_LEAST_ONCE)
.payload("payload".getBytes())
.build();
client.publish(publishMessage);
3. Subscribe:
A SUBSCRIBE message can contain an arbitrary number of subscriptions for a client. Each subscription is a pair of a topic and QoS level. The topic in the subscribe message can also contain wildcards, which makes it possible to subscribe to certain topic patterns. If there are overlapping subscriptions for one client, the highest QoS level for that topic wins and will be used by the broker for delivering the message.
Example:
Eclipse Paho library:
IMqttToken subToken = MqttAndroidClientInstance.subscribe(topics, qos);
HiveMQ Library:
client.subscribeWith().topicFilter("test/topic").qos(MqttQos.EXACTLY_ONCE).send();
//or:
Mqtt5Subscribe subscribeMessage = Mqtt5Subscribe.builder()
.topicFilter("test/topic")
.qos(MqttQos.EXACTLY_ONCE)
.build();
client.subscribe(subscribeMessage);
Edit(1):
A mqtt client have to use the following parameters, if wants to receive the already subscribed topics after reconnecting:
A- connect with cleanSession false.
B- Subscribe with QOS>0.

Related

Spring WebSocket: Order of subscriptions don't allow messages to be received

I followed the https://spring.io/guides/gs/messaging-stomp-websocket/ and added a few modifications (mainly Spring Security Auth and dynamic topics(Rooms)).
The order the client subscribes to topics doesn't allow messages onDisconnect event to be received by the client that's still in the room despite the server sysout showing that it sends.
I have 2 topics that the client subscribes to in a room and this order of subscriptions works but if I change the order of the subscriptions then the user that's still in the room won't receive a message:
// user direct message
stompClient.subscribe(`/user/topic/${roomId}`, (message) => {...}
// General
stompClient.subscribe(`/topic/${roomId}`, (message) => {...}
2 users connect and I send alerts to the client that the users joined and all is well until 1 client disconnects. In the order of subscriptions above the client that's still in the room will receive the message that a client has disconnected but if the subscription order is changed then the client will not receive the message that a client disconnected.
Does the order in how the client subscribes to topics matter?

How to send websocket message to a specific subscription in a session?

I have implemented a WebSocket server using Spring WebSocket and STOMP. There are multiple subscriptions over a single session and I want to send message to a specific subscription only.
Steps to reproduce:
Client connects to server by calling registered STOMP endpoint.
Client makes 2 subscription over the connection.
SUBSCRIBE
country:germany
id:sub-0
destination:/user/queue/countryUpdates
SUBSCRIBE
country:france
id:sub-1
destination:/user/queue/countryUpdates
In SimpUserRegistry there is 1 user and 1 session with 2 subscription.
Problem is if I send a message to one subscription it's send to other subscription as well.
At the time of sending the message I am adding subscription id in native headers, but it's not working.
SimpMessageHeaderAccessor accessor = SimpMessageHeaderAccessor.create(SimpMessageType.MESSAGE);
accessor.setNativeHeader("id", subscriptionId);
accessor.setNativeHeader("subscription", subscriptionId);
accessor.setSubscriptionId(subscriptionId);
accessor.setLeaveMutable(true);
messagingTemplate.convertAndSendToUser(simpUserId, REPLY_DESTINATION, message, accessor.getMessageHeaders());
What I've tried: If I add country to destination, each subscription have unique destination than there are no duplicate messages.
I want to use id/subscriptionId to determine the subscription and send message to that particular subscription.

Azure Service bus with AMQP - how to specify the session ID

I am trying to send messages to Service bus using AMQP QPID java library
I am getting this error:
"SessionId needs to be set for all brokered messages to a Partitioned
Topic that supports Ordering"
My topic has "Enforce Message ordering" turned on (this is way i get this error i guess)
When using the Azure Service bus java library (and not AMQP) i have this function :
this.entity.setSessionId(...);
When using the AMQP library i do not see an option to set the session ID on the message i want to send
Note that if i un-check the option "Enforce Message ordering" the message will be sent successfully
This is my code
private boolean sendServiceBusMsg(MessageProducer sender,Session sendSession) {
try {
// generate message
BytesMessage createBytesMessage = (BytesMessage)sendSession.createBytesMessage();
createBytesMessage.setStringProperty(CAMPAIGN_ID, campaignKey);
createBytesMessage.setJMSMessageID("ID:" + bm.getMessageId());
createBytesMessage.setContentType(Symbol.getSymbol("application/octet-stream"));
/*message is the actual data i send / not seen here*/
createBytesMessage.writeBytes(message.toByteArray());
sender.send(createBytesMessage);
} catch (JMSException e) {
}
The SessionId property is mapped to AMQP message properties.group-id. The Qpid JMS client should map it to JMSXGroupID property, so try the following,
createBytesMessage.setStringProperty("JMSXGroupID", "session-1");
As you guessed, there is a similar SO thread Azure Service Bus topics partitioning verified that to disable the feature Enforce Message Ordering via set SupportOrdering with false can solve the issue, but it can't be done via Azure Service Bus Java library because the property supportsOrdering is privated now.
And you can try to set property Group as #XinChen said using AMQP, as the content below from here.
Service Bus Sessions, also called 'Groups' in the AMQP 1.0 protocol, are unbounded sequences of related messages. ServiceBus guarantees ordering of messages in a session.
Hope it helps.

how to set timeout to google pubsub publisher and subscriber?

In my java code I use Google -pubsub.
how can i set a timeout for
subscriber - wait for messages until timeout expires?
(how can i set a retry policy?)
publisher - wait till message is sent for timeout time.
(how can i set a retry policy?)
I saw this post but didn't manage to translate the js post to java
Here is how i set my sub
final Subscriber subscriber = Subscriber
.defaultBuilder(subscriptionName, receiver)
.setChannelProvider(channelProvider)
.build();
and pub
final Publisher publisher = Publisher.defaultBuilder(topicName)
.setChannelProvider(channelProvider)
.build();
With the latest Cloud Pub/Sub client library, you do not need to set timeouts or retry policies in the Subscriber. These are handled under the hood for you and you just need to pass your MessageReceiver into defaultBuilder. When messages are available, they will be sent to receiveMessage. If your subscribing stops for any non-retryable reason, then the Subscriber will be shut down. You can listen for these notifications by calling addListener on your Subscriber.
On the Publisher, you can use setRetrySettings in the Builder. In particular, you'd want to setTotalTimeout on RetrySettings.Builder. The Publisher will retry publish calls on retryable errors up to this total deadline.

Mqtt Paho - Trying to publish while broker is unreachable

I'm begininng to use Mqtt and I have a hard time with handling an unreliable network.
I'm using a Paho Java Client (in groovy) to publish messages to a distant Mosquitto Broker.
Is there a way, when the broker is unreachable, to have the Paho client persist the message and automatically re-connect to the broker and publish the locally stored messages ? Do I have to handle everything myself, using for example a local broker ?
Here is my client building code
String persistenceDir = config['persistence-dir'] ?: System.getProperty('java.io.tmpdir')
def persistence = new MqttDefaultFilePersistence(persistenceDir)
client = new MqttAsyncClient(uri, clientId, persistence)
client.setCallback(this)
options = new MqttConnectOptions()
if (config.password) {
options.setPassword(config.password as char[])
options.setUserName(config.user)
}
options.setCleanSession(false)
client.connect(options)
And my publish code
def message = new MqttMessage(Json.encode(outgoingMessage).getBytes())
try {
client?.connect(options)
def topic = client.getTopic('processMsg')
message.setQos(1)
def token = topic.publish(message)
if (client) {
client.disconnect()
}
Thanks
The Paho client will only persist in-flight messages when it is connected to the broker.
Typically, when connectivity issues start to arrive you'll see message timeouts popping up
Timed out waiting for a response from the server (32000)
At that point the message will still be persisted.
However, when the connection is lost, and you start seeing this
Client is not connected (32104)
You should assume that the message has not been persisted by Paho.
You can debug this in org.eclipse.paho.client.mqttv3.internal.ClientComms :
/**
* Sends a message to the broker if in connected state, but only waits for the message to be
* stored, before returning.
*/
public void sendNoWait(MqttWireMessage message, MqttToken token) throws MqttException {
final String methodName = "sendNoWait";
if (isConnected() ||
(!isConnected() && message instanceof MqttConnect) ||
(isDisconnecting() && message instanceof MqttDisconnect)) {
this.internalSend(message, token);
} else {
//#TRACE 208=failed: not connected
log.fine(className, methodName, "208");
throw ExceptionHelper.createMqttException(MqttException.REASON_CODE_CLIENT_NOT_CONNECTED);
}
}
The internalSend will persist the message, but only if it is connected to the broker.
Also take into account that there is a maximum number of inflight messages that Paho can process. If it exceeds that it will also decide to not persist the message.
You could just setup a local broker and bridge that with the remote broker. That way you can queue up all your messages locally and when the remote broker comes back online all can be delivered.
Yes... After you get an exception that the message can't be delivered, it has to be either persisted or the message needs to be regenerated.
If you plan to use a local broker you can look at Really Small Message Broker (https://www.ibm.com/developerworks/community/groups/service/html/communityview?communityUuid=d5bedadd-e46f-4c97-af89-22d65ffee070)

Categories

Resources