45

I have an Elastic Beanstalk application which was initially configured to use a Classic Load Balancer. I found that this caused errors when connecting via WebSocket. Because of this, I configured the application to use an Application Load Balancer instead, because I was told that ALBs support WebSockets. However, it seems that they do not: I get exactly the same error when attempting to connect to my ALB via WebSocket.

Do ALBs actually support WebSocket? The AWS documentation is contradictory on this. This page says it only supports HTTP and HTTPS. There are no guides to setting up an ALB to support WebSocket.

jameshfisher
  • 34,029
  • 31
  • 121
  • 167
  • From the official blog: https://aws.amazon.com/blogs/aws/new-aws-application-load-balancer/ "ALB provides native support for WebSocket via the ws:// and wss:// protocols." – Mark B Sep 05 '16 at 19:00
  • despite that announcement and the FAQ section for the ALB, I also get that same error on an ALB (http status 501 Not Implemented) – nont Sep 15 '16 at 18:17
  • notwithstanding https://aws.amazon.com/elasticloadbalancing/applicationloadbalancer/faqs/ – nont Sep 15 '16 at 18:18
  • Socket.io has issues and doesn't seem to work out of the box. – coolboyjules Nov 09 '18 at 21:35

5 Answers5

53

I was able to get WebSockets working with the new Application Load Balancer (ALB).

First, create a new Target Group for your ALB. This Target Group should use the same port as your application, and will need to have health checks configured. However, the main difference is that you must enable Stickiness.

Add Target Group with Stickiness

Next, add a new Listener Rule to your ALB. This rule must have a Path to route the WebSocket setup -- /socket.io. Also, set the Target Group Name to the Target Group you just created.

Add Listen rule for WebSocket

I am using Node/Hapi/Socket.io for my server (running on instance derived from Amazon Linux AMI). Basic setup is:

const hapi = require('hapi');
const websocket = require('./WebSocket');

var server = new hapi.Server();
server.connection(config.Application);
websocket.Initialize(server.listener);

where WebSocket.js is

var io = null;

module.exports = {

    Initialize: function (http) {

        io = require('socket.io')(http);

        io.on('connection', function (socket) {
            console.log('Websocket ' + socket.id + ' connected.');

            socket.on('disconnect', function () {
                console.log('Websocket ' + socket.id + ' disconnected.');
            });
        });
    }
};

I am using Angular 1.5x for my client, with socket.io-client. It is important to configure the WebSocket client options as follows, or you will not be able to connect.

(function () {

    'use strict';

    angular
        .module('XXXXX', [])
        .run(runHandler);

    runHandler.$inject = ['WebSocketService'];

    function runHandler(WebSocketService) {
       WebSocketService.Initialize();
    }
})();

The WebSocket service:

(function () {

    'use strict';

    angular
        .module('XXXXX')
        .factory('WebSocketService', WebSocketService);

    WebSocketService.$inject = [];

    function WebSocketService() {

        var socket = null;

        function initialize() {

            var url = 'http://' + ALB_URL + ':5800';

            socket = io(url, {transports: ['websocket'], upgrade: false});

            socket.on('connect', function () {
                console.log('Socket connected');
            });

            socket.on('disconnect', function () {
                console.log('Socket disconnected');
            });
        }

        return {
            Initialize: initialize
        };
    }
})();
Tim S. Van Haren
  • 8,861
  • 2
  • 30
  • 34
programmerj
  • 1,634
  • 18
  • 29
  • 2
    What is your health check config? Does socket.io or your application code have a special health check endpoint? In my case, requesting `/` results in an `HTTP 426` response, which ALB doesn't consider healthy. – dskrvk Nov 24 '16 at 20:20
  • 1
    I have two VMs behind the ALB. Each hosts a Node web service. I have a REST endpoint at http::5800/v1/monitor/ping that returns the current server date/time as a string with HTTP 200 OK. I configure this information in my API (non web socket) Target Group->Health Checks. – programmerj Nov 28 '16 at 17:38
  • @programmerj So what would your ALB_URL be? "xxx.us-east-1.elasticbeanstalk.com/socket.io:5800"? Is it required the path be exactly "socket.io" or is it arbitrary? Flask-socketio exposes websocket via port 5000, so would I use port 5000? – Alpenglow Apr 11 '17 at 15:24
  • Hello @Alpenglow. Going from memory because I no longer use this stack, but the ALB_URL will be the generated URL of the application load balancer, or in my case, a URL created in Route53 that points to the application load balancer. I don't think the path has to be 'socket.io', as long as the client matches. I believe you would use 5000 in your case. – programmerj Apr 12 '17 at 17:22
  • I could only get this to work by enabling load balance stickiness despite the AWS documentation indicating it wasn't necessary: http://docs.aws.amazon.com/elasticloadbalancing/latest/application/load-balancer-target-groups.html#sticky-sessions – n4cer500 Apr 25 '17 at 13:08
  • 1
    Thanks for this post. However has anyone had the problem where the socket disconnects and then it retries? It seems that it stays connected for awhile then looses the connection intermittently. And I noticed it gives "Error during WebSocket handshake: Unexpected response code: 502" - any ideas? – Aaron Dec 26 '17 at 15:15
  • Sorry, I've not done anything with web sockets and ALBs since I posted this answer. Did you ensure you enabled stickiness with the target group? – programmerj Dec 27 '17 at 16:13
  • Note that if you're using elastic beanstalk, you should follow this answer which has the solution https://stackoverflow.com/questions/47584103/socket-io-in-aws-elasticbeanstalk-node – coolboyjules Feb 01 '19 at 17:11
  • UseLess, keypoint is healthy check,if check fail, I may be redirect to a wrong server! – Albert.Qing Nov 30 '21 at 10:55
  • The AWS interface has changed quite a bit since, can this answer be updated? Also, there are a few more options now, such as "group stickiness" and "target type". – ribbonwind Dec 06 '21 at 12:54
7

ALB support Websocket but the load balancer can close the connection if the instance doesn't send some data at least every "idle timeout" seconds.

Namtrh
  • 71
  • 1
  • 3
3

Application load balancer supports websocket. But No support for websocket health check till 23 Feb 2017. They may add an option later. You need to set up a HTTP or HTTPS health check for your target group when you want to use a websocket behind Application Load Balancer.

From AWS document: "Note that health checks do not support WebSockets."

Reference: http://docs.aws.amazon.com/elasticloadbalancing/latest/application/target-group-health-checks.html

  • 5
    Just a heads up, we're in 2018, near 2019, and AWS still does not support WSS Health Checks :'( https://docs.aws.amazon.com/elasticloadbalancing/latest/application/target-group-health-checks.html still sayd – Cyril Duchon-Doris Oct 02 '18 at 23:37
  • 6
    Just a heads up, we're in 2022, and AWS still does not support WebSocket Health Checks :'( https://docs.aws.amazon.com/elasticloadbalancing/latest/application/target-group-health-checks.html – jenovachild May 26 '22 at 01:41
1

ALB (Application load balancer) supports websockets. you can check here

also ALB close the connection depending upon idle time out configured.

SnapShot from the above blog

Michael Wiles
  • 20,902
  • 18
  • 71
  • 101
-4

In terms of nodejs When we use socket io on the client-side and call socket.js library
like this

const socket = io('domain.com');

what I observed is when I specified Http as protocol then it redirected the call to ws://domain.com but when I specified https then it redirected to wss://domain.com and I was getting this error failed: Error during WebSocket handshake: Unexpected response code: 400 what I did is I specified

const socket = io(window.location.origin, {reconnect: true,transports: ['websocket']});

which indeed solved my problem