Uncommon TCP flow with Spring Integration - spring

I need suggestion how to implement, if it is possible, with the Spring integration the following TCP flow:
Only the server side is need.
The TCP server waits for the incoming connection
On connection of the client, server sends data to the client
Client replies with response
Server may reply immediately with the new data or wait for external application events to send new packages to the client.
In groovy the code could be demonstrated as follow:
def serverSocket = new ServerSocket(...)
def connSocket = serverSocket.accept()
connSocket.outputStream.write(...)
while(true) {
def readBuffer = new byte[256]
connSocket.inputStream.read(readBuffer)
if(needToSendBack(readBuffer)) {
connSocket.outputStream.write(...)
}
}
def sendByDemand(def data) {
connSocket.outputStream.write(data)
}
The method sendByDemand could be invoked from the separate thread.
Here is a list of problems which I marked for myself, which prevents me to implement it with the Spring Integration (2.x version):
As far as I understand, the standard "Service Activator" approach cannot work in this scenario, since it is "connection events" driven. So when the application decides to send the new data to the client it cannot use the Service Activator
I have no "On TCP connection" event. I found that version 3.0 comes with the events support in this area, but since I cannot upgrade to 3.0, I implemented the connection check with the interceptors on the connection factory. However, when I know that client is connected, trying using the Direct Channels to send message fails with "no subscribers" error.
If someone could post possible Spring configuration for this scenario or point to the similar flow example it may be very helpful.

Your use case is possible, but it would make your life easier if you could upgrade to 3.0.
'Dispatcher has no subscribers' means there is no consumer subscribed to that channel.
You need to show your configuration; you must use collaborating channel adapters for this (not a gateway).
You need to capture the connectionId of the connection when it is established, and use it to populate the ip_connectionId header so the outbound channel adapter knows which socket to which to write the message.

Related

Websocket from UI connecting to one of the instances for Spring boot application for streaming the data coming on a queue

I have an UI application (displays streaming) which makes a WebSocket connection to the Spring Boot microservice (multiple JVM'S) and this service forwards the request to one of the upstream servers and listens to the responses on a JMS queue coming from upstream server, which then response messages had to be returned to the socket.
Issue we are facing is since the socket is point to point, and the Spring Boot application is running on multiple instances which all are listening to the same JMS queue we are unable to serve the data back to the WebSocket when a message is received on a instance which the request to upstream wasn't made.
Here's the basic flow:
WebSocket -> instance1, instance2, instance3 -> Data provider
Instance1 made the request to data provider.
Data provider sends the data back to the queue
Instance 3 receives the message, but it doesn't have the socket connection to send the data back.
We had an interim solution using correlation id in JMS headers and selectors on the queue however now the data publisher is not able to provide the correlation id to depend on.
Does anybody have a better suggestion to address this?
Since you're using a request/reply pattern with JMS you must either use a correlation ID or a unique temporary queue for the response.
You indicated that, "the data publisher is not able to provide the correlation id to depend on." However, your application actually provides the correlation ID. The "data provider" in this case just needs to take it from the message it receives and put it into the response message. The process only requires 2 method calls by the "data provider" - javax.jms.Message.getJMSCorrelationID and javax.jms.Message.setJMSCorrelationID.
If the "data provider" can't do this then it's doubtful they will be able to accomplish the other option of using a unique temporary queue for the response. However, it's worth explaining in any case. When one of your "instance" servers sends the request message it first needs to use javax.jms.Session.createTemporaryQueue to create a temporary queue and then take the return parameter of that method and set it on the request message using javax.jms.Message.setJMSReplyTo. When the "data provider" receives the message they will get this value using javax.jms.Message.getJMSReplyTo and then send the response to this queue where the "instance" will then retrieve it.
These are the two generally accepted ways to implement a request/response pattern with JMS. I don't know of any other ways to implement such a pattern.

spring-amqp not work correctly when connections are blocked

I am using spring-amqp 1.4.4 and after queue contains too much messages and it is above watermark memory, RabbitTemplate receive method don't response if it was called after send method. It is wait indefinitely. And in spring xml I set reply-timeout="10" to rabbit:template. If i not call send method and simply call receive it work good. What's wrong?
template.convertAndSend("test message");
String msg = (String) template.receiveAndConvert("log.queue"); // receiveAndConvert not response
The rabbitmq guys recommend using separate connections for publishers and consumers, for exactly this reason.
The spring amqp CachingConnectionFactory shares a single connection for all users.
We are looking at providing an option to use two connections but, in the meantime, you can configure two connection factories (and templates), one for sends and the other for receives.

stomp message acknowledgement from client

I am using spring/stomp/websocket framework to notify users of messages asynchronously. I have done this successfully. However, I would be get ACK from the client so that some server side action can take place when this is done.
The flow is roughly as flows:
Service notifies a specific user about a decision and updates a record in the DB with status = "notified"
Client receives the message (using stompClient.subscribe(...))
Client acknowledges that the message was received.
The service "knows" that this message was acknowledged and updates the status to "ACK" in the DB.
stompClient.connect({login:'guest', passcode:'guest'},
function(frame) {
setConnected(true);
**var headers = {ack: 'client'};**
...
stompClient.subscribe('/user/guest/response',function(notification) {
//doSomething
}), **headers**);
}
In the service, the message is sent:
this.messagingTemplate.convertAndSendToUser(user, "/response",msg, map);
Is there a way to handle the client ACK on the server side?
Alternatively, I tried to do a
stompClient.send("/app/response/ack/"+messageId);
on the client, in the method that handles the subscription, but in vain.
Can someone please tell me what is standard way to handle acknowledgments? I have been struggling with this for a a couple of days and any thoughts would be very helpful.
Thanks!
Use the ACK frame as per spec. The server sends an ack:some_id header, the client uses that some_id in the ACK frame.
The answer is no for simple broker.
https://docs.spring.io/spring/docs/current/spring-framework-reference/html/websocket.html
The simple broker is great for getting started but supports only a
subset of STOMP commands (e.g. no acks, receipts, etc.), relies on a
simple message sending loop, and is not suitable for clustering. As an
alternative, applications can upgrade to using a full-featured message
broker.

Spring 4 websocket not closing on application shutdown

So this is a strange one. I have a basic Spring 4 websockets application running on Glassfish 4 using RabbitMQ for the message broker, nothing fancy. I was testing the durability of the websocket clients (one in java and one in javascript using stomp.js and socks.js) and noticed that when I undeployed the application from glassfish both clients would think the websocket was still up. For fun I added a recurring ping request from each client to the server to mimic a heartbeat. When the application is up the ping request works great and I get pong responses from the server, but when I undeploy the app from glassfish (to simulate a disconnect) I still get successful ping and pong messages from the server. It seems to me that when the application is undeployed it should send out disconnect messages to all connected clients which would invoke their reconnect logic to hit another server in the cluster. Has anyone seen similar behavior??? Thanks for any help!
I think I have this one figured out. I failed to set the heartbeat configuration on the STOMP connection. Once I set these values I began seeing heartbeats sent to the client by the server, and when I pulled the plug on the web socket application the heartbeats stopped, as they should. After that point is was very easy to implement some reconnect logic based on the last time I received a heartbeat and if it was too old or not. Here is some sample code for configuring the STOMP client, most of this I pulled from the spring stock-portfolio stomp client example.
Inside the StompWebSocketHandler class you simply add this block of code. You would obviously set the heartbeatInterval variable to whatever value you desire.
public void afterConnectionEstablished(WebSocketSession session) throws IOException {
StompHeaderAccessor headers = StompHeaderAccessor.create(StompCommand.CONNECT);
headers.setAcceptVersion("1.1,1.2");
headers.setHeartbeat(heartbeatInterval, heartbeatInterval);
Message<byte[]> message = MessageBuilder.withPayload(new byte[0]).setHeaders(headers).build();
TextMessage textMessage = new TextMessage(new String(encoder.encode(message), DEFAULT_CHARSET));
session.sendMessage(textMessage);
}

RabbitMQ with Websocket and Gevent

I'm looking forward to develop a realtime API for my web application using Websocket. For this I'm using RabbitMQ as the broker and My backend is based on python (gevent + websocket),and Pika/Puka as rabbitmq client.
The problem I'm facing here is that, how we can use websocket to connect with rabbitMQ. After the initial websocket connection establishment, the socket object wait for new messages from client, and in the case of rabbitMQ, we need to setup a consumer for it, so it will process the message when it receive one. We can take this in this way,
Clients are established connection with server via full-duplex websocket.
All clients should act as RabbitMQ's consumer after initial websocket handshake, so they all get updates when a client gets some message.
When new message arrives at websocket, that client will send it to RabbitMQ, so at this time this client act as publisher.
The problem is Websocket wait for a new message, and the RabbitMQ consumer wait for new message on its channel, I'm failed to link these two cases.
I'm not sure whether this is a wrong method ...
I'm unable to find a method to implement this scenario.If I'm going wrong way or is there any alternate method ?, please help me to fix this.
Thank you,
Haridas N.
I implemented similar requirement with Tornado + websocket + RabbitMQ + Pika.
I think this were already known method. Here is my git repo for this web chat application.
https://github.com/haridas/RabbitChat
It seems very difficult to the similar thing with gevent/twisted because the rabbitMQ clients couldn't supporting the event loops of gevent/twisted.
The pika has tornado adapter, so that makes this easy to setup. Pika development team working on the twisted adapter also. I hope they will release it very soon.
Thanks,
Haridas N.
http://haridas.in.
A simple solution would be to use gevent.queue.Queue instances for inter-greenlet communication.

Resources