Setting default heartbeat from WebSocketStompClient causes exceptions - spring

We trying to use WebSocketStompClient to connect to the server implemented using spring websocket.
Our client is developed using spring 4.2.2 and works perfectly fine when the default heartbeat is not overriden.
However, I would like the client to send heartbeats in 2s intervals and therefore modified the client to set the default heart beat.
WebSocketStompClient stompClient = new WebSocketStompClient(webSocketClient);
ThreadPoolTaskScheduler taskScheduler = new ThreadPoolTaskScheduler();
taskScheduler.afterPropertiesSet();
long heartbeat[] = {2000, 10000};
stompClient.setDefaultHeartbeat(heartbeat);
stompClient.setTaskScheduler(taskScheduler); // for heartbeats, receipts
String url = "ws://localhost:8080/Notify/app";
StompSessionHandler handler = new MyStompHandler();
ListenableFuture<StompSession>session = stompClient.connect(url, handler, "localhost", "8080");
This causes an exception
Transport Error Connection closed
org.springframework.messaging.simp.stomp.ConnectionLostException: Connection closed
at org.springframework.messaging.simp.stomp.DefaultStompSession.afterConnectionClosed(DefaultStompSession.java:459)
at org.springframework.web.socket.messaging.WebSocketStompClient$WebSocketTcpConnectionHandlerAdapter.afterConnectionClosed(WebSocketStompClient.java:353)
at org.springframework.web.socket.adapter.standard.StandardWebSocketHandlerAdapter.onClose(StandardWebSocketHandlerAdapter.java:143)
at org.glassfish.tyrus.core.TyrusEndpointWrapper.onClose(TyrusEndpointWrapper.java:1190)
at org.glassfish.tyrus.core.TyrusWebSocket.onClose(TyrusWebSocket.java:126)
at org.glassfish.tyrus.core.frame.CloseFrame.respond(CloseFrame.java:131)
We are using tyrus client 1.10, spring websocket 4.2.2 (for websocket client) and ActiveMQ 5.11.1 as the broker.
I have tried to set different heartbeat values, but all of them cause exceptions.
I am not sure what is different when the client manually specifies the heartbeat.
Can someone please help?
Thanks.

Related

ConnectionPoolTimeoutException at caller [spring web applicaiton] logs but no logs at server [springboot]

I have one issue in PRD. we recently released a springboot application and it has REST API exposed. Mobile/web APP call a legacy spring application which is in spring [not sprintboot] and it is a web applicationwhich then routes and makes a call to the these failing apis in new springboot. We are seeing timeout exception for these apis only .
there are lots of other OUTBOUND api calls made from spring legacy web application to other applications eg : login API [which has apis heavy traffic but these legacy apis work well and call other legacy applications.
There are no exception/error in logs in springboot application which has these REST API exposed. Infact we only see timeout in spring web application -meaning connection is exhausted but that does not explain why other apis OUTBOUND call are not failing which use same wrapper HTTPClient. Those which fail with timeout dont have request logs in springboot [ obviously because they dont leave spring web application tomcat JVM and die there due to timeout ]
So if we say connection pool is exhausted, the other heavey traffic OUTBOUnd calls should also face same issue but we dont see that.
All API call OUTWARD use HTTPCLient [apache.]
Not clear what is causing issue. I also explicitly defined below in new springboot for server side [I just did it to see if that makes difference but in vain]:
server:
tomcat:
connection-timeout: 10s
max-connections: 20000
max-threads: 500
min-spare-threads: 10
tomcat Log at spring web applicaiton [caller]:
org.apache.http.conn.ConnectionPoolTimeoutException
org.apache.http.conn.ConnectionPoolTimeoutException.Timeout waiting for connection from pool
at org.apache.http.impl.conn.PoolingHttpClientConnectionManager.leaseConnection(PoolingHttpClientConnectionManager.java:313)
at org.apache.http.impl.conn.PoolingHttpClientConnectionManager$1.get(PoolingHttpClientConnectionManager.java:279)
at org.apache.http.impl.execchain.MainClientExec.execute(MainClientExec.java:191)
at org.apache.http.impl.execchain.ProtocolExec.execute(ProtocolExec.java:185)
at org.apache.http.impl.execchain.RedirectExec.execute(RedirectExec.java:110)
at org.apache.http.impl.client.InternalHttpClient.doExecute(InternalHttpClient.java:185)
at org.apache.http.impl.client.CloseableHttpClient.execute(CloseableHttpClient.java:72)
at org.apache.http.impl.client.CloseableHttpClient.execute(CloseableHttpClient.java:221)
at org.apache.http.impl.client.CloseableHttpClient.execute(CloseableHttpClient.java:165)
at org.apache.http.impl.client.CloseableHttpClient.execute(CloseableHttpClient.java:140)
at
Any inputs?
Code snippet of Wrapper HTTPClient :
SSLContext sslContext = SSLContexts.createDefault();
HostnameVerifier hostnameVerifier = new DefaultHostnameVerifier();
SSLConnectionSocketFactory secureSSLConnectionSocketFactory = new SSLConnectionSocketFactory(
sslContext,
sslProtocolsArray,
ciphersArray,
hostnameVerifier);
ConnectionSocketFactory nonSecureConnectionSocketFactory = PlainConnectionSocketFactory.getSocketFactory();
Registry<ConnectionSocketFactory> socketFactoryRegistry = RegistryBuilder
.<ConnectionSocketFactory>create()
.register("https", secureSSLConnectionSocketFactory)
.register("http", nonSecureConnectionSocketFactory)
.build();
securePoolingConnectionManager = new PoolingHttpClientConnectionManager(socketFactoryRegistry);
securePoolingConnectionManager.setMaxTotal(this.connectionMgrMaxTotalSecure);
securePoolingConnectionManager.setDefaultMaxPerRoute(this.connectionMgrMaxPerRouteSecure);
SocketConfig secureSocketConfig = SocketConfig
.custom()
.setSoKeepAlive(true)
.setTcpNoDelay(true)
.build();
secureHttpsClient = HttpClients
.custom()
.setSSLSocketFactory(secureSSLConnectionSocketFactory)
.setConnectionManager(securePoolingConnectionManager)
.setDefaultRequestConfig(secureRequestConfig)
.setDefaultSocketConfig(secureSocketConfig)
.disableAutomaticRetries()
.build();
Stacktrace after above is just failing at wrapper HTTPClient method where call is invoked :
protected String execute(HttpClient httpclient, HttpRequestBase http) throws IOException {
String result;
ResponseHandler<String> responseHandler = new BasicResponseHandler();
result = httpclient.execute(http, responseHandler);
return result;
}
So I have to dig in another wrapper which was also using this HTTP pool and was being used in our legacy which was leaking. Closing this. Fortunately there was pool statistics api exposed so that I can see leased connection count which confirmed leaking. Since this second wrapper was used rerely and we had used in this release this was suspect and removing it solved the issue. It is another matter to dig that wrapper and find out how the pool was handled but the cause was caught.

How to disable connection pooling in Webclient in new Springboot 2.1.4.Release?

I am using springboot webclient to call rest api from remote server. First request works fine. If I made subsequent request after sometime, the server throws 500 server error. The error I got is " onError(java.io.IOException: An existing connection was forcibly closed by the remote host)".
I want to test the behavior with disabling connection pool since I believe it uses the previous connection. Can you help me how to disable the connection pool when creating webclient?
TcpClient tcpClient = TcpClient.create()
.option(ChannelOption.CONNECT_TIMEOUT_MILLIS, 30000)
.option(ChannelOption.SO_KEEPALIVE, false)
.doOnConnected(connection ->
connection.addHandlerLast(new ReadTimeoutHandler(30))
.addHandlerLast(new WriteTimeoutHandler(30))
);
ReactorClientHttpConnector httpConnector = new ReactorClientHttpConnector(HttpClient.from(tcpClient));
final WebClient webClient = WebClient
.builder()
.clientConnector(httpConnector)
.baseUrl("http://customer.service.api.internal.cloud.qa.intranet.pagseguro.uol")
.exchangeStrategies(strategies)
.build()
You can disable the connection pooling using the code below
TcpClient tcpClient = TcpClient.newConnection()
Use wiretap(true) to inspect the traffic between the server and the client.
You can also enable the logging for Reactor Netty and trace whether the connection is reused (in the connection pool scenario)
logging.level.reactor.netty=debug
If the connection is reused you will see
2019-04-11 18:52:10.049 DEBUG 98105 --- [ctor-http-nio-5] r.n.resources.PooledConnectionProvider : [id: 0x897584fa, L:/<IP>:<PORT> - R:/<IP>:<PORT>] Channel acquired, now 1 active connections and 0 inactive connections
Also using the channel id id: 0x897584fa you can track what's happening with the channel.

Webflux & WebSocket, send to specific session id

Every session data passed into the socket is broadcasted to all users since every session subscribes to the UnicastProcessor eventPublisher.
How can I send by event data to a single session id and not to all of them?
#Override
public Mono<Void> handle(WebSocketSession session) {
WebSocketMessageSubscriber subscriber = new WebSocketMessageSubscriber(eventPublisher);
session.receive()
.map(WebSocketMessage::getPayloadAsText)
.map(this::toEvent)
.subscribe(subscriber::onNext, subscriber::onError, subscriber::onComplete);
return session.send(outputEvents.map(session::textMessage));
}
My use-case requires me to include both options for broadcasting any changed state with any client to all sockets connected plus the abillity to send response to a specific client (sessionId) that send a request within a specific event
Github link
or should It be routed to 2 different handlers from the same websocket path?
note that from javascript
new WebSocket(url/path) creates a socket connection
there is no way to change the path without creating or instantiating a new WebSocket object which is not wanted.
I'm not interested in creating for every browser client 2 sockets...
so my goal is to base the server connection via 1 single websocket path
#Bean
public HandlerMapping webSocketMapping(UnicastProcessor<Event> eventPublisher, Flux<Event> events) {
Map<String, Object> map = new HashMap<>();
map.put("/websocket/chat", new ChatSocketHandler(eventPublisher, events));
SimpleUrlHandlerMapping simpleUrlHandlerMapping = new SimpleUrlHandlerMapping();
simpleUrlHandlerMapping.setUrlMap(map);
//Without the order things break :-/
simpleUrlHandlerMapping.setOrder(10);
return simpleUrlHandlerMapping;
}
if so would be glad to see an example of such solution
With servlet based web socket, it's possible because you can connect websocket to messaging brokers. Then the messaging broker will take care of sending messages to specific client.
But with webflux based websocket that spring is provided , I couldn't manage to do bring messaging brokers into action. It seems that there is no support yet for it in spring webflux.
Find a sample with servlet stack here:
https://github.com/bmd007/RealtimeNoteSharing.git

Handler for connect timeout

I want to use Spring 5 Web client for REST connections.
WebClient.Builder builder = WebClient.builder().baseUrl(gatewayUrl);
ClientHttpConnector httpConnector = new ReactorClientHttpConnector(opt -> opt.sslContext(sslContext));
builder.clientConnector(httpConnector);
How I can implement handler for connection timeout?
I checked the code here but I can't find a solution.

Artemis 1.2.0. Paging don't work with activemq(5.12.0,5.13.2) client

Artemis 1.2.0 Standalone broker conf:
<address-full-policy>PAGE</address-full-policy>
<page-size-bytes>1048576</page-size-bytes>
Other settings are default.
"tcp://localhost:61616" - is the "artemis" multiprotocol acceptor
Normal case:
Behaviour: Paging works.
Client: artemis-jms-client v1.2.0
Class: org.apache.activemq.artemis.jms.client.ActiveMQConnectionFactory
ConnectionFactory connectionFactory = new ActiveMQConnectionFactory("tcp://localhost:61616", user, pass);
Connection connection = connectionFactory.createConnection();
Session session = connection.createSession(false, Session.AUTO_ACKNOWLEDGE);
Queue queue = session.createQueue("tmpQueue");
MessageProducer producer = session.createProducer(queue);
connection.start();
byte[] bytes = new byte[Integer.parseInt(size)];
Random random = new Random();
whilt (true) {
BytesMessage bytesMessage = session.createBytesMessage();
random.nextBytes(bytes);
bytesMessage.writeBytes(bytes);
producer.send(bytesMessage);
}
Error case:
Behaviour: Paging DO NOT works. When messages fills queue to the max-size-bytes setting, JMX's Address.Paging becomes true, page file is created, page files count becomes 1, but producer freezes until consumer start working. Log tells that paging is starting, so it should be ok, but id didn't. No additional messages even if I set Trace level.
Client: activemq-client v5.12.0 (Also tried latest 5.13.2, no difference)
Class: org.apache.activemq.ActiveMQConnectionFactory
ConnectionFactory connectionFactory = new ActiveMQConnectionFactory(user, pass, "tcp://localhost:61616");
// the same code after ConnectionFactory as for artemis-jms-client ...
Is this a bug or I just cook it wrong?
Artemis is under heavy development and the OpenWire support is especially young. This is most likely fixed as there is a ton of ongoing work on the master branch. You can checkout the source from the Github mirror and build it yourself to test it out.
If you find things that don't work you can of course open Jira issues for them, but it is best at this point to test against a development snapshot given how new the project is.

Resources