I have been following this tutorial to try and get Flask SocketIO
running using nginx
and gunicorn
.
nginx
server {
location / {
proxy_pass http://127.0.0.1:8000;
proxy_redirect off;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
}
location /socket.io {
proxy_pass http://localhost:8000/socket.io;
proxy_redirect off;
proxy_buffering off;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "Upgrade";
}
}
gunicorn_config.py
bind = '127.0.0.1:8000'
workers = 2
worker_class = 'socketio.sgunicorn.GeventSocketIOWorker'
In Supervisor
I call my app with:
[program:gunicorn-couponmonk]
directory = ~/couponmonk_project
command =~/venv/py2.7/bin/python ~/venv/py2.7/bin/gunicorn -c ~/venv/py2.7/lib/python2.7/site-packages/gunicorn/gunicorn_config.py __init__.py
stdout_logfile = /var/log/gunicorn/couponmonk-std.log
stderr_logfile = /var/log/gunicorn/couponmonk-err.log
user = mint
With this configuration, my app (without using Flask SocketIO
) works fine.
I'm just confused at to how to use socketIO
. If I go to the address http://localhost:8000/socket.io, I get the response Internal Server Error
.
The example HTML/Javascript
(https://github.com/miguelgrinberg/Flask-SocketIO/blob/master/example/templates/index.html) contains this line:
var socket = io.connect('http://' + document.domain + ':' + location.port + namespace);
I have two questions:
1) What address is this variable supposed to point to?
2) Is my configuration (nginx
, gunicorn
) correct?
Sorry if this question is silly. I'm just confused as to how all this is supposed to work.
Thanks for your help.
**UPDATE**
This is the output of the nginx
error.log
file when I try and access http://localhost/socket.io.
2015/06/20 14:05:08 [debug] 1917#0: *1 http cleanup add: 00000000009F2170
2015/06/20 14:05:08 [debug] 1917#0: *1 get rr peer, try: 1
2015/06/20 14:05:08 [debug] 1917#0: *1 socket 12
2015/06/20 14:05:08 [debug] 1917#0: *1 epoll add connection: fd:12 ev:80000005
2015/06/20 14:05:08 [debug] 1917#0: *1 connect to 127.0.0.1:8000, fd:12 #2
2015/06/20 14:05:08 [debug] 1917#0: *1 http upstream connect: -2
2015/06/20 14:05:08 [debug] 1917#0: *1 posix_memalign: 0000000000A14170:128 @16
2015/06/20 14:05:08 [debug] 1917#0: *1 event timer add: 12: 60000:1434773168437
2015/06/20 14:05:08 [debug] 1917#0: *1 http finalize request: -4, "/socket.io?" a:1, c:2
2015/06/20 14:05:08 [debug] 1917#0: *1 http request count:2 blk:0
2015/06/20 14:05:08 [debug] 1917#0: *1 post event 0000000000A295E0
2015/06/20 14:05:08 [debug] 1917#0: *1 post event 0000000000A29648
2015/06/20 14:05:08 [debug] 1917#0: *1 delete posted event 0000000000A29648
2015/06/20 14:05:08 [debug] 1917#0: *1 http upstream request: "/socket.io?"
2015/06/20 14:05:08 [debug] 1917#0: *1 http upstream send request handler
2015/06/20 14:05:08 [debug] 1917#0: *1 http upstream send request
2015/06/20 14:05:08 [debug] 1917#0: *1 chain writer buf fl:1 s:439
2015/06/20 14:05:08 [debug] 1917#0: *1 chain writer in: 00000000009F21A8
2015/06/20 14:05:08 [debug] 1917#0: *1 writev: 439
2015/06/20 14:05:08 [debug] 1917#0: *1 chain writer out: 0000000000000000
2015/06/20 14:05:08 [debug] 1917#0: *1 event timer del: 12: 1434773168437
2015/06/20 14:05:08 [debug] 1917#0: *1 event timer add: 12: 60000:1434773168437
2015/06/20 14:05:08 [debug] 1917#0: *1 delete posted event 0000000000A295E0
2015/06/20 14:05:08 [debug] 1917#0: *1 http run request: "/socket.io?"
2015/06/20 14:05:08 [debug] 1917#0: *1 http upstream check client, write event:1, "/socket.io"
2015/06/20 14:05:08 [debug] 1917#0: *1 http upstream recv(): -1 (11: Resource temporarily unavailable)
2015/06/20 14:05:08 [debug] 1917#0: *1 post event 0000000000A15E38
2015/06/20 14:05:08 [debug] 1917#0: *1 post event 0000000000A29648
2015/06/20 14:05:08 [debug] 1917#0: *1 delete posted event 0000000000A29648
2015/06/20 14:05:08 [debug] 1917#0: *1 http upstream request: "/socket.io?"
2015/06/20 14:05:08 [debug] 1917#0: *1 http upstream dummy handler
2015/06/20 14:05:08 [debug] 1917#0: *1 delete posted event 0000000000A15E38
2015/06/20 14:05:08 [debug] 1917#0: *1 http upstream request: "/socket.io?"
2015/06/20 14:05:08 [debug] 1917#0: *1 http upstream process header
2015/06/20 14:05:08 [debug] 1917#0: *1 malloc: 00000000009E88A0:4096
2015/06/20 14:05:08 [debug] 1917#0: *1 recv: fd:12 244 of 4096
2015/06/20 14:05:08 [debug] 1917#0: *1 http proxy status 500 "500 Internal Server Error"
2015/06/20 14:05:08 [debug] 1917#0: *1 http proxy header: "Connection: close"
2015/06/20 14:05:08 [debug] 1917#0: *1 http proxy header: "Content-Type: text/html"
2015/06/20 14:05:08 [debug] 1917#0: *1 http proxy header: "Content-Length: 141"
2015/06/20 14:05:08 [debug] 1917#0: *1 http proxy header done
2015/06/20 14:05:08 [debug] 1917#0: *1 xslt filter header
2015/06/20 14:05:08 [debug] 1917#0: *1 HTTP/1.1 500 Internal Server Error
Server: nginx/1.4.6 (Ubuntu)
Date: Sat, 20 Jun 2015 04:05:08 GMT
Content-Type: text/html
Content-Length: 141
Connection: keep-alive
Not sure how helpful this is but I don't know where else to look for extra info.
I'm also curious as to why I can access this: http://localhost/socket.io123abc
and still get an Internal Server Error
as opposed to a Not Found
error?
I also updated my supervisord.conf
file based on Miguel's
answer below:
supervisord.conf
[program:gunicorn-couponmonk]
directory = /home/giri/couponmonk_project
command = /home/giri/venv/py2.7/bin/python /home/giri/venv/py2.7/bin/gunicorn --worker-class socketio.sgunicorn.GeventSocketIOWorker __init__:app
stdout_logfile = /var/log/gunicorn/couponmonk-std.log
stderr_logfile = /var/log/gunicorn/couponmonk-err.log
user = mint
/var/log/gunicorn/couponmonk-err.log
2015-06-20 14:30:11 [3821] [ERROR] Connection in use: ('127.0.0.1', 8000)
2015-06-20 14:30:11 [3821] [ERROR] Retrying in 1 second.
2015-06-20 14:30:12 [3821] [ERROR] Connection in use: ('127.0.0.1', 8000)
2015-06-20 14:30:12 [3821] [ERROR] Retrying in 1 second.
2015-06-20 14:30:13 [3821] [ERROR] Connection in use: ('127.0.0.1', 8000)
2015-06-20 14:30:13 [3821] [ERROR] Retrying in 1 second.
This list continues on for a while..
First, I'll mention that I don't recommend using SocketIO at all. It adds a little bit of useful functionality over WebSockets but it makes true load balancing with multiple workers (horizontal scaling) impossible unless you make clients sticky to individual workers or use something like Redis to share state information between them. I recommend taking a look at:
https://github.com/youen/gevent-websocket
Native WebSockets is simpler and easier and scales across multiple frontend workers for true load balancing. The code it takes to implement chat room logic is very minimal.
Having said that, here's the answer to your questions:
Your namespace is going to be "". (According to Miguel, author of Flask-SocketIO, the Javascript client automatically inserts the "socket.io" part.)
So, your
io.connect
url should be "http://[hostname]/".Ngnix config seems correct (though see next section). If you decide to use WebSockets, you might consider adding
proxy_read_timeout 3600;
. Otherwise, unless you have a chatty protocol, the WebSocket will be dropped by Nginx once a minute (the default value). (Also, according to Miguel, SocketIO has heartbeats which deal with this.)Gunicorn config is not correct. With SocketIO you have a couple of choices:
workers = 1
in gunicorn so that each SocketIO client is talking to the same worker process.ip-hash
command which will cause clients to be assigned to workers by client IP address.These choices are forced because SocketIO uses a stateful setup mechanism which breaks when you try to scale horizontally. See this issue for more information: https://github.com/abourget/gevent-socketio/issues/112
And here's a link to SocketIO's documentation talking about it also: http://socket.io/docs/using-multiple-nodes/
If you are getting an
Internal Server Error
then there is likely an exception being logged somewhere. Try to hunt it down and add it to your question.Note that you can't test this by hitting that URL in the address bar of your browser – the browser doesn't speak the proper WebSocket protocol by default and will do nothing useful for you. WebSocket connections must be setup using the Javascript API.
Also, trying to hit that URL with the port number bypasses nginx - which is probably not what you want to do. Nginx usually listens at 80/443 and forwards requests to localhost:8000 (that's called a "reverse proxy" setup).