Deployment

There are many options to deploy a Flask-SocketIO server, ranging from simpleto the insanely complex. In this section, the most commonly used options aredescribed.

Embedded Server

The simplest deployment strategy is to start the web server by callingsocketio.run(app) as shown in examples above. This will look through thepackages that are installed for the best available web server and start theapplication on it. The current web server choices that are evaluated areeventlet,gevent and the Flask development server.

If eventlet or gevent are available,socketio.run(app) starts aproduction-ready server using one of these frameworks. If neither of these areinstalled, then the Flask development web server is used, and in this case theserver is not intended to be used in a production deployment.

Unfortunately this option is not available when using gevent with uWSGI. Seethe uWSGI section below for information on this option.

Gunicorn Web Server

An alternative tosocketio.run(app) is to usegunicorn as web server, using the eventlet or geventworkers. For this option, eventlet or gevent need to be installed, in additionto gunicorn. The command line that starts the eventlet server via gunicorn is:

gunicorn--worker-classeventlet-w1module:app

If you prefer to use gevent, the command to start the server is:

gunicorn-kgevent-w1module:app

When using gunicorn with the gevent worker and the WebSocket support providedby gevent-websocket, the command that starts the server must be changed toselect a custom gevent web server that supports the WebSocket protocol. Themodified command is:

gunicorn-kgeventwebsocket.gunicorn.workers.GeventWebSocketWorker-w1module:app

A third option with Gunicorn is to use the threaded worker, along with thesimple-websocketpackage for WebSocket support. This is a particularly good solution forapplications that are CPU heavy or are otherwise incompatible with eventletand gevent use of green threads. The command to start a threaded web serveris:

gunicorn-w1--threads100module:app

In all these commands,module is the Python module or package that definesthe application instance, andapp is the application instance itself.

Due to the limited load balancing algorithm used by gunicorn, it is not possibleto use more than one worker process when using this web server. For that reason,all the examples above include the-w1 option.

The workaround to use multiple worker processes with gunicorn is to launchseveral single-worker instances and put them behind a more capable loadbalancer such asnginx.

uWSGI Web Server

When using the uWSGI server in combination with gevent, the Socket.IO servercan take advantage of uWSGI’s native WebSocket support.

A complete explanation of the configuration and usage of the uWSGI server isbeyond the scope of this documentation. The uWSGI server is a fairly complexpackage that provides a large and comprehensive set of options. It must becompiled with WebSocket and SSL support for the WebSocket transport to beavailable. As way of an introduction, the following command starts a uWSGIserver for the example application app.py on port 5000:

$ uwsgi --http :5000 --gevent 1000 --http-websockets --master --wsgi-file app.py --callable app

Using nginx as a WebSocket Reverse Proxy

It is possible to use nginx as a front-end reverse proxy that passes requeststo the application. However, only releases of nginx 1.4 and newer supportproxying of the WebSocket protocol. Below is a basic nginx configuration thatproxies HTTP and WebSocket requests:

server {    listen 80;    server_name _;    location / {        include proxy_params;        proxy_pass http://127.0.0.1:5000;    }    location /static/ {        alias <path-to-your-application>/static/;        expires 30d;    }    location /socket.io {        include proxy_params;        proxy_http_version 1.1;        proxy_buffering off;        proxy_set_header Upgrade $http_upgrade;        proxy_set_header Connection "Upgrade";        proxy_pass http://127.0.0.1:5000/socket.io;    }}

The next example adds the support for load balancing multiple Socket.IOservers:

upstream socketio_nodes {    ip_hash;    server 127.0.0.1:5000;    server 127.0.0.1:5001;    server 127.0.0.1:5002;    # to scale the app, just add more nodes here!}server {    listen 80;    server_name _;    location / {        include proxy_params;        proxy_pass http://127.0.0.1:5000;    }    location /static/ {        alias <path-to-your-application>/static/;        expires 30d;    }    location /socket.io {        include proxy_params;        proxy_http_version 1.1;        proxy_buffering off;        proxy_set_header Upgrade $http_upgrade;        proxy_set_header Connection "Upgrade";        proxy_pass http://socketio_nodes/socket.io;    }}

While the above examples can work as an initial configuration, be aware that aproduction install of nginx will need a more complete configuration coveringother deployment aspects such as SSL support.

Using Multiple Workers

Flask-SocketIO supports multiple workers behind a load balancer starting withrelease 2.0. Deploying multiple workers gives applications that useFlask-SocketIO the ability to spread the client connections among multipleprocesses and hosts, and in this way scale to support very large numbers ofconcurrent clients.

There are two requirements to use multiple Flask-SocketIO workers:

  • The load balancer must be configured to forward all HTTP requests from agiven client always to the same worker. This is sometimes referenced as“sticky sessions”. For nginx, use theip_hash directive to achieve this.Gunicorn cannot be used with multiple workers because its load balanceralgorithm does not support sticky sessions.

  • Since each of the servers owns only a subset of the client connections, amessage queue such as Redis or RabbitMQ is used by the servers to coordinatecomplex operations such as broadcasting and rooms.

When working with a message queue, there are additional dependencies that need tobe installed:

  • For Redis, the packageredis must be installed (pipinstallredis).

  • For RabbitMQ, the packagekombu must be installed (pipinstallkombu).

  • For Kafka, the packagekafka-python must be installed (pipinstallkafka-python).

  • For other message queues supported by Kombu, see theKombu documentationto find out what dependencies are needed.

  • If eventlet or gevent are used, then monkey patching the Python standardlibrary is normally required to force the message queue package to usecoroutine friendly functions and classes.

For eventlet, monkey patching is done with:

importeventleteventlet.monkey_patch()

For gevent, you can monkey patch the standard library with:

fromgeventimportmonkeymonkey.patch_all()

In both cases it is recommended that you apply the monkey patching at the topof your main script, even above your imports.

To start multiple Flask-SocketIO servers, you must first ensure you have themessage queue service running. To start a Socket.IO server and have it connect tothe message queue, add themessage_queue argument to theSocketIOconstructor:

socketio=SocketIO(app,message_queue='redis://')

The value of themessage_queue argument is the connection URL of thequeue service that is used. For a redis queue running on the same host as theserver, the'redis://' URL can be used. Likewise, for a default RabbitMQqueue the'amqp://' URL can be used. For Kafka, use akafka:// URL.The Kombu package has adocumentationsectionthat describes the format of the URLs for all the supported queues.

Emitting from an External Process

For many types of applications, it is necessary to emit events from a processthat is not the SocketIO server, for an example a Celery worker. If theSocketIO server or servers are configured to listen on a message queue asshown in the previous section, then any other process can create its ownSocketIO instance and use it to emit events in the same way the serverdoes.

For example, for an application that runs on an eventlet web server and usesa Redis message queue, the following Python script broadcasts an event toall clients:

socketio=SocketIO(message_queue='redis://')socketio.emit('my event',{'data':'foo'},namespace='/test')

When using theSocketIO instance in this way, the Flask applicationinstance is not passed to the constructor.

Thechannel argument toSocketIO can be used to select a specificchannel of communication through the message queue. Using a custom channelname is necessary when there are multiple independent SocketIO servicessharing the same queue.

Flask-SocketIO does not apply monkey patching when eventlet or gevent areused. But when working with a message queue, it is very likely that the Pythonpackage that talks to the message queue service will hang if the Pythonstandard library is not monkey patched.

It is important to note that an external process that wants to connect toa SocketIO server does not need to use eventlet or gevent like the mainserver. Having a server use a coroutine framework, while an external processis not a problem. For example, Celery workers do not need to beconfigured to use eventlet or gevent just because the main server does. But ifyour external process does use a coroutine framework for whatever reason, thenmonkey patching is likely required, so that the message queue accessescoroutine friendly functions and classes.

Cross-Origin Controls

For security reasons, this server enforces a same-origin policy by default. Inpractical terms, this means the following:

  • If an incoming HTTP or WebSocket request includes theOrigin header,this header must match the scheme and host of the connection URL. In caseof a mismatch, a 400 status code response is returned and the connection isrejected.

  • No restrictions are imposed on incoming requests that do not include theOrigin header.

If necessary, thecors_allowed_origins option can be used to allow otherorigins. This argument can be set to a string to set a single allowed origin, orto a list to allow multiple origins. A special value of'*' can be used toinstruct the server to allow all origins, but this should be done with care, asthis could make the server vulnerable to Cross-Site Request Forgery (CSRF)attacks.