We are using ActiveMQ 5.16.1 with the stompit client to create a durable subscription in our NodeJS app using the following code snippet:
var connectOptions = {
"host": "",
"port": amqPort,
rejectUnauthorized: false,
checkServerIdentity: () => { },
"connectHeaders": {
"heart-beat": "15000,15000",// hear-beat of 15 seconds
'login': 'admin',
'passcode': 'admin',
'client-id' : "agent-manager"
}
};
var server1 = connectOptions;
server1.host = amqPrimaryHost;
var server2 = connectOptions;
server2.host = amqSecondaryHost;
var amqSubscription;
var subscribeHeaders = {
"destination": "/topic/delivery-reports",
"activemq.subscriptionName": "channel_manager_delivery_reports",
"ack": "client-individual"
};
var connectionManager = new stompit.ConnectFailover([server1,server2], reconnectOptions);
connectionManager.connect(function (error, client, reconnect){
if (error) {
logger.error("Terminal error, gave up reconnecting ", error);
return;
}
client.on("error", function (error) {
if(!client)
reconnect();
});
amqSubscription=client.subscribe(subscribeHeaders, function (error, message,subscription) {
logger.info("going to subscribe")
if (error) {
logger.error("Subscription failed. Going to disconnect", error);
subscription.unsubscribe();
// reconnect();
}
logger.info("subscribed")
});
});
function unsubscribe () {
logger.info("Going to unsub")
amqSubscription.unsubscribe({"activemq.subscriptionName":"channel_manager_delivery_reports"})
};
However, when I call the unsubscribe, it only changes the Subscriber active status to false but does not remove it from the active subscribers' list as shown in the screenshot.
Getting the following exception in stomp.logs.
2021-05-12 05:20:14,826 [0.1:50251#61613] WARN ProtocolConverter - Exception occurred for client agent-manager (tcp://127.0.0.1:50251) processing: UNSUBSCRIBE -> javax.jms.JMSException: Durable consumer is in use
2021-05-12 05:20:14,826 [0.1:50251#61613] DEBUG ProtocolConverter - Exception detail
javax.jms.JMSException: Durable consumer is in use
at org.apache.activemq.broker.region.TopicRegion.removeSubscription(TopicRegion.java:220)
at org.apache.activemq.broker.region.RegionBroker.removeSubscription(RegionBroker.java:457)
at org.apache.activemq.broker.BrokerFilter.removeSubscription(BrokerFilter.java:119)
at org.apache.activemq.advisory.AdvisoryBroker.removeSubscription(AdvisoryBroker.java:396)
at org.apache.activemq.broker.BrokerFilter.removeSubscription(BrokerFilter.java:119)
at org.apache.activemq.broker.BrokerFilter.removeSubscription(BrokerFilter.java:119)
at org.apache.activemq.broker.BrokerFilter.removeSubscription(BrokerFilter.java:119)
at org.apache.activemq.broker.TransportConnection.processRemoveSubscription(TransportConnection.java:419)
at org.apache.activemq.command.RemoveSubscriptionInfo.visit(RemoveSubscriptionInfo.java:81)
at org.apache.activemq.broker.TransportConnection.service(TransportConnection.java:331)
at org.apache.activemq.broker.TransportConnection$1.onCommand(TransportConnection.java:200)
at org.apache.activemq.transport.MutexTransport.onCommand(MutexTransport.java:45)
at org.apache.activemq.transport.AbstractInactivityMonitor.onCommand(AbstractInactivityMonitor.java:301)
at org.apache.activemq.transport.stomp.StompTransportFilter.sendToActiveMQ(StompTransportFilter.java:97)
at org.apache.activemq.transport.stomp.ProtocolConverter.sendToActiveMQ(ProtocolConverter.java:179)
at org.apache.activemq.transport.stomp.ProtocolConverter.onStompUnsubscribe(ProtocolConverter.java:714)
at org.apache.activemq.transport.stomp.ProtocolConverter.onStompCommand(ProtocolConverter.java:251)
at org.apache.activemq.transport.stomp.StompTransportFilter.onCommand(StompTransportFilter.java:85)
at org.apache.activemq.transport.TransportSupport.doConsume(TransportSupport.java:83)
at org.apache.activemq.transport.tcp.TcpTransport.doRun(TcpTransport.java:233)
at org.apache.activemq.transport.tcp.TcpTransport.run(TcpTransport.java:215)
at java.lang.Thread.run(Thread.java:748)
2021-05-12 05:20:14,827 [0.1:50251#61613] TRACE ProtocolConverter - Command that caused the error: UNSUBSCRIBE
activemq.subscriptionName:channel_manager_delivery_reports
receipt:1
id:1
Any suggestion on how to remove the durable subscription properly via stompit.
You first need to disconnect the connection which the durable subscriber is using. This deactivates the subscription and will prevent the JMSException: Durable consumer is in use you're seeing.
Then you need to reconnect using the same client-id header value which you used on your CONNECT frame for the connection used to subscribe.
Then need to pass the activemq.subscriptionName header in the UNSUBSCRIBE frame just like you did for the SUBSCRIBE frame, e.g.:
amqSubscription.unsubscribe({"activemq.subscriptionName": "channel_manager_delivery_reports"})
To be clear, this was resolved awhile back via AMQ-1890. You can see the corresponding source code which checks for the header and removes the subscription. You can also see the unit test which subscribes, disconnects, reconnects, and unsubscribes the durable subscription.
If you still have trouble then it may be worth turning on STOMP trace logging in the broker using these instructions to ensure the UNSUBSCRIBE frame is being received with the expected activemq.subscriptionName header.
Related
I have a nodeJS application that listens to Azure ServiceBus and would like to correlate all my logs under the same operationId for each message that it handles.
Because this is not a HttpRequest nor am I running an Azure Function app, I believe I do not have the automatic correlation from AppInsights and thus need to do it manually.
I am trying to use the SDK to create a context, log a Trace event, then end the context within the scope of each service bus message received.
The following is what I have so far, but results in a run time error.
/**
* Handles the incoming message from the service bus topic.
* #param messageReceived The incoming received message from the subscribed topic.
*/
public async handleMessage(messageReceived: ServiceBusReceivedMessage) {
const correlationContext = appInsights.startOperation({ traceId: '123', spanId: 'abc' } as SpanContext, 'myASBListener')
appInsights.wrapWithCorrelationContext(() => {
const traceTelem: TraceTelemetry = {
message: 'Processing service bus message',
properties: {
body: messageReceived.body,
},
}
telemetryClient.trackTrace(traceTelem)
}, correlationContext)
}
I have an azure function which makes a promise based http post request and gets a response; now I want to send this response to a service bus and to a different event hub (the azure function is being triggered by a different event hub).
function says it has been executed successfully in the case of event hub, but no events are being sent.
In the case of service bus I am getting this error NamespaceConnectionString should not contain EntityPath.
module.exports = async function (context, eventHubMessages) {
context.log(`JavaScript eventhub trigger function called for message array ${eventHubMessages}`);
var completeData = '';
eventHubMessages.forEach((message, index) => {
context.log(`Processed message ${message}`);
completeData = message;
});
var output = '';
const axios = require('axios');
try {
const response = await axios.post('http://fake-endpoint',
{ data-json : completeData
})
context.log(`statusCode: ${response.statusCode}`);
context.log(response.data);
output += response.data;
var time = new Date().toString();
context.log('Event Hub message created at: ', time);
context.bindings.outputEventHubMessage = out;
context.bindings.outputSbMsg = out;
context.done()
return response.data; // or return a custom object using properties from response
} catch (error) {
// If the promise rejects, an error will be thrown and caught here
context.done(error);
}
};
Expected output: successful execution; data available on service bus and event hub to receive.
Actual output: Error: NamespaceConnectionString should not contain EntityPath.
As the error message says, you need to look at your connection string and remove the EntityPath variable. This is included if you copy the connection string when viewing a specific topic or queue as opposed to copying it from the main Service Bus blade.
Endpoint=sb://{servicebus-name}.servicebus.windows.net/;SharedAccessKeyName=test-queue-sender;SharedAccessKey={SharedAccessKey}=;EntityPath=test-queue;
vs
Endpoint=sb://{servicebus-name}.servicebus.windows.net/;SharedAccessKeyName=test-queue-sender;SharedAccessKey={SharedAccessKey};
I created a Service Bus Queue following the tutorial in Microsoft Documentation. I can send and receive messages, however, only half of my messages make it through. Literally half, only the even ones.
I tried changing the message frequency but it doesn't change anything. It doesn't matter if I send a message every 3 seconds or 3 messages per second, I only get half of them on the other end.
I have run the example code in all the possible languages and I have tried using the REST API and batch messaging but no dice.
I also tried using Azure Functions with the specific trigger for Service Bus Queues.
This is the receiving function code:
module.exports = async function(context, mySbMsg) {
context.log('JavaScript ServiceBus queue trigger function processed message', mySbMsg);
context.done();
};
And this is the send function code:
module.exports = async function (context, req) {
context.log('JavaScript HTTP trigger function processed a request.');
var azure = require('azure-sb');
var idx = 0;
function sendMessages(sbService, queueName) {
var msg = 'Message # ' + (++idx);
sbService.sendQueueMessage(queueName, msg, function (err) {
if (err) {
console.log('Failed Tx: ', err);
} else {
console.log('Sent ' + msg);
}
});
}
var connStr = 'Endpoint=sb://<sbnamespace>.servicebus.windows.net/;SharedAccessKeyName=<keyname>;SharedAccessKey=<key>';
var queueName = 'MessageQueue';
context.log('Connecting to ' + connStr + ' queue ' + queueName);
var sbService = azure.createServiceBusService(connStr);
sbService.createQueueIfNotExists(queueName, function (err) {
if (err) {
console.log('Failed to create queue: ', err);
} else {
setInterval(sendMessages.bind(null, sbService, queueName), 2000);
}
});
};
I expect to receive most of the sent messages (specially in this conditions of no load at all) but instead I only receive 50%.
My guess is that the reason is that you are only listening to one of 2 subscriptions on the topic and it is set up to split the messages between subscriptions. This functionality is used to split workload to multiple services. You can read about topics here: https://learn.microsoft.com/en-us/azure/service-bus-messaging/service-bus-messaging-overview
and
https://learn.microsoft.com/en-us/azure/service-bus-messaging/topic-filters
Here sort description from above links:
"Partitioning uses filters to distribute messages across several existing topic subscriptions in a predictable and mutually exclusive manner. The partitioning pattern is used when a system is scaled out to handle many different contexts in functionally identical compartments that each hold a subset of the overall data; for example, customer profile information. With partitioning, a publisher submits the message into a topic without requiring any knowledge of the partitioning model. The message then is moved to the correct subscription from which it can then be retrieved by the partition's message handler."
To check this you can see if your service bus have partitioning turned on or any other filters.Turning partitioning off should do the trick in your case I think.
I'm trying to write a cloud function which sends a push notification to an iOS device. The logs say that sendToDevice was successful. But my device isn't receiving any notifications. Neither Xcode nor Cloud Functions are showing any errors. How can I diagnose this problem?
My cloud function takes a registration token from the realtime database. This token is saved to the database during the didRegisterForRemoteNotificationsWithDeviceToken function in the ios app, confirming that the front end is registering for remote notifications. The app has been given permission to show notifications and the push notification capabilities have been enabled in Xcode.
This block of code comes from my cloud function (Node.js):
// This snapshot was taken from the realtime database
// Xcode logs confirmed that this function is receiving the correct key
const notificationKey = userSnapshot.child("notificationKey").val();
const payload = {
notification: {
title: 'Test Notification Title',
body: 'Test Notification Body',
sound: 'default',
badge: '1'
}
};
return admin.messaging().sendToDevice(notificationKey, payload).then(function (response) {
console.log("Successfully sent message: ", JSON.stringify(response));
return;
}).catch(function (error) {
console.log("Error sending message: ", error);
return;
});
When calling the cloud function above, the logs showed this console log (Id numbers truncated):
"Successfully sent message: {"results":[{"messageId":"0:154/* ... */"}],"canonicalRegistrationTokenCount":0,"failureCount":0,"successCount":1,"multicastId":576/* ... */}"
But my test device (iPhone 7) hasn't received any notifications. My app has the following delegate functions (Swift 4):
func userNotificationCenter(_ center: UNUserNotificationCenter, willPresent notification: UNNotification, withCompletionHandler completionHandler: #escaping (UNNotificationPresentationOptions) -> Void) {
print("Notification will present: \(notification.request.content.userInfo)")
}
func userNotificationCenter(_ center: UNUserNotificationCenter, didReceive response: UNNotificationResponse, withCompletionHandler completionHandler: #escaping () -> Void) {
print("Notification received: \(response.notification.request.content.userInfo)")
}
Neither print statement is appearing in Xcode's output. The only relevant print statements found are the ones I included in didRegisterForRemoteNotificationsWithDeviceToken. My APNs certificate is apparently still valid and has not expired.
The issue was simply that my podfile was missing:
pod 'Firebase/Messaging'
That allowed my didReceiveRemoteNotification to receive the notification payloads from Firebase cloud function. Then once I added UNUserNotificationCenter.current().delegate = self to the AppDelegate, the UNUserNotificationCenterDelegate functions worked as intended.
Strange how the missing pod didn't give me any compiler errors.
I'm trying to write an azure function that supports 'retries' or future calls based on a service bus queue. It seems that the output bindings don't support any brokerProperties in the payload, is this correct or am I simply doing it wrong?
I'm able to do future calls in the queue with the following:
const azure = require('azure-sb');
const moment = require('moment');
const scheduled_time = moment().utc().add(5, 'm').format('M/D/YYYY H:mm:ss A');
const msg =
{
body: "Testing",
brokerProperties: {
ScheduledEnqueueTimeUtc: scheduled_time
}
};
sbService.sendQueueMessage(queueName, msg, function (err) {
if (err) {
console.log('Failed Tx: ', err);
} else {
console.log('Sent ' + msg);
}
});
However, simply passing the same msg object to the output binding the brokerProperties seem to be ignored. I HAVE confirmed that the function output binding works in general (properly configured).
context.done(null,
{
body: "Testing",
brokerProperties: {
ScheduledEnqueueTimeUtc: scheduled_time
}
});
Is it possible to leverage output bindings to do this or do I really need to add azure-sb and all this code for such a simple parameter? Is there a better way to call an azure function in the future?
The Node SDK docs don't even include ScheduledEnqueueTimeUtc property so it's been impossible to find any info in the docs.
It is does not seems to be supported ATM, see this Github Issue:
Enhancement: Add Scheduled Queue Messages to Service Bus Binding Documentation