I am using a RabbitMQ DefaultConsumer
public void init () {
DefaultConsumer dc = new DefaultConsumer(channel);
dc.addShutdownListener(this);
}
handleShutdownSignal() {
//TODO: Recreate channel associated with this consumer
}
Once there is an error with the channel, and a shutdownsignal is sent, how can I reinitialize the channel since the consumer is already dependent on the previous channel it was using ?
You will need to re establish the connection, create a channel and then create a new consumer. If the connection is still ok you may be able to use that to get a new channel.
Related
I am trying to build a group chat message using WebSocket using spring-webflux and rector-netty. I am new to reactor-netty framework and even after reading multiple articles posts I couldn't figure out if it is possible to throttle a client from sending too many messages in reactor-netty framework.
public class ServerWebSocketHandler implements WebSocketHandler {
Map<String, WebSocketSession> sessions = new HashMap<>();
ConcurrentLinkedQueue<String> messages = new ConcurrentLinkedQueue<>();
public ServerWebSocketHandler() {
// logic to start a thread which will drain all the messages in the queue to all the sessions periodically
}
#Override
public Mono<Void> handle(WebSocketSession session) {
System.out.println("Client connected: " + session);
sessions.put(session.getId(), session);
Flux<String> stringFlux = session.receive()
.map(WebSocketMessage::getPayloadAsText)
.map(String::toLowerCase)
.doOnNext(m -> messages.offer(m))
.doFinally(m -> System.out.printf("Client %s Disconnected due to %s\n", session, m));
return stringFlux.then();
}
}
ReactorNettyWebSocketSession is the implementation which is used in this case and it doesn't seem to expose any methods to have any control over the inbound/outbound, like marking the inbound as not readable or something. Is it possible to throttle/block a client from sending too many messages. If it is not possible, I am thinking that creating a bounded queue for each session and receive and then ignore/drop the incoming message in application layer.
I have a websocket implementation using redis messaging operation on webflux. And what it does is it listens to topic and returns the values via websocket endpoint.
The problem I have is each time a user sends a message via websocket to the endpoint it seems a brand new redis subscription is made, resulting in the accumulation of subscribers on the redis message topic and the websocket responses are increased with the number of redis topic message subscribtions as well (example user sends 3 messages, redis topic subscriptions are increased to three, websocket connection responses three times).
Would like to know if there is a way to reuse the same subscription to the messaging topic so it would prevent multiple redis topic subscriptions.
The code I use is as follows:
Websocket Handler
public class SendingMessageHandler implements WebSocketHandler {
private final Gson gson = new Gson();
private final MessagingService messagingService;
public SendingMessageHandler(MessagingService messagingService) {
this.messagingService = messagingService;
}
#Override
public Mono<Void> handle(WebSocketSession session) {
Flux<WebSocketMessage> stringFlux = session.receive()
.map(WebSocketMessage::getPayloadAsText)
.flatMap(inputData ->
messagingService.playGame(inputData)
.map(data ->
session.textMessage(gson.toJson(data))
)
);
return session.send(stringFlux);
}
}
Message Handling service
public class MessagingService{
private final ReactiveRedisOperations<String, GamePubSub> reactiveRedisOperations;
public MessagingService(ReactiveRedisOperations<String, GamePubSub> reactiveRedisOperations) {
this.reactiveRedisOperations = reactiveRedisOperations;
}
public Flux<Object> playGame(UserInput userInput){
return reactiveRedisOperations.listenTo("TOPIC_NAME");
}
}
Thank you in advance.
Instead of using ReactiveRedisOperations, MessageListener is the way to go here. You can register a listener once, and use the following as the listener.
data -> session.textMessage(gson.toJson(data))
The registration should happen only once at the beginning of the connection. You can override void afterConnectionEstablished(WebSocketSession session) of SendingMessageHandler to accomplish this. That way a new subscription created per every new Websocket connection, per every message.
Also, don't forget to override afterConnectionClosed, and unsubscribe from the redis topic, and clean up the listener within it.
Instructions on how to use MessageListener.
We are using spring integration application for data receiption from gps devices. For current configuration we are able to receive data from device also respose sent back to device through same connection
current configuration is as
#SpringBootApplication
#IntegrationComponentScan
public class SpringIntegrationApplication extends SpringBootServletInitializer{
private Integer TIMEOUT=1000*60*10;
#Value("${TCP_PORT}")
private Integer TCP_PORT;
public static void main(String[] args) throws IOException {
ConfigurableApplicationContext ctx = SpringApplication.run(SpringIntegrationApplication.class, args);
System.in.read();
ctx.close();
}
#Bean
TcpNetServerConnectionFactory cf(){
TcpNetServerConnectionFactory connectionFactory=new TcpNetServerConnectionFactory(TCP_PORT);
connectionFactory.setSerializer(new CustomSerializerDeserializer());
connectionFactory.setDeserializer(new CustomSerializerDeserializer());
connectionFactory.setSoTimeout(TIMEOUT);
return connectionFactory;
}
#Bean
TcpInboundGateway tcpGate(){
TcpInboundGateway gateway=new TcpInboundGateway();
gateway.setConnectionFactory(cf());
gateway.setRequestChannel(requestChannel());
gateway.setRequestTimeout(TIMEOUT);
return gateway;
}
#Bean
public MessageChannel requestChannel(){
return new DirectChannel();
}
}
and message end point
#MessageEndpoint
public class Echo {
#ServiceActivator(inputChannel="requestChannel")
public byte[] echo(byte[] in,#SuppressWarnings("deprecation") #Header("ip_address") String ip){
//here we receive packet data in bytes from gps device
return "".getBytes();//string will contains expected result for device.
}
Above configuartion works fine for one way communication. but we want to implement two way communication. What we want after connection established between server and device we want to send message explicitely.To send command through server we dont know ip and port of device, so how can we send command through server to connected device.
I am trying following solution
created oubound channel adapter
#Bean
public TcpSendingMessageHandler tcpSendingMessageHandler() {
System.out.println("Creating outbound adapter");
TcpSendingMessageHandler outbound = new TcpSendingMessageHandler();
return outbound;
}
then created gateway for explicite message send, this will be called from service where we want to send data explicitely
#MessagingGateway(defaultRequestChannel="toTcp")
public static interface tcpSendService {
public byte [] send(String string);
}
After calling gate way following service activator invoked where we are setting connection ip and port, these ip and ports will be from connection established while receiving data from device
#ServiceActivator(inputChannel="toTcp", outputChannel="fromTcp")
public String send(String in){
System.out.println(new String(in));
TcpNetClientConnectionFactory factory = new TcpNetClientConnectionFactory(ip_extracted_from_inbound_connection, port_extarcted_from_inbound_connection);
factory.start();
tcpSendingMessageHandler.setConnectionFactory(factory);
return in;
}
// for ip and port extraction i am using following service which is inbound sevice
#ServiceActivator(inputChannel="requestChannel")
public byte[] echo(byte[] in,#Header("ip_address") String ip){
System.out.println(new String(in)+ " ; IP : "+ip);
for (String connectionId : factory.getOpenConnectionIds()) {
if(!lastConection.contains(ip))
lastConection = connectionId;
}
return "hello".getBytes();
}
For service activator i am setting new TcpNetClientConnectionFactory every time service called. Ip and port are extracted from TcpNetServerConnectionFactory. whenever device connects with server i am saving its connection ip and port, using these ip and port for data transmission through server but i am getting connection timeout issue.
Kindly help me out and suggest me a solution over it.
Thank you.
Replace the gateway with a pair of Collaborating Outbound and Inbound Channel Adapters.
In order to send arbitrary messages to a connection, you must set the ip_connectionId header.
The challenge, though, is how to direct the reply to the gateway. You would need to capture the replyChannel header from the request and, when a reply is received for that ip_connectionId, set the replyChannel headers.
This will only work, though, if you have only one request/reply outstanding to each device at a time, unless there is some data in the reply that can be used to correlate it to a request.
Another challenge is race conditions, where the device and the server initiate a request at the same time. You would need to look at data in the inbound message to see if it's a request or reply.
As I know ActiveMQ has a feature called AUTO Acknowledge that actually inform the broker that message has been received (not acknowledging the producer).
I want to know if it is possible to send acknowledgement from consumer to producer in ActiveMQ or RabbitMQ. then I want to handle the acknowledgment message in producer and if it wouldn't receive acknowledge then sending the message again to the consumer.
You want to perform a synchronous usecase over an asynchronous medium.
In RabbitMQ's case you can use RPC, as described here - https://www.rabbitmq.com/tutorials/tutorial-six-python.html
and
https://www.rabbitmq.com/direct-reply-to.html
Please notice that even authors advise to avoid it:
When in doubt avoid RPC. If you can, you should use an asynchronous pipeline - instead of RPC-like blocking, results are asynchronously pushed to a next computation stage.
RabbitMQ Java client provides auto-acking through com.rabbitmq.client.Channel.basicConsume.
At least for ActiveMQ - this is built in. You have to turn it on in activemq.xml
<policyEntry queue=">" advisoryForConsumed="true"/>
Simply listen the advisory topic for the queue you want to monitor consumed messages for. Then you can extract message id:s and what not to "tick off" outstanding requests.
For a complete end-to-end acknowledgement, I recommend something more custom. I.e. your producer-app should listen to some "response" queue that receives responses about the status of the produced message. I.e. if processing failed - you may want to know why etc..
Anyway, here is some code with a producer that also listens to acknowledgements from ActiveMQ.
public void run() throws Exception {
ActiveMQConnectionFactory cf = new ActiveMQConnectionFactory("tcp://localhost:61616");
conn = cf.createConnection();
sess = conn.createSession(false, Session.AUTO_ACKNOWLEDGE);
Destination dest = sess.createQueue("duck");
MessageConsumer mc = sess.createConsumer(AdvisorySupport.getMessageConsumedAdvisoryTopic(dest));
mc.setMessageListener(this);
conn.start();
MessageProducer mp = sess.createProducer(sess.createQueue("duck"));
mp.send(sess.createTextMessage("quack"));
}
public void onMessage(Message msg) {
try {
String msgId = msg.getStringProperty("orignalMessageId");
System.out.println("Msg: " + msgId + " consumed");
} catch ( Exception e) {
e.printStackTrace();
}
}
My client is using one WCF service which is throwing an exception
(EXCEPTION: The communication object, System.ServiceModel.Channels.ServiceChannel, cannot be used for communication because it is in the Faulted state).
All subsequent calls throwing an same exception.
I read on internet that client need to close()/Abort() channel, this will solve the problem. is it completely right?
Also I am using customer serviceChannel factory provided by service developers. When I create channel it does not show the close and abort methods. So how do I get these close and abort methods when I create custom service channel instance on client side?
Assuming that you have a proxy instance that implements the IClientChannel interface, here is a way (hopefully the right way) to use it.
IClientChannel clientChannel = (IClientChannel)proxy;
bool success = false;
try
{
// do something with the proxy
clientChannel.Close();
success = true;
}
finally
{
if (!success)
{
clientChannel.Abort();
}
}
You may also want to check this. You can wrap your operations using a shared class or function.