While Websockets are indeed the big feature of Spring 4, they are also extremely buggy even after two revisions (version 4.0.2). We tried using them for DripStat but ended up switching to an 3rd party service after encountering all the bugs. This is a guide for some of the issues you will encounter and solutions for them.
1. Using an external queue throws ClassNotFoundException
Spring makes it real easy to use an external message queue like RabbitMQ for messaging. You just need to replace config.enableSimpleBroker("/topic");
with config.enableStompBrokerRelay("/topic”);
.
Sounds awesome right. Well, that is until you make that change and run your program only to run into a ClassNotFoundException
for reactor/tcp/TcpClient
. At this point, you are probably confused. The Spring Projects page does not show anything by the name of ‘Reactor’ so what is this beast and why don’t the spring websocket docs mention it?
Apparently Reactor is a project by the spring people but not under the official Spring brand. And using an external queue causes Spring to switch to using Reactor, without telling you that it needs it.
Solution:
Just add the following dependency in gradle and you should be fine:compile 'org.projectreactor:reactor-tcp:1.0.0.RELEASE’
Related bug: https://jira.springsource.org/browse/SPR-11449
2. Cannot authenticate with external queue
If you are using version 4.0.0 or 4.0.1, you may find yourself not being able to authenticate at all against your external queue. Apparently services like CloudAMQP and CloudFoundry require you to set a virtual host for your queue and prior to version 4.0.2 there was no way to set that!
Solution:
Upgrade to version 4.0.2 and call setVirtualHost()
in the StompBrokerRelayRegistration
instance.
Related bug: https://jira.springsource.org/browse/SPR-11433
3. CORS filter breaks
If you use CORS and have a filter setup to add CORS headers, you will find that it breaks when used against spring’s websocket endpoint url. That is because just for the web socket endpoint, Spring decides to add CORS headers by itself, which will interfere with the ones you already added. Again this fact is not documented anywhere. It also seems that Spring’s filter doesn’t even bother to check if the CORS headers are already present so as to not interfere with them.
Solution:
Your CORS filter will need to check for and exclude adding CORS headers to the websocket endpoint (since Spring insists on adding them for you).
Related bug: https://jira.springsource.org/browse/SPR-11443
4. Tons of exceptions in logs
You may see tons of exceptions like these in your logs:
ClientAbortException: java.net.SocketException: Broken pipe
.
This seems to result from this issue in the servlet spec. Again Spring neither has detection for this nor documents this issue.
Solution:
Vote/comment to get the bug resolved at https://java.net/jira/browse/SERVLET_SPEC-44
Related bug: https://jira.springsource.org/browse/SPR-11438
5. Websockets slow even after using external queue
While the docs continually harp on using an external queue for production usage, you may find that even doing so doesn’t solve all the performance issues with your app. It turns out it is not until you read the javadoc of the method WebSocketMessageBrokerConfigurer.configureClientInboundChannel()
do you realize that Spring is using just 1 thread to do all the work. How are you supposed to stumble upon the javadoc of this particular method? No idea.
Solution:
Implement the WebSocketMessageBrokerConfigurer
’s configureClientInboundChannel()
and configureClientOutboundChannel()
methods to return an executor with a thread pool size > 1.
Related bug: https://jira.springsource.org/browse/SPR-11450
Conclusion
While some of these may be resolved in future versions, the current release of Spring’s Websockets is undoubtedly very undocumented and buggy. That said I do think Spring has done an outstanding job architecture wise of making web sockets as simple to use as creating Rest APIs and i am looking forward to a more stable version.