How to create a durable publisher/subscriber topic using the AMQP.Net Lite library in .NET Core with clientId and subscriber name and topic name - activemq

I am new to ActiveMQ, but I tried and am able to create a durable publisher, but I am not able to set Client Id, because I am not finding any properties with client Id and am even unable to find in Google. It will be great help if I will get some sample code.
Note:
Not with the NMS protocol. I am using AMQP.Net Lite with ActiveMQ in the .NET Core Web API for creating a durable publisher/subscriber with ClientId.

In order to create a durable subscription to ActiveMQ or ActiveMQ Artemis your client needs to do a couple things.
Set a unique "client-id" for the client using the AMQP 'ContainerId' property which can be seen in the code below. The client must use that same container ID every time it connects and recovers it's durable subscription.
Create a new Session.
Create a new Receiver for the address (in this case Topic) that you want to subscribe to. The Source of a durable subscription need to have the address set to a Topic address (in ActiveMQ this is topic://name). The Source also needs the expiray policy set to NEVER, the Source must also have the terminus durability state set to UNSETTLED_STATE, and the distribution mode set to COPY.
Once the Receiver is created then you can either set an onMessage handler in start or call receive to consume messages (assuming you've granted credit for the broker to send you any).
using System;
using Amqp;
using Amqp.Framing;
using Amqp.Types;
using Amqp.Sasl;
using System.Threading;
namespace aorg.apache.activemq.examples
{
class Program
{
private static string DEFAULT_BROKER_URI = "amqp://localhost:5672";
private static string DEFAULT_CONTAINER_ID = "client-1";
private static string DEFAULT_SUBSCRIPTION_NAME = "test-subscription";
private static string DEFAULT_TOPIC_NAME = "test-topic";
static void Main(string[] args)
{
Console.WriteLine("Starting AMQP durable consumer example.");
Console.WriteLine("Creating a Durable Subscription");
CreateDurableSubscription();
Console.WriteLine("Attempting to recover a Durable Subscription");
RecoverDurableSubscription();
Console.WriteLine("Unsubscribe a durable subscription");
UnsubscribeDurableSubscription();
Console.WriteLine("Attempting to recover a non-existent durable subscription");
try
{
RecoverDurableSubscription();
throw new Exception("Subscription was not deleted.");
}
catch (AmqpException)
{
Console.WriteLine("Recover failed as expected");
}
Console.WriteLine("Example Complete.");
}
// Creating a durable subscription involves creating a Receiver with a Source that
// has the address set to the Topic name where the client wants to subscribe along
// with an expiry policy of 'never', Terminus Durability set to 'unsettled' and the
// Distribution Mode set to 'Copy'. The link name of the Receiver represents the
// desired name of the Subscription and of course the Connection must carry a container
// ID uniqure to the client that is creating the subscription.
private static void CreateDurableSubscription()
{
Connection connection = new Connection(new Address(DEFAULT_BROKER_URI),
SaslProfile.Anonymous,
new Open() { ContainerId = DEFAULT_CONTAINER_ID }, null);
try
{
Session session = new Session(connection);
Source source = CreateBasicSource();
// Create a Durable Consumer Source.
source.Address = DEFAULT_TOPIC_NAME;
source.ExpiryPolicy = new Symbol("never");
source.Durable = 2;
source.DistributionMode = new Symbol("copy");
ReceiverLink receiver = new ReceiverLink(session, DEFAULT_SUBSCRIPTION_NAME, source, null);
session.Close();
}
finally
{
connection.Close();
}
}
// Recovering an existing subscription allows the client to ask the remote
// peer if a subscription with the given name for the current 'Container ID'
// exists. The process involves the client attaching a receiver with a null
// Source on a link with the desired subscription name as the link name and
// the broker will then return a Source instance if this current container
// has a subscription registered with that subscription (link) name.
private static void RecoverDurableSubscription()
{
Connection connection = new Connection(new Address(DEFAULT_BROKER_URI),
SaslProfile.Anonymous,
new Open() { ContainerId = DEFAULT_CONTAINER_ID }, null);
try
{
Session session = new Session(connection);
Source recoveredSource = null;
ManualResetEvent attached = new ManualResetEvent(false);
OnAttached onAttached = (link, attach) =>
{
recoveredSource = (Source) attach.Source;
attached.Set();
};
ReceiverLink receiver = new ReceiverLink(session, DEFAULT_SUBSCRIPTION_NAME, (Source) null, onAttached);
attached.WaitOne(10000);
if (recoveredSource == null)
{
// The remote had no subscription matching what we asked for.
throw new AmqpException(new Error());
}
else
{
Console.WriteLine(" Receovered subscription for address: " + recoveredSource.Address);
Console.WriteLine(" Recovered Source Expiry Policy = " + recoveredSource.ExpiryPolicy);
Console.WriteLine(" Recovered Source Durability = " + recoveredSource.Durable);
Console.WriteLine(" Recovered Source Distribution Mode = " + recoveredSource.DistributionMode);
}
session.Close();
}
finally
{
connection.Close();
}
}
// Unsubscribing a durable subscription involves recovering an existing
// subscription and then closing the receiver link explicitly or in AMQP
// terms the close value of the Detach frame should be 'true'
private static void UnsubscribeDurableSubscription()
{
Connection connection = new Connection(new Address(DEFAULT_BROKER_URI),
SaslProfile.Anonymous,
new Open() { ContainerId = DEFAULT_CONTAINER_ID }, null);
try
{
Session session = new Session(connection);
Source recoveredSource = null;
ManualResetEvent attached = new ManualResetEvent(false);
OnAttached onAttached = (link, attach) =>
{
recoveredSource = (Source) attach.Source;
attached.Set();
};
ReceiverLink receiver = new ReceiverLink(session, DEFAULT_SUBSCRIPTION_NAME, (Source) null, onAttached);
attached.WaitOne(10000);
if (recoveredSource == null)
{
// The remote had no subscription matching what we asked for.
throw new AmqpException(new Error());
}
else
{
Console.WriteLine(" Receovered subscription for address: " + recoveredSource.Address);
Console.WriteLine(" Recovered Source Expiry Policy = " + recoveredSource.ExpiryPolicy);
Console.WriteLine(" Recovered Source Durability = " + recoveredSource.Durable);
Console.WriteLine(" Recovered Source Distribution Mode = " + recoveredSource.DistributionMode);
}
// Closing the Receiver vs. detaching it will unsubscribe
receiver.Close();
session.Close();
}
finally
{
connection.Close();
}
}
// Creates a basic Source type that contains common attributes needed
// to describe to the remote peer the features and expectations of the
// Source of the Receiver link.
private static Source CreateBasicSource()
{
Source source = new Source();
// These are the outcomes this link will accept.
Symbol[] outcomes = new Symbol[] {new Symbol("amqp:accepted:list"),
new Symbol("amqp:rejected:list"),
new Symbol("amqp:released:list"),
new Symbol("amqp:modified:list") };
// Default Outcome for deliveries not settled on this link
Modified defaultOutcome = new Modified();
defaultOutcome.DeliveryFailed = true;
defaultOutcome.UndeliverableHere = false;
// Configure Source.
source.DefaultOutcome = defaultOutcome;
source.Outcomes = outcomes;
return source;
}
}
}

Related

What is the use case of BrokerService in ActiveMQ and how to use it correctly

I am new about ActiveMQ. I'm trying to study and check how it works by checking the example code provided by Apache at this link:-
http://activemq.apache.org/how-should-i-implement-request-response-with-jms.html
public class Server implements MessageListener {
private static int ackMode;
private static String messageQueueName;
private static String messageBrokerUrl;
private Session session;
private boolean transacted = false;
private MessageProducer replyProducer;
private MessageProtocol messageProtocol;
static {
messageBrokerUrl = "tcp://localhost:61616";
messageQueueName = "client.messages";
ackMode = Session.AUTO_ACKNOWLEDGE;
}
public Server() {
try {
//This message broker is embedded
BrokerService broker = new BrokerService();
broker.setPersistent(false);
broker.setUseJmx(false);
broker.addConnector(messageBrokerUrl);
broker.start();
} catch (Exception e) {
System.out.println("Exception: "+e.getMessage());
//Handle the exception appropriately
}
//Delegating the handling of messages to another class, instantiate it before setting up JMS so it
//is ready to handle messages
this.messageProtocol = new MessageProtocol();
this.setupMessageQueueConsumer();
}
private void setupMessageQueueConsumer() {
ActiveMQConnectionFactory connectionFactory = new ActiveMQConnectionFactory(messageBrokerUrl);
Connection connection;
try {
connection = connectionFactory.createConnection();
connection.start();
this.session = connection.createSession(this.transacted, ackMode);
Destination adminQueue = this.session.createQueue(messageQueueName);
//Setup a message producer to respond to messages from clients, we will get the destination
//to send to from the JMSReplyTo header field from a Message
this.replyProducer = this.session.createProducer(null);
this.replyProducer.setDeliveryMode(DeliveryMode.NON_PERSISTENT);
//Set up a consumer to consume messages off of the admin queue
MessageConsumer consumer = this.session.createConsumer(adminQueue);
consumer.setMessageListener(this);
} catch (JMSException e) {
System.out.println("Exception: "+e.getMessage());
}
}
public void onMessage(Message message) {
try {
TextMessage response = this.session.createTextMessage();
if (message instanceof TextMessage) {
TextMessage txtMsg = (TextMessage) message;
String messageText = txtMsg.getText();
response.setText(this.messageProtocol.handleProtocolMessage(messageText));
}
//Set the correlation ID from the received message to be the correlation id of the response message
//this lets the client identify which message this is a response to if it has more than
//one outstanding message to the server
response.setJMSCorrelationID(message.getJMSCorrelationID());
//Send the response to the Destination specified by the JMSReplyTo field of the received message,
//this is presumably a temporary queue created by the client
this.replyProducer.send(message.getJMSReplyTo(), response);
} catch (JMSException e) {
System.out.println("Exception: "+e.getMessage());
}
}
public static void main(String[] args) {
new Server();
}
}
My confusion about the messageBrokerUrl = "tcp://localhost:61616"; You know ActiveMQ service is running on port 61616 by default. Why does this example chooses same port. If I try to run the code thows eception as:
Exception: Failed to bind to server socket: tcp://localhost:61616 due to: java.net.BindException: Address already in use: JVM_Bind
Perhaps if I change the port number, I can execute the code.
Please let me know why it is like this in the example and how to work with BrokerService.
The BrokerService in this example is trying to create an in memory ActiveMQ broker for use in the example. Given the error you are seeing I'd guess you already have an ActiveMQ broker running on the machine that is bound to port 61616 as that's the default port and thus the two are conflicting. You could either stop the external broker and run the example or modify the example to not run the embedded broker and just rely on your external broker instance.
Embedded brokers are great for unit testing or for creating examples that don't require the user to have a broker installed and running.

RabbitMQ .NET Client and connection timeouts

I'm trying to test the AutomaticRecoveryEnabled property of the RabbitMQ ConnectionFactory. I'm connecting to a RabbitMQ instance on a local VM and on the client I'm publishing messages in a loop. The problem is if I intentionally break the connection, the client just waits forever and doesn't time out. How do I set the time out value? RequestedConnectionTimeout doesn't appear to have any effect.
I'm using the RabbitMQ client 3.5.4
Rudimentary publish loop:
// Client is a wrapper around the RabbitMQ client
for (var i = 0; i < 1000; ++i)
{
// Publish sequentially numbered messages
client.Publish("routingkey", GetContent(i)));
Thread.Sleep(100);
}
The Publish method inside the wrapper:
public bool Publish(string routingKey, byte[] body)
{
try
{
using (var channel = _connection.CreateModel())
{
var basicProps = new BasicProperties
{
Persistent = true,
};
channel.ExchangeDeclare(_exchange, _exchangeType);
channel.BasicPublish(_exchange, routingKey, basicProps, body);
return true;
}
}
catch (Exception e)
{
_logger.Log(e);
}
return false;
}
The connection and connection factory:
_connectionFactory = new ConnectionFactory
{
UserName = _userName,
Password = _password,
HostName = _hostName,
Port = _port,
Protocol = Protocols.DefaultProtocol,
VirtualHost = _virtualHost,
// Doesn't seem to have any effect on broken connections
RequestedConnectionTimeout = 2000,
// The behaviour appears to be the same with or without these included
// AutomaticRecoveryEnabled = true,
// NetworkRecoveryInterval = TimeSpan.FromSeconds(10),
};
_connection = _connectionFactory.CreateConnection();
It appears this is a bug in version 3.5.4. Version 3.6.3 does not wait indefinitely.

Send message has been delivering successfully, But when i'm trying to get messages from agent no messages be presented in agent

I'm new to JMS+OPenMq + Glassfish , please give me up by make successful send message and receive messages....
I have created two different servlet programs and i have deployed in galssfish server....Here i'm sending message successfully , but consumer has not able to consuming messages ......
producer :
Properties p = new Properties();
p.put("java.naming.factory.initial","com.sun.enterprise.naming.SerialInitContextFactory");
p.put("java.naming.factory.url.pkgs","com.sun.enterprise.naming");
p.put("java.naming.provider.url", "iiop://localhost:3700");
InitialContext jndiContext = new InitialContext(p);
TopicConnectionFactory connectionFactory = (TopicConnectionFactory) jndiContext.lookup("jms/HQTapicConnectionFactory");
Topic topic = (Topic) jndiContext.lookup("jms/HqDestTopic");
System.out.println(topic.getTopicName());
TopicConnection connection = (TopicConnection) connectionFactory.createTopicConnection();
System.out.println(connection.toString());
TopicSession session = connection.createTopicSession(true, Session.AUTO_ACKNOWLEDGE); //createSession(false, Session.AUTO_ACKNOWLEDGE);
TopicPublisher publisher = session.createPublisher(topic);
ObjectMessage message = session.createObjectMessage();
ArrayList<Employee> employeeList= new ArrayList<Employee>();
Employee employee = null;
for (int i = 0; i < 5; i++) {
employee = new Employee();
employee.setEmpid((100+i));
employee.setName("devid"+i);
employeeList.add(employee);
}
System.out.println(employeeList.size());
message.setObject(employeeList);
publisher.send(message);
Consumer:
public void onMessage(Message message){
ObjectMessage objectMessage= (ObjectMessage) message;
try{
System.out.println("Received the following message: ");
Object object = objectMessage.getObject();
if(object instanceof ArrayList){
ArrayList arrayList = (ArrayList)object;
for (int i = 0; i < arrayList.size(); i++) {
Object object1 = arrayList.get(i);
if(object1 instanceof Employee){
Employee employee = (Employee)object1;
System.out.println(employee.getEmpid());
System.out.println(employee.getName());
System.out.println();
}
}
}
}
catch (JMSException e)
{
e.printStackTrace();
}
}
I'm not able to receiving messages ,
Please help me to proper configure for broker in glassfish server.
,...appreciate for your replay
If your consumer is in a servlet, it will only catch the messages which are sent in the same moment of time (quite unlikely) - you are using topics which do not buffer by default.
Either use queues (instead of topics) or write a stand-alone program which is permanently running (and thus listening/eceiving). Normally topic listeners do not make much sense in a servlet.

How to receive messages for a correlationid from RabbitMQ using Spring AMQP

I went through the API of RabbitTemplate. It provides only receive method which gets the message from queue. However there is no way to get a message with a particular correlation id. Can you please help me understand what I am missing here.
Currently, I am using JMS API's from ActiveMQ to receive messages using the following code which createConsumer with message selector. Looking to do the same with Spring AMQP with RabbitMQ:
private ObjectMessage receiveMessage(final String readQueue, final UUID correlationId, final boolean isBroadcastMessage, final int readTimeout) throws JMSException
{
final ActiveMQConnectionFactory connectionFactory = this.findConnectionFactory(readQueue);
Connection connection = null;
Session session = null;
MessageConsumer consumer = null;
ObjectMessage responseMessage = null;
try
{
connection = connectionFactory.createConnection();
connection.start();
session = connection.createSession(false, Session.AUTO_ACKNOWLEDGE);
Destination destination = session.createQueue(readQueue);
consumer = session.createConsumer(destination, "correlationId = '" + correlationId + "'");
final Message message = consumer.receive(readTimeout);
}
finally
{
if (consumer != null)
{
consumer.close();
}
if (session != null)
{
session.close();
}
if (connection != null)
{
connection.close();
}
}
return responseMessage;
}
You are using a messageSelector string in JMS; RabbitMQ/AMQP does not have an equivalant.
Instead, each consumer gets its own queue and you use a direct or topic exchange in the broker to do the routing. I suggest you take a look at the tutorials on the rabbitmq web site and topics.
If you are using the correlationId for request/reply processing, consider using the inbuilt sendAndReceive or convertSendAndReceive methods in the template. See the reference documentation for more information.

First subscriber not called with Redis MQ

I am using the solution from the ServiceStack Re-usability use case project.
To this solution I have added a new console app which contains the code below.
With the original Re-usability use-case project, when an EmailMessage is published it was handled by a subscriber which sent an email (i.e. SMessageService.Any(EmailMessage request).
When I run the console app, which means I have two applications that are subscribing to the EmailMessage, only the new console app receives the message.
I have the following:
My console app is:
class Program
{
static void Main(string[] args)
{
var subscriberHost = new SubscriberHost();
subscriberHost.Init();
Console.WriteLine("Waiting of publishing
to happen on EmailMessage as we are subscribing to it...");
Console.ReadLine();
}
}
public class SubscriberHost : AppHostHttpListenerBase
{
private RedisMqServer mqHost;
public SubscriberHost()
:base("Subscriber console",typeof(EmailMessageEventHandler).Assembly)
{
}
public override void Configure(Container container)
{
var redisFactory = new PooledRedisClientManager("localhost:6379");
mqHost = new RedisMqServer(redisFactory, retryCount:2);
mqHost.RegisterHandler<EmailMessage>((message) =>
{
var emailMessage = message.GetBody();
Console.WriteLine(emailMessage.To);
Console.WriteLine(emailMessage.Subject);
Console.WriteLine(emailMessage.Body);
return new SMessageReceipt {
Type = "not used",
To = "test",
From = "Reusability",
RefId = "1,"
};
});
// mqHost.RegisterHandler<EmailMessage>(ServiceController.ExecuteMessage);
mqHost.Start();
}
}
I was expecting both subscribers to receive the EmailMessage but only the new console app is receiving it. Why isn't the other subscriber receiving the message?
The client code that does the publishing has not been modified.
What I have shown above is using Redis MQ, and for the multiple subscribers problem I was testing I need the Redis Pub/Sub.
For MQ, a subscriber takes the message off the queue to process. Once processed, that is it.
For Pub/Sub, there could be many subscribers and each will receive a copy of the message.
I hope this helps others.