Rabbitmq's priority queue mechanism has been tested and will not take effect until the producer is started to publish the message before the consumer is started. How to solve this problem?
**Code snippet**
consumer:
Map<String,Object> args = new HashMap<String,Object>();
args.put("x-max-priority", 10);
channel.queueDeclare(TEST_PRIORITY_QUEUE, true, false, false,args);
//omit... ...
DeliverCallback deliverCallback= (consumerTag, delivery) -> {
try {
String message = new String(delivery.getBody(), "UTF-8");
System.out.println("message="+message);
Thread.sleep(20*1000);
}catch (Exception e) {
e.printStackTrace();
}finally {
channel.basicAck(delivery.getEnvelope().getDeliveryTag(), false);
}
};
//... ...
producer:
for (int i = 0; i <20; i++) {
String messagelow = "lowLevelMsg";
channel.basicPublish(TEST_EXCHANGE_direct,
"prikey",
new BasicProperties.Builder().priority(1).build(),
messagelow.getBytes());
}
String messagehigh = "HigherLevelMsg";
channel.basicPublish(TEST_EXCHANGE_direct,
"prikey",
new BasicProperties.Builder().priority(9).build(),
messagehigh.getBytes());
Related
I'm new in RabbitMQ, and I using Publisher Confirm to ensure that message is delivered successfully.
But I'm facing a strange behavior, which is when I publish a message and take a sequenceNumber the function channel.BasicAcks is fired n times which n is the DeliveryTag, so if the DeliveryTag for this message is 5 the function channel.BasicAcks is fired 5 times?!!
And here is my code:
IModel channel = _customRabbitMQ.GetChannel();
IBasicProperties properties = _customRabbitMQ.GetBasicProperties();
ulong sequenceNumber = channel.NextPublishSeqNo;
message.SequenceNumber = sequenceNumber.ToString();
_context.Messages.Update(message);
await _context.SaveChangesAsync();
try
{
_customRabbitMQ.AddOutstandingConfirm(sequenceNumber, message.Id);
channel.BasicPublish(message.ExchangeName, message.RoutingKey, properties, Encoding.UTF8.GetBytes(JsonSerializer.Serialize(message)));
Console.WriteLine("Message Published");
}
catch (Exception ex)
{
// TODO: log to custom logger here
Console.WriteLine($"Error => {ex.Message}");
}
channel.BasicAcks += async (sender, ea) =>
{
try
{
Console.WriteLine("Message Confirmed");
using var scope = _provider.CreateScope();
var _context = scope.ServiceProvider.GetService<Data.DataContext>();
var _customRabbitMQ = scope.ServiceProvider.GetService<CustomRabbitMQ>();
Guid messageId = _customRabbitMQ.GetOutstandingConfirm(ea.DeliveryTag);
Message message = await _context.Messages.Where(m => m.Id == messageId).FirstOrDefaultAsync();
message.Status = MessageStatuses.INQUEUE;
_context.Messages.Update(message);
await _context.SaveChangesAsync();
_customRabbitMQ.RemoveOutstandingConfirm(ea.DeliveryTag);
}
catch (Exception ex)
{
Console.WriteLine($"Error => {ex.Message}");
}
};
channel.BasicNacks += (sender, ea) =>
{
Console.WriteLine("Message Not Confirmed");
_customRabbitMQ.RemoveOutstandingConfirm(ea.DeliveryTag);
};
}
So, why this happening and how to stop it and make it confirm only one time?
Thanks in advance.
I have publish messages with some priority set for a single consumer(i.e single consumer that may receive messages according to message priority).
What i want is to get that messages and print them according to the message priority on the consumer side. Hey guys Help me out in this !
public class Send extends Thread {
int priority;
String name = "";
String app_type = "";
private static final String EXCHANGE_NAME = "topic_exchange";
public void run()
{
ConnectionFactory connFac = new ConnectionFactory();
connFac.setHost("localhost");
try {
Connection conn = connFac.newConnection();
Channel channel = conn.createChannel();
channel.exchangeDeclare(EXCHANGE_NAME,
BuiltinExchangeType.TOPIC);
for(int j=1; j<=200; j++)
{
randomWait();
int random = (int)(Math.random() * 10 + 1);
String routingKey = j+"."+"update"+"."+app_type;
String msg = name;
channel.basicPublish(EXCHANGE_NAME, routingKey, new
AMQP.BasicProperties.Builder()
.contentType("text/plain")
.deliveryMode(2)
.priority(priority)
.build(),
msg.getBytes("UTF-8"));
System.out.println("Sent " + routingKey + " : " + msg +
" "+" Priority : "+priority);
}
channel.close();
conn.close();
} catch (IOException ex) {
Logger.getLogger(Send.class.getName()).log(Level.SEVERE, null,
ex);
System.out.println("Exception1 :--"+ex);
} catch (TimeoutException ex) {
Logger.getLogger(Send.class.getName()).log(Level.SEVERE, null,
ex);
System.out.println("Exception 2:--"+ex);
}
}
void randomWait()
{
try {
Thread.currentThread().sleep((long)(200*Math.random()));
} catch (InterruptedException x) {
System.out.println("Interrupted!");
}
}
public static void main(String[] args) {
// TODO code application logic here
Send test1 = new Send();
test1.name = "Hello ANDROID";
test1.app_type = "ANDROID";
test1.priority = 10;
Send test2 = new Send();
test2.name = "Hello ANDROID";
test2.app_type = "ANDROID";
test2.priority = 5;
test1.start();
test2.start();
}
}
In the above code I have use thread to pass the priority and message value and started the both the thread at the same time to publish messages with different priorities. I have set the priority value in the AMQ Builder.
The queue has to be configured to support priority.
I am using ActiveMQ in my app. My question is how to delete messages that ı consumed successfully from kahadb. Because if it is not deleted, my db.data file is growing up constantly.
Here is my consumer;
ActiveMQConnectionFactory connectionFactory = new ActiveMQConnectionFactory("tcp://localhost:8182");
Connection connection = connectionFactory.createConnection();
connection.start();
Session session = connection.createSession(false, Session.AUTO_ACKNOWLEDGE);
Destination destination = session.createQueue("TEST.FOO");
MessageConsumer consumer = session.createConsumer(destination);
MessageListener listner = new MessageListener() {
int count = 0;
public void onMessage(Message message) {
if (message instanceof ObjectMessage) {
ObjectMessage objectMessage = (ObjectMessage) message;
ResponseDuration responseDuration = null;
try {
responseDuration = (ResponseDuration) objectMessage.getObject();
System.out.println("Received Time : " + new Date() + "Received: " + responseDuration.toString());
} catch (JMSException e1) {
// TODO Auto-generated catch block
e1.printStackTrace();
}
try {
ResponseDurationOperations.insertResponseDurations(responseDuration);
count++;
System.out.println("Count = " + count);
} catch (SQLException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
};
consumer.setMessageListener(listner);
Seems that ActiveMQ has a different meaning for what it means with persistance than you (and me as well).
Persistence is defined not to persist for ever but just to make you safe from message loss when you restart the server. See this
One option for you could be to switch off the persistence. See here.
For example by this way:
ActiveMQConnectionFactory("vm://localhost?broker.persistent=false");
I'm running through a Glassfish web process and I need a non-container managed class (EJBUserManager) to be able to receive messages from a MessageDrivenBean. The class has the javax.jms.Queues and connection factories and I can write to the Queues. The queue sends to a MessageDrivenBean (AccountValidatorBean) that receives the code correctly, and then writes back a message. But the EJBUserManager attempts to read from the queue and never receives the message.
#Override
public boolean doesExist(String username) throws FtpException {
LOGGER.finer(String.format("Query if username %s exists", username));
QueueConnection queueConnection = null;
boolean doesExist = false;
try {
queueConnection = connectionFactory.createQueueConnection();
final UserManagerMessage userManagerMessage =
new UserManagerMessage(UserManagerQueryCommands.VALIDATE_USER, username);
final Session session = queueConnection.createSession(false, Session.AUTO_ACKNOWLEDGE);
final ObjectMessage objectMessage = session.createObjectMessage(userManagerMessage);
session.createProducer(accountValidatorQueue).send(objectMessage);
session.close();
queueConnection.close();
queueConnection = connectionFactory.createQueueConnection();
final QueueSession queueSession =
queueConnection.createQueueSession(false, Session.AUTO_ACKNOWLEDGE);
LOGGER.finest(String.format("Right before doesExist receive for username %s", username));
final Message firstAttemptMessage = queueSession.createConsumer(userManagerQueue).receive(3000);
final Message message = firstAttemptMessage != null ?
firstAttemptMessage : queueSession.createConsumer(userManagerQueue).receiveNoWait();
LOGGER.finest(String.format("Right after doesExist receive for username %s", username));
LOGGER.finest(String.format("Is the message null: %b", message != null));
if (message != null && message instanceof StreamMessage) {
final StreamMessage streamMessage = (StreamMessage) message;
doesExist = streamMessage.readBoolean();
}
} catch (JMSException e) {
e.printStackTrace();
} finally {
if (queueConnection != null) {
try {
queueConnection.close();
} catch (JMSException e) {
e.printStackTrace();
}
}
}
return doesExist;
}
The above is the code from the EJBUserManager. Now, it can send to the accountValidatorQueue. It just never receives from the userManagerQueue
Here's the code for the AccountValidatorBean
private void validateUser(final String username) {
QueueConnection queueConnection = null;
final String doctype = doctypeLookupDAO.getDocumentTypeForUsername(username);
LOGGER.finest(String.format("Doctype %s for username %s", doctype, username));
try {
queueConnection = queueConnectionFactory.createQueueConnection();
final Session session = queueConnection.createSession(false, Session.AUTO_ACKNOWLEDGE);
//final StreamMessage message = session.createStreamMessage();
//message.clearBody();
//message.writeBoolean(doctype != null);
//message.reset();
final ObjectMessage message = session.createObjectMessage(Boolean.valueOf(doctype != null));
final MessageProducer messageProducer =
session.createProducer(userManagerQueue);
LOGGER.finest(String.format("Queue name %s of producing queue", userManagerQueue.getQueueName()));
messageProducer.send(message);
LOGGER.finest(String.format("Sending user validate message for user %s", username));
messageProducer.close();
session.close();
} catch (JMSException e) {
e.printStackTrace();
} finally {
if (queueConnection != null) {
try {
queueConnection.close();
} catch (JMSException e1) {
e1.printStackTrace();
}
}
}
}
Fixed. I needed to call QueueConnection.start() to consume messages from the queue.
How do i keep the java mail transport object alive or connected.
I have written this in my code in a simple class file inside a web application : -
#Resource(name = "myMailServer")
private Session mailSession;
Transport transport ;
public boolean sendMail(String recipient, String subject, String text) {
boolean exe = false;
Properties p = new Properties();
String username = "someone#gmail.com";
String password = "password";
InitialContext c = null;
try
{
c = new InitialContext();
mailSession = (javax.mail.Session) c.lookup("java:comp/env/myMailServer");
}
catch(NamingException ne)
{
ne.printStackTrace();
}
try
{
Message msg = new MimeMessage(mailSession);
msg.setRecipients(Message.RecipientType.TO,InternetAddress.parse(recipient, false));
msg.setSubject(subject);
msg.setText(text);
msg.setHeader("MIME-Version" , "1.0" );
msg.setHeader("Content-Type" , "text/html" );
msg.setHeader("X-Mailer", "Recommend-It Mailer V2.03c02");
msg.saveChanges();
//Transport.send(msg);
if(transport == null) {
transport = mailSession.getTransport("smtps");
System.out.println("" + transport.isConnected());
if(!transport.isConnected()) {
transport.connect(username, password);
}
}
transport.sendMessage(msg, msg.getAllRecipients());
exe = true;
}
catch (AddressException e)
{
e.printStackTrace();
exe = false;
}
catch (MessagingException e)
{
e.printStackTrace();
exe = false;
}
finally {
/*try {
if(transport != null)
transport.close();
}
catch(MessagingException me) {
me.printStackTrace();
}
catch(Exception e) {
e.printStackTrace();
}*/
}
return exe;
}
the full code here
Now everytime i run this code it takes some time to connect with the mail server
and the line
System.out.println("" + transport.isConnected());
prints a false
How do i retain the object transport as it does gets null and into the block
if(transport == null) {
or the transport object remains connected...
Thanks
Pradyut
the code should be....
with a static initialization of transport object
without any problems but can be good with a function
static Transport getTransport() method
#Resource(name = "myMailServer")
private Session mailSession;
static Transport transport ;
public boolean sendMail(String recipient, String subject, String text) {
boolean exe = false;
Properties p = new Properties();
String username = "someone#gmail.com";
String password = "password";
InitialContext c = null;
try
{
c = new InitialContext();
mailSession = (javax.mail.Session) c.lookup("java:comp/env/myMailServer");
}
catch(NamingException ne)
{
ne.printStackTrace();
}
try
{
Message msg = new MimeMessage(mailSession);
msg.setRecipients(Message.RecipientType.TO,InternetAddress.parse(recipient, false));
msg.setSubject(subject);
msg.setText(text);
msg.setHeader("MIME-Version" , "1.0" );
msg.setHeader("Content-Type" , "text/html" );
msg.setHeader("X-Mailer", "Recommend-It Mailer V2.03c02");
msg.saveChanges();
//Transport.send(msg);
if(transport == null) {
transport = mailSession.getTransport("smtps");
}
if(!transport.isConnected()) {
transport.connect(username, password);
}
transport.sendMessage(msg, msg.getAllRecipients());
exe = true;
}
catch (AddressException e)
{
e.printStackTrace();
exe = false;
}
catch (MessagingException e)
{
e.printStackTrace();
exe = false;
}
finally {
/*try {
if(transport != null)
transport.close();
}
catch(MessagingException me) {
me.printStackTrace();
}
catch(Exception e) {
e.printStackTrace();
}*/
}
return exe;
}
Thanks
Regards
Pradyut