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

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.

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) {
}
});

ISubscription to be passed in CreateFlow method for ITopicEndpoint

I am creating a Flow instance for a ITopicEndpoint, the documentation says that ISubscription should be passed to the createFlow method only if endpoint is of type ISubscriberEndpoint.
From the documentation
subscription Type:
SolaceSystems.Solclient.Messaging.ISubscription Only valid if endpoint
is of type ISubscriberEndpoint.
I am creating a Flow for a ITopicEndpoint so why am I getting the below error. What should i pass for ISubscription?
System.ArgumentException: 'subscription must be non-null when endpoint is of type ITopicEndpoint'
topic = ContextFactory.Instance.CreateDurableTopicEndpointEx(topicName);
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 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 loose 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'

How to get solace queue statistics from Solclient API? c#

I am looking to retrieve some Solace queue stats e.g. the current messages spooled count out of the maximum limit for us to set a threshold to stop publishing more messages to the queue.
Also, to subscribe to vpn events to track message discard rates.
By the time we receive errors e.g. MaxMsgUsageExceeded/SpoolOverQuota, it will be too late.
I can't seem to find any of these on SolaceSystems.Solclient.Messaging API
https://docs.solace.com/API-Developer-Online-Ref-Documentation/net/html/7f10bcf6-19f4-beff-0768-ced843e35168.htm
Would be great if someone could help
(using C# for this)
To poll for Solace queue stats from your C# application, you could use legacy SEMP over the message bus to make a SEMP request for the details that you want. Semp (Solace Element Management Protocol) is a request/reply protocol that uses an XML schema to identify all managed objects available in a message broker. Applications can use SEMP to manage and monitor a message broker.
To allow for legacy SEMP to be used over the message bus, as opposed to the management interface, it first needs to be enabled on the Solace PubSub+ message broker at the VPN level.
To publish a SEMP request with the Solace .Net Messaging API, perform the following steps:
Create a Session.
Create the message topic. “#SEMP//SHOW”
ITopic topic = ContextFactory.Instance.CreateTopic( “#SEMP/<router name>/SHOW”);
Create a request message and set its Destination to the topic in Step 2:
IMessage requestMsg = ContextFactory.Instance.CreateMessage();
requestMsg.Destination = topic;
Set the SEMP request string as the binary attachment.
string SOLTR_VERSION = "8_4_0" //change to the message-broker's version
string SEMP_SHOW_QUEUE = "<rpc semp-version=\"soltr/" + SOLTR_VERSION +
"<show><queue><name>queueName</name><detail></detail></queue></show></rpc>";
requestMsg.BinaryAttachment = Encoding.UTF8.GetBytes(SEMP_SHOW_QUEUE);
Call the SendRequest(…) method on Session.
IMessage replyMsg;
ReturnCode rc = session.SendRequest(requestMsg, out replyMsg, timeout);
The SEMP response is returned in replyMsg.
Obtain the binary attachment data from the reply message:
replyMsg.BinaryAttachment
The binary attachment contains the SEMP reply for the command topic in the publish request.
The Solace PubSub+ message broker does raise an event when an egress message is discarded. However, it is only sent out approximately once every 60 seconds for the specified client so it is not possible to get these exact rates.
It is possible for your .NET application to subscribe to VPN-level events over the message-bus. To do this, you must first enable the Solace PubSub+ message broker to publish the events. You can then subscribe to the special topic and receive the events as messages.
The topic to subscribe to is:
#LOG/<level>/VPN/<routerName>/<eventName>/<vpnName>
The different levels can use the * wildcard. For example, if you wish to subscribe to all VPN events of all levels for the VPN apple on router QA-NY1, the topic string would be:
#LOG/*/VPN/QA-NY1/*/apple
SEMP (starting in v2) is a RESTful API for configuring, monitoring, and administering a Solace PubSub+ broker.
1-The swapper page link is SEMP V2 API
2-The Swagger metadata definitions URL is located # http://{solace-sever-url}/SEMP/v2/config/spec
3- From Visual studio, add REST API Client
4-In the configuration dialog pass swagger metadata URL (defined at step 2), for code purpose I choose SolaceSemp as input value parameter for client namespace input.
4 Once you click ok, VS will create the client along with the models under SolaceSemp namespace
5 Start using the client as per following
using SolaceSemp;
using Microsoft.Rest;
var credentials = new BasicAuthenticationCredentials();
credentials.UserName = "place user name";
credentials.Password = "place password";
using (var client = new SolaceSempClient(credentials))
{
var model = client.GetAboutApi();
}

StompClientLib - unsubscribe socketclient

I've added StompClientLib in my project and I'm facing problem while unsubscribing a destination topic.
Unsubscription of destination gives following error:
"org.apache.activemq.transport.stomp.ProtocolException: No subscription matched.\r\tat org.apache.activemq.transport.stomp.ProtocolConverter.onStompUnsubscribe(ProtocolConverter.java:734)\r\tat org.apache.activemq.transport.stomp.ProtocolConverter.onStompCommand(ProtocolConverter.java:262)\r\tat org.apache.activemq.transport.ws.AbstractStompSocket.processStompFrame(AbstractStompSocket.java:151)\r\tat org.apache.activemq.transport.ws.jetty9.StompSocket.onWebSocketText(StompSocket.java:96)\r\tat org.eclipse.jetty.websocket.common.events.JettyListenerEventDriver.onTextMessage(JettyListenerEventDriver.java:128)\r\tat org.eclipse.jetty.websocket.common.message.SimpleTextMessage.messageComplete(SimpleTextMessage.java:69)\r\tat org.eclipse.jetty.websocket.common.events.AbstractEventDriver.appendMessage(AbstractEventDriver.java:64)\r\tat org.eclipse.jetty.websocket.common.events.JettyListenerEventDriver.onTextFrame(JettyListenerEventDriver.java:122)\r\tat org.eclipse.jetty.websocket.common.events.AbstractEventDriver.incomingFrame(AbstractEventDriver.java:160)\r\tat org.eclipse.jetty.websocket.common.WebSocketSession.incomingFrame(WebSocketSession.java:309)\r\tat org.eclipse.jetty.websocket.common.extensions.ExtensionStack.incomingFrame(ExtensionStack.java:214)\r\tat org.eclipse.jetty.websocket.common.Parser.notifyFrame(Parser.java:220)\r\tat org.eclipse.jetty.websocket.common.Parser.parse(Parser.java:258)\r\tat org.eclipse.jetty.websocket.common.io.AbstractWebSocketConnection.readParse(AbstractWebSocketConnection.java:628)\r\tat org.eclipse.jetty.websocket.common.io.AbstractWebSocketConnection.onFillable(AbstractWebSocketConnection.java:476)\r\tat org.eclipse.jetty.io.AbstractConnection$2.run(AbstractConnection.java:540)\r\tat org.eclipse.jetty.util.thread.QueuedThreadPool.runJob(QueuedThreadPool.java:635)\r\tat org.eclipse.jetty.util.thread.QueuedThreadPool$3.run(QueuedThreadPool.java:555)\r\tat java.lang.Thread.run(Unknown Source)\r")
Yes, It's an issue with subscription name but It doesn't accept the same string I used to subscribe specific channel.
For example:
```
// destination
let destinationChannelTopic = "/topic/channel_1234"
// subscribe successful
socketClient?.subscribe(destination: destinationChannelTopic)
// unsubscribe not successful with same destination
socketClient?.unsubscribe(destination: destinationChannelTopic)
```
Here unsubscribe replies me with an error: No subscription matched
Can anyone help me to understand, what is wrong? What am I doing wrong?
As I analyzed from Subscribe and receive messages, subscription (a subscribe method) returns a string (subscription channel id) from the server, that we need to store somewhere at client side (in our project/code) and we need to use the same string to unsubscribe.
This is javcScript (not an iOS - swift) code, a sample that available in this link (Subscribe and receive messages) and we implemented similar way it in web application:
```
// subscribe
var subscription = client.subscribe("/queue/test", callback);
```
The subscribe() methods return a JavaScript object with 1 attribute, id, that correspond to the client subscription ID and one method unsubscribe() that can be used later on to unsubscribe the client from this destination.
```
// unsubscribe
subscription.unsubscribe();
```
So, is this only the method/possible way, for subscription and unsubscription. If yes, then we do not have any value returning to subscribe(...) that I can use to unsubscribe. I don't get any value (destination subscription id) from subscription socketClient?.subscribe(destination: destinationChannelTopic) method
As an alternate solution to this issue, I disconnect the client from the server and reconnect again + subscribe to all other destinations again. And this is not a proper way to handle but I have only this solution at this time.
Please help to find out the solution for this problem.
Here is reference link about the issue: unsubscribe socketclient #14
Subscription methodology approach for subscribe and unsubscribe are very different.
Subscribe uses unique destination (id) to connect with server as a destination but links and remembers it with reference of subscription-id and uses subscription-id to identify itself on server while unsubcribe.
Here is code, you are looking for. Try it.
let destination = "/topic/channel_1234"
let ack = "ack_\(destination)" // It can be any unique string
let subsId = "subscription_\(destination)" // It can be any unique string
let header = ["destination": destination, "ack": ack, "id": subsId]
// subscribe
socketClient?.subscribeWithHeader(destination: destination, withHeader: header)
// unsubscribe
socketClient?.unsubscribe(destination: subsId)
The subscribe func in this StompClientLib library is not returning the subscription ID that the server will generate and return to the client. You can see the code here:
https://github.com/WrathChaos/StompClientLib/blob/master/StompClientLib/Classes/StompClientLib.swift#L356
So you will have to specify a subscription ID by using this other func from the library, and provide a stomp header that includes a subscription ID of your choosing:
public func subscribeWithHeader(destination: String, withHeader header: [String: String])
For example:
var destination = "mytopic"
var ack = StompCommands.ackAuto
var subsId = "12345"
let header = [StompCommands.commandHeaderDestination: destination, StompCommands.commandHeaderAck: ack, StompCommands.commandHeaderDestinationId: subsId]
socketClient?.subscribeWithHeader(destination, header)
...
socketClient?.unsubscribe(subsId)

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