Vertx amqp client doesnt reconnect on broker down - amqp

I am trying to write a program to pull messages from a message broker via Vert.x AMQP client. I want to make the program try to reconnect on broker down. Currently if I turn off the broker container, the program doesn't react. Below is my code.. What do I miss ?
public class BrokerConnector {
public void consumeEventsQueue() {
AmqpClientOptions options = new AmqpClientOptions()
.setHost("localhost")
.setPort(5672)
.setUsername("")
.setPassword("");
AmqpClient amqpClient = AmqpClient.create(options);
amqpClient.connect(con -> {
if (con.failed()) {
System.out.println("Unable to connect to the broker");
} else {
System.out.println("Connection succeeded");
}
});
amqpClient.createReceiver("MY_QUEUE",
done -> {
if (done.failed()) {
System.out.println("Unable to create receiver");
} else {
AmqpReceiver receiver = done.result();
receiver.handler(msg -> {
System.out.println("Received " + msg.bodyAsString());
});
}
}
);
}
}

To my knowledge (and from peeking at the source) the vertx AMQP client doesn't have automatic client reconnect so it seems quite normal that on loss of connection you application is failing. The client exposes an exception handler that you can hook and recreate your client resources from when the connection drops. There are some clients for AMQP that do have automatic reconnect built in like Qpid JMS or the Qpid protonj2 client.

Related

Closing connection to client in Vertx without restarting the server

I am using Vertx for my backend.
This is a TCP server and the server is connected to several clients.
I am trying to disconnect the client when reaching a certain condition.
The code that I used is as follows.
vertx.createNetServer(new NetServerOptions().setIdleTimeout(601))
.connectHandler(socket -> {
Instant start = Instant.now();
writerId = socket.writeHandlerID();
log.info("[TCPServerVerticle] first Tcp Server Instance Id : {}", serverId);
socket.handler(input -> { // input을 받았을 때 실행
writerId = socket.writeHandlerID();
SocketAddress localAddr = socket.localAddress();
SocketAddress remoteAddr = socket.remoteAddress();
central.setWriterId(writerId);
byte[] bytes = input.getBytes();
String inputString = Utils.byteArrayToHex(bytes);
central.inputMessage(inputString, writerId, vertx, localAddr, remoteAddr, versionMap).onComplete(ok -> {
String result = ok.result();
if (result.equals("nak")) {
socket.close();
}
});
});
When I execute this code, when the condition for "nak" is met, the server seems to restart and not the client.
Would there be a way to close the connection to the client without restarting the server?
Thank you in advance

Ubuntu Mosquitto broker websocket is not working

I'm new at IoT & MQTT communication protocol. I'm trying to connect my broker which runs at Amazon Ec2 from my Vue web app via Websockets. I have started mosquitto with:
root#ip-xxx-xx-xx-xx:~# mosquitto -c /etc/mosquitto/conf.d/default.conf
1618518468: mosquitto version 1.6.7 starting
1618518468: Config loaded from /etc/mosquitto/conf.d/default.conf.
1618518468: Opening ipv4 listen socket on port 1883.
1618518468: Opening ipv6 listen socket on port 1883.
1618518468: Opening websockets listen socket on port 9001.
/etc/mosquitto/conf.d/default.conf file contains:
listener 1883
protocol mqtt
allow_anonymous true
listener 9001
protocol websockets
allow_anonymous true
My test js file is:
var mqtt = require('mqtt');
var count =0;
var client = mqtt.connect("mqtt://xx.xxx.xxx.xxx",{clientId:"mqttjs01"});
console.log("connected flag " + client.connected);
//handle incoming messages
client.on('message',function(topic, message, packet){
console.log("message is "+ message);
console.log("topic is "+ topic);
});
client.on("connect",function(){
console.log("connected "+ client.connected);
})
//handle errors
client.on("error",function(error){
console.log("Can't connect" + error);
process.exit(1)});
//publish
function publish(topic,msg,options){
console.log("publishing",msg);
if (client.connected == true){
client.publish(topic,msg,options);
}
count+=1;
if (count==2) //ens script
clearTimeout(timer_id); //stop timer
client.end();
}
//////////////
var options={
retain:true,
qos:1};
var topic="acs";
var message="test message";
var topic_list=["topic2","topic3","topic4"];
var topic_o={"topic22":0,"topic33":1,"topic44":1};
console.log("subscribing to topics");
client.subscribe(topic,{qos:0}); //single topic
client.subscribe(topic_list,{qos:1}); //topic list
client.subscribe(topic_o); //object
var timer_id=setInterval(function(){publish(topic,message,options);},5000);
//notice this is printed even before we connect
console.log("end of script");
But I'm getting this error:
New client connected from 176.xxx.xxx.xx as mqttjs01 (p2, c1, k60).
1618518546: Socket error on client mqttjs01, disconnecting.
I have installed libwebsockets, I have tried with various mosquitto versions. Current version is: 1.6.7.
Is there any problem with my client or broker? How can I fix this?
At the end of the publish() function the if statement is missing enclosing braces so it doesn't do what you think it does.
function publish(topic,msg,options){
console.log("publishing",msg);
if (client.connected == true){
client.publish(topic,msg,options);
}
count+=1;
if (count==2) //ens script
clearTimeout(timer_id); //stop timer
client.end();
}
Lets fix the indentation so we can see more clearly.
function publish(topic,msg,options){
console.log("publishing",msg);
if (client.connected == true){
client.publish(topic,msg,options);
}
count+=1;
if (count==2) //ens script
clearTimeout(timer_id); //stop timer
client.end();
}
As you can see client.end() will ALWAYS be called when ever publish() is called. If you only want to publish twice you need to wrap the 2 statements in the braces (this is not python where whitespace has meaning)
if (count==2) { //ens script
clearTimeout(timer_id); //stop timer
client.end();
}
You really should indent all your code properly it will make it much easier to read and to spot errors like this.
Also as #JDAllen mentioned you are not making use of the WebSocket connection, unless this code is running in the browser, where the sandbox will force it to be a WebSocket connection even if you specify mqtt:// as the schema in the URL, and you will have to include the port number to make it actually connect. e.g.
ws://xxx.xxx.xxx.xxx:9001

How to detect if RSocket connection is successfull?

I have the following program through which I can detect the connection failure i.e doBeforeRetry.
Can someone tell me how to detect the successful connection or reconnection. I want to integrate a Health Check program that monitors this connection, but I am unable to capture the event that informs the connections is successfull.
Thanks
requester = RSocketRequester.builder()
.rsocketConnector(connector -> {
connector.reconnect(Retry
.fixedDelay(Integer.MAX_VALUE,Duration.ofSeconds(1))
.doBeforeRetry(e-> System.out.println("doBeforeRetry===>"+e))
.doAfterRetry(e-> System.out.println("doAfterRetry===>"+e))
);
connector.payloadDecoder(PayloadDecoder.ZERO_COPY);
}
).dataMimeType(MediaType.APPLICATION_CBOR)
.rsocketStrategies(strategies)
.tcp("localhost", 7999);
I achieved the detection of successful connection or reconnection with the following approach.
Client Side (Connection initialization)
Mono<RSocketRequester> requester = Mono.just(RSocketRequester.builder()
.rsocketConnector(
// connector configuration goes here
)
.dataMimeType(MediaType.APPLICATION_CBOR)
.setupRoute("client-handshake")
.setupData("caller-name")
.tcp("localhost", 7999)));
One the server side
#ConnectMapping("client-handshake")
public void connect(RSocketRequester requester, #Payload String callerName) {
LOG.info("Client Connection Handshake: [{}]", callerName);
requester
.route("server-handshake")
.data("I am server")
.retrieveMono(Void.class)
.subscribe();
}
On the client side, when I receive the callback on the below method, I detect the connection is successfull.
#MessageMapping("server-handshake")
public Mono<ConsumerPreference> handshake(final String response){
LOG.info("Server Connection Handshake received : Server message [{}]", response.getCallerName());
connectionSuccess.set(true);
return Mono.empty();
}else{
throw new InitializationException("Invalid response message received from Server");
}
}
Additionally, created a application level heartbeat to ensure, the liveliness of the connection.
If you want to know if it's actually healthy, you should probably have a side task that is polling the health of the RSocket, by sending something like a custom ping protocol to your backend. You could time that and confirm that you have a healthy connection, record latencies and success/failures.

how to use websocket in jhipster microservices architecture?

i try to trigger the websocket to push notification to client , but jhipster microservices doesn't support websocket in non-gateway service,
so i try to send the websocket message in a Gateway controller, and call this controller in other services with feignclient, but it seems doesn't work to call gateway controller in other service.
#PostMapping("/notify")
#Timed
public ResponseEntity<String> sendNotification(#RequestBody TodoDTO todoDTO) {
String dest = "/topic/notify/";
if (StringUtils.isNotEmpty(todoDTO.getTo())) {
dest += todoDTO.getTo();
} else if (StringUtils.isNotEmpty(todoDTO.getToShopId())) {
dest += todoDTO.getToShopId();
} else if (StringUtils.isNotEmpty(todoDTO.getToParentShopId())) {
dest += todoDTO.getToParentShopId();
}
messagingTemplate.convertAndSend(dest, todoDTO);
return ResponseEntity.ok("SUCCESS");
}
does anyone know the best practice when using websocket in jhipster microservices ?

Subscribing to a removed queue with spring-websocket and RabbitMQ broker (Queue NOT_FOUND)

I have a spring-websocket (4.1.6) application on Tomcat8 that uses a STOMP RabbitMQ (3.4.4) message broker for messaging. When a client (Chrome 47) starts the application, it subscribes to an endpoint creating a durable queue. When this client unsubscribes from the endpoint, the queue will be cleaned up by RabbitMQ after 30 seconds as defined in a custom made RabbitMQ policy. When I try to reconnect to an endpoint that has a queue that was cleaned up, I receive the following exception in the RabbitMQ logs: "NOT_FOUND - no queue 'position-updates-user9zm_szz9' in vhost '/'\n". I don't want to use an auto-delete queue since I have some reconnect logic in case the websocket connection dies.
This problem can be reproduced by adding the following code to the spring-websocket-portfolio github example.
In the container div in the index.html add:
<button class="btn" onclick="appModel.subscribe()">SUBSCRIBE</button>
<button class="btn" onclick="appModel.unsubscribe()">UNSUBSCRIBE</button>
In portfolio.js replace:
stompClient.subscribe("/user/queue/position-updates", function(message) {
with:
positionUpdates = stompClient.subscribe("/user/queue/position-updates", function(message) {
and also add the following:
self.unsubscribe = function() {
positionUpdates.unsubscribe();
}
self.subscribe = function() {
positionUpdates = stompClient.subscribe("/user/queue/position-updates", function(message) {
self.pushNotification("Position update " + message.body);
self.portfolio().updatePosition(JSON.parse(message.body));
});
}
Now you can reproduce the problem by:
Launch the application
click unsubscribe
delete the position-updates queue in the RabbitMQ console
click subscribe
Find the error message in the websocket frame via the chrome devtools and in the RabbitMQ logs.
reconnect logic in case the websocket connection dies.
and
no queue 'position-updates-user9zm_szz9' in vhost
Are fully different stories.
I'd suggest you implement "re-subscribe" logic in case of deleted queue.
Actually that is how STOMP works: it creates auto-deleted (generated) queue for the subscribe and yes, it is removed on the unsubscrire.
See more info in the RabbitMQ STOMP Adapter Manual.
From other side consider to subscribe to the existing AMQP queue:
To address existing queues created outside the STOMP adapter, destinations of the form /amq/queue/<name> can be used.
The problem is Stomp won't recreate the queue if it get's deleted by the RabbitMQ policy. I worked around it by creating the queue myself when the SessionSubscribeEvent is fired.
public void onApplicationEvent(AbstractSubProtocolEvent event) {
if (event instanceof SessionSubscribeEvent) {
MultiValueMap nativeHeaders = (MultiValueMap)event.getMessage().getHeaders().get("nativeHeaders");
List destination = (List)nativeHeaders.get("destination");
String queueName = ((String)destination.get(0)).substring("/queue/".length());
try {
Connection connection = connectionFactory.newConnection();
Channel channel = connection.createChannel();
channel.queueDeclare(queueName, true, false, false, null);
} catch (IOException e) {
e.printStackTrace();
}
}
}

Resources