How to use redis pub-sub channel with expire? - redis

Does redis support pub-sub channels with expiration ttl?
We are using redis-cli with vertx : https://vertx.io/docs/vertx-redis-client/java/
redisClient.subscribe(channel, result -> {
if (!result.succeeded()) {
log.warn("subscribe failed msgId={}", msgId, result.cause());
message.fail(500, String.format("retrieve conversation %s failure", msgId));
return;
}
// redisClient.psetex(channel, 28000, ... handler -> {}); ???
is this the way to go?
PS: I m referring to the TTL of the channel reference itself.

Related

Sending message to queue in SingleActiveConsumer mode in MassTransit

I have registered receive endpoint in SingleActiveConsumer mode. However I can't find a way to send a message directly to queue by using sendEndpoint. I receive following error:
The AMQP operation was interrupted: AMQP close-reason, initiated by Peer, code=406, text='PRECONDITION_FAILED - inequivalent arg 'x-single-active-consumer' for queue 'test' in vhost '/': received none but current is the value 'true' of type 'bool'',
I tried setting header "x-single-active-consumer"=true by using bus configurer:
var bus = Bus.Factory.CreateUsingRabbitMq(cfg =>
{
cfg.Host("localhost", "/", h =>
{
h.Username("guest");
h.Password("guest");
});
cfg.ConfigureSend(a => a.UseSendExecute(c => c.Headers.Set("x-single-active-consumer", true)));
});
and directly on sendEndpoint:
await sendEndpoint.Send(msg, context => {
context.Headers.Set("x-single-active-consumer", true);
});
If you want to send directly to a receive endpoint in MassTransit, you can use the short address exchange:test instead, which will send to the exchange without trying to create/bind the queue to the exchange with the same name. That way, you decouple the queue configuration from the message producer.
Or, you could just use Publish, and let the exchange bindings route the message to the receive endpoint queue.

BizTalk receiving from RabbitMQ

I'm new to RabbitMQ but I have now installed onto a Windows server and have a couple of demo console apps (C#) that happily write to a read from a queue.
The following code works to pull messages from a queue called "RabbitPoCQueue_2" on the local server:
string queueName = "RabbitPoCQueue_2";
var factory = new ConnectionFactory();
bool keepGoing = true;
factory.HostName = "127.0.0.1";
try
{
using (var connection = factory.CreateConnection())
using (var channel = connection.CreateModel())
{
const bool durable = false;
channel.QueueDeclare(queueName, durable, false, false, null);
System.Console.WriteLine(" [*] Waiting for messages.");
while (keepGoing)
{
var consumer = new EventingBasicConsumer(channel);
consumer.Received += (model, ea) =>
{
var body = ea.Body;
var message = Encoding.UTF8.GetString(body);
System.Console.WriteLine(" [x] Received {0}", message);
};
channel.BasicConsume(queue: queueName,
autoAck: true,
consumer: consumer);
channel.BasicGet(queue: queueName, autoAck: true);
System.Console.WriteLine("Press Y to continue or any other key to exit");
keepGoing = System.Console.ReadKey().Key == ConsoleKey.Y;
}
}
}
I now need to configure a BizTalk (2016 FP3 CU5) receive location to do the same. I have ensured I've stopped the console receiver and that I have messages sat on the queue for BizTalk to collect.
I followed the article https://social.technet.microsoft.com/wiki/contents/articles/7401.biztalk-server-and-rabbitmq.aspx
Problem is, when I start the receive location, I get no errors but nothing is received.
The config for the WCF receive location can be seen below:
and here:
and here's a pic from the RabbitMQ management console showing messages sat on the queue:
When I look in the RabbitMQ log file, I see 2 rows on starting the receive location. I see 3 rows when starting the .Net console app (using RabbitMQ API), as shown below - first 2 rows are from BizTalk, last 3 from the console app:
2019-08-28 16:17:45.693 [info] <0.13361.2> connection <0.13361.2> ([::1]:16807 -> [::1]:5672): user 'guest' authenticated and granted access to vhost '/' ** Start of Receive location
2019-08-28 16:19:57.958 [info] <0.13452.2> accepting AMQP connection <0.13452.2> (127.0.0.1:17173 -> 127.0.0.1:5672)
2019-08-28 16:19:58.026 [info] <0.13452.2> connection <0.13452.2> (127.0.0.1:17173 -> 127.0.0.1:5672): user 'guest' authenticated and granted access to vhost '/' ** Receive from command line
2019-08-28 18:56:26.267 [info] <0.13452.2> closing AMQP connection <0.13452.2> (127.0.0.1:17173 -> 127.0.0.1:5672, vhost: '/', user: 'guest')
2019-08-28 18:56:39.815 [info] <0.17923.2> accepting AMQP connection <0.17923.2> (127.0.0.1:41103 -> 127.0.0.1:5672)
Can anyone spot where I went wrong?

Prevent MassTransit from creating a RabbitMQ exchange for a consumer host

Is it possible to configure MassTransit to not create a RabbitMQ exchange for a consumer host? My RabbitMQ user has not enough rights to declare an exchange at the host where the consuming queue is located, so MassTransit fails to start with the following error:
Unhandled Exception: MassTransit.RabbitMqTransport.RabbitMqConnectionException:
Operation interrupted ---> RabbitMQ.Client.Exceptions.OperationInterruptedExcept
ion: The AMQP operation was interrupted: AMQP close-reason, initiated by Peer, c
ode=403, text="ACCESS_REFUSED - access to exchange '***' i
n vhost '***' refused for user '***'", classId=
40, methodId=10, cause=
Here is the code that I use:
var bus = Bus.Factory.CreateUsingRabbitMq(sbc =>
{
var host = sbc.Host(host: "***", port: 5671, virtualHost: "***", configure: configurator =>
{
configurator.UseSsl(sslConfigurator =>
{
sslConfigurator.Certificate = certificate;
sslConfigurator.UseCertificateAsAuthenticationIdentity = true;
sslConfigurator.ServerName = "***";
});
});
sbc.ReceiveEndpoint(host, "***", endpointConfigurator =>
{
endpointConfigurator.Consumer<UpdateCustomerConsumer>();
});
});

How do I have RabbitMQ redeliver unacknowledged messages?

I'm following the following tutorial to the letter:
https://www.rabbitmq.com/tutorials/tutorial-two-java.html.
I start the RabbitMQ server as such:
docker pull rabbitmq
docker run -d --hostname my-rabbit-host --name my-rabbit -p 5672:5672 rabbitmq:3
From the tutorial:
Using this code we can be sure that even if you kill a worker using
CTRL+C while it was processing a message, nothing will be lost. Soon
after the worker dies all unacknowledged messages will be redelivered.
I spawn two consumers, and when I CTRL+C one of them, the other running one does not receive the messages that were originally destined to the former consumer. How do I get the messages to be redelivered after CTRL+C'ing out of one of the consumers?
Edit: I'm now installing RabbitMQ via 'brew', but I'm still seeing the same issue.
brew update
brew install rabbitmq
/usr/local/sbin/rabbitmq-server &
There is no need to put sleep or anything like that in the consumer code. On the link you provided, search for paragraph starting with Manual message acknowledgments and look at the code there. The key thing is not to acknowledge the message. If you have autoACK flag set to true, then you can call anything you want, the message is acknowledged as soon as it is received. So simply don't set that flag, and also for testing you could comment out the line channel.basicAck(envelope.getDeliveryTag(), false); in order not to do manual ACK either. So when the consumer exits, the message will still be in the queue.
Strange, RabbitMQ works for me out of the box.
Step 1, I started RabbitMQ:
$ docker run -d --hostname my-rabbit-host --name my-rabbit -p 5672:5672 rabbitmq:3
$ docker ps
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
e0c3257b8b49 rabbitmq:3 "docker-entrypoint.s…" 18 minutes ago Up 14 minutes 4369/tcp, 5671/tcp, 25672/tcp, 0.0.0.0:5672->5672/tcp my-rabbit
Step 2, I published a message (By the way, I tried with Node.js. See Appendix below for source code):
$ node src/producer.js
Publisher: TODO 1st
Step 3, I started two consumers one after another (my consumer is designed, for the testing purpose, not to acknowledge, so RabbitMQ will never dequeue a message).
Consumer 1 will receive the message, while consumer 2 won't.
Consumer 1:
$ node src/consumer.js
Consumer: TODO 1st
Consumer 2:
$ node src/consumer.js
Step 4, when I stop consumer 1 by 'Ctrl + c', Consumer 2 will immediately receive the message from RabbitMQ:
Consumer 2:
$ node src/consumer.js
Consumer: TODO 1st
Conclusion: Basically, when setting up a consumer, we need to tell RabbitMQ not to dequeue the message until its acknowledgement has been received from the consumer. As a result, if consumer 1 is stopped for any reason before it has a chance to acknowledge the message, RabbitMQ will redeliver the message to consumer 2.
Appendix
src/producer.js
var q = 'tasks'
function bail (err) {
console.error(err)
process.exit(1)
}
// Publisher
function publisher (conn) {
conn.createChannel(onOpen)
function onOpen (err, ch) {
if (err != null) bail(err)
ch.assertQueue(q)
const msg = 'TODO 1st'
ch.sendToQueue(q, Buffer.from(msg), { persistent: true })
console.log('Publisher: ', msg)
}
}
require('amqplib/callback_api')
.connect('amqp://guest:guest#localhost', function (err, conn) {
if (err != null) bail(err)
publisher(conn)
})
src/consumer.js
var q = 'tasks'
function bail (err) {
console.error(err)
process.exit(1)
}
// Consumer
function consumer (conn) {
conn.createChannel(onOpen)
function onOpen (err, ch) {
if (err != null) bail(err)
ch.assertQueue(q)
ch.consume(q, function (msg) {
if (msg !== null) {
console.log('Consumer: ', msg.content.toString())
// Commented out the line below, so RabbitMQ never dequeues a message
// ch.ack(msg)
}
}, { noAck: false })
}
}
require('amqplib/callback_api')
.connect('amqp://guest:guest#localhost', function (err, conn) {
if (err != null) bail(err)
consumer(conn)
})

logstash rabbitmq output never posts to exchange

I've got logstash running, and successfully reading in a file
rabbitmq is running, I'm watching the log, and I can see the web interface
I've configured logstash to output to a rabbitmq exchange... I think!
Here's the problem: nothing ever gets posted to the exchange, as seen in the web interface.
Any ideas?
My output config:
output {
rabbitmq {
codec => plain
host => localhost
exchange => yomtvraps
exchange_type => direct
}
file { path => "/tmp/heartbeat-from-logstash.log" }
}
UPDATE: I'm watching the rabbit log with
tail -F /usr/local/var/log/rabbitmq/rabbit\#localhost.log
As it turns out, the problem was that there was no routing key set for the exchange and queue.
A working config is:
output {
rabbitmq {
codec => plain
host => localhost
exchange => yomtvraps
exchange_type => direct
key => yomtvraps
# these are defaults but you never know...
durable => true
port => 5672
user => "guest"
password => "guest"
}
}
Here's a sample receiver code (using ruby "Bunny")
require "bunny"
conn = Bunny.new(:automatically_recover => false)
conn.start
ch = conn.create_channel
q = ch.queue("yomtvraps")
exchange = ch.direct("yomtvraps", :durable => true)
begin
puts " [*] Waiting for messages. To exit press CTRL+C"
q.bind(exchange, :routing_key => "yomtvraps").subscribe(:block => true) do |delivery_info, properties, body|
puts " [x] Received #{body}"
end
rescue Interrupt => _
conn.close
exit(0)
end
you rabbitmq's parameter seems not enough, username,password and port have not been configured.
You can configure two outputs, one is to rabbitmq, the other is to file for vertifying the log's creation and log stash is ok.
pay attention to the logstash's version(log stash, rabbitmq plugin), it gave me lots of trouble in my trial before (log stash to another redis server etc).
You could debug rabbitmq's log.
ps -ef|grep erl you could find the log file's path in the arguments.
Be sure that rabbitmq's web manager plugin is enabled, and firewall is rightly configured, then open rabbitmq's web manager, ipaddress:15672
check the exchange's type is ok (in this case 'direct' may be a correct choice), your message consumer is configured ok, and your consumer's queue has been been bound to the exchange correctly.
try to post the message to your consumer through web manager and ensure consumer work well.
Monitor your queue when log stash push log into your consumer.