I'm working on a Django app that's using the django-websocket-redis app to publish events from the server side.
I've successfully configured a test server using nginx and uWSGI to route both HTTP and WS requests correctly to my Django app, but on my local development environment I cannot get this working.
According to the django-websocket-redis documentation, it's enough to start the Django development server and everything should work fine, but it seems like this is far away from reality.
Here is what I've checked:
redis is running on localhost:6379 and is responding to PING requests
tried to run the server on different ports (80, 8080, 8000) to check if the django-websocket-redis makes any assumption about the development server's port, but nothing changed
searched for solution online, but there is nothing about this topic
On my local environment on the client side I see a 404 error when my app tries to connect to the local WebSocket. My settings.py sets the WEBSOCKET_URL to the correct URL (on test server it's working, but locally isn't).
Anyone has an idea what am I doing wrong? Thanks!
I've found a workaround to this.
I've modified my wsgi.py to this and it works now:
import os
os.environ.setdefault("DJANGO_SETTINGS_MODULE", "myapp.settings")
# This application object is used by any WSGI server configured to use this
# file. This includes Django's development server, if the WSGI_APPLICATION
# setting points here.
from django.core.wsgi import get_wsgi_application
from django.conf import settings
from ws4redis.django_runserver import WebsocketRunServer
if settings.DEBUG:
_django_app = get_wsgi_application()
_websocket_app = WebsocketRunServer()
def application(environ, start_response):
if environ.get('PATH_INFO').startswith(settings.WEBSOCKET_URL):
return _websocket_app(environ, start_response)
return _django_app(environ, start_response)
else:
application = get_wsgi_application()
UPDATE: it's not needed to create a custom management command as I mentioned previously, so I've deleted that part of the answer.
Related
I believe I have a similar issue to the one found here
and here.
The gist is that I'm running a Django app with channels on an Amazon-ec2 instance and the websockets are failing to connect.
Most of my code regarding the websockets comes from the django-channels tutorial here.
Traffic is being directed though secured dns name behind an application load balancer.
I'm running this entirely on Daphne (handling both https and websocket traffic) with pretty minimal configurations:
daphne -b <server_url> -p <port> test_app.asgi:application
I'm also authenticating with openID-connect using the mozilla-django-oidc module. However for the websocket test I'm not expecting authentication. I feel it's worth pointing out if the issue is related to websocket authentication in any way.
In development I'm running a local redis cluster as my channel layer. My dev app (all http:// and ws://) has no issues connecting to websockets. The chat app works as expected. I can connect to my websockets and is confirmed with a
127.0.0.1:60779 - - [07/Apr/2021:12:06:05] "WSCONNECTING /ws/chat/lobby/"
Here is the code in asgi.py
import chat.routing
asgi_app = get_asgi_application()
from channels.auth import AuthMiddlewareStack
application = ProtocolTypeRouter(
{
"http": asgi_app,
"websocket": AuthMiddlewareStack(
URLRouter(chat.routing.websocket_urlpatterns)
),
}
)
and the code in chat/routing.py
from django.urls import re_path
from . import consumers
websocket_urlpatterns = [
re_path(r'ws/chat/(?P<room_name>\w+)/$', consumers.ChatConsumer.as_asgi()),
]
In production I'm using an elasticache redis cluster as my channel layer. I can test this in the django shell and it connects/sends/receives.
However in the production chat I am unable to reach the room or see the above WSCONNECTING message. It never upgrades the connection to a websocket.
The next log after that is
2021-04-07 16:15:15,279 WARNING Not Found: /ws/chat/lobby/
Like its trying to resolve that route as http and not as a websocket.
After doing some additional reading I tried to use the host/port that is running on Daphne like
wss://<my host ip>:<port>/ws/chat/lobby
Which did not cause an immediate failure but ultimately never connected.
(index):46 Uncaught DOMException: Failed to execute 'send' on 'WebSocket': Still in CONNECTING state.
Here's the error in chrome
I feel like it's something to do with that public-facing host name that is being used as the host name in the websocket url. All inbound traffic is allowed. I'm not sure how this relates to the issue at hand.
I'm still trying to learn my way around all this. Any advice would certainly be appreciated.
Thanks!
I was just running thru that same tutorial, and I got the same error. I decided to look at the methods imported here:
from channels.routing import ProtocolTypeRouter, URLRouter
and I couldn't find them. So I uninstalled django-channels and installed channels
python3 -m pip uninstall django-channels && python3 -m pip install channels
and stuff worked.
Auto reloader (on code change) works fine using app.run/flask run --host=0.0.0.0 --port 8080.
However as soon as I add flask_socketio to the mix it stops working.
E.g. bringing in:
from flask_socketio import SocketIO, send, emit
socketio = SocketIO(app, path='/kpi/socket.io')
socketio.run(app, host='0.0.0.0', port=8080, debug=True, use_reloader=True, use_debugger=True)
requirements.txt
Flask==1.0.2
Flask-Bootstrap==3.3.7.1
Gunicorn==19.8.1
inotify==0.2.9
pymysql==0.7.2
flask-socketio==3.0.1
eventlet==0.23.0
socketIO-client==0.7.2
I do get "* Restarting with stat" in the logs, same as I would if I was typically running flask.
Extraneous info: this is running in a docker container where I have a compose file for dev where I run the dev mode, and have an env variable for FLASK_DEBUG=1. Using nginx to proxy the rest to port 80 then I server that up to another local port. This all works fine and dandy until I add socketio.
Ending up doing this:
Running this in dev (this brings in the werkzeug debugger for wsgi)
# TODO run this only in dev
from werkzeug.debug import DebuggedApplication
app.debug = True
app.wsgi_app = DebuggedApplication(app.wsgi_app, evalex=True)
Then use supervisord and run uwsgi instead including the flag
--py-autoreload 1
Those both return the functionality I want in development. Just need to make them only run in dev mode now and I'm good.
Hello I have a django app. My whole system configuration is the following: python 3, django 1.11, eventlet 0.21.0.
1) Nginx as an upstream server:
upstream proj_server {
server unix:///tmp/proj1.sock fail_timeout=0;
server unix:///tmp/proj2.sock fail_timeout=0;
}
2) Supervisor that controls workers. There is a gunicorn worker:
[program:proj]
command=/home/vagrant/.virtualenvs/proj/bin/gunicorn -c /vagrant/proj/proj/proj/deploy/gunicorn.small.conf.py proj.wsgi:application
directory=/vagrant/proj/proj/proj/deploy
user=www-data
autostart=true
autorestart=true
stdout_logfile=/var/log/supervisor/proj.log
3) This is a gunicorn.small.conf content:
bind = ["unix:///tmp/proj1.sock", "unix:///tmp/proj2.sock"]
pythonpath = "/vagrant/proj/proj/proj/deploy"
workers = 2
worker_class = "eventlet"
worker_connections = 10
timeout = 60
graceful_timeout = 60
4) And this is proj.wsgi content:
"""
WSGI config for proj project.
This module contains the WSGI application used by Django's development server
and any production WSGI deployments. It should expose a module-level variable
named ``application``. Django's ``runserver`` and ``runfcgi`` commands discover
this application via the ``WSGI_APPLICATION`` setting.
Usually you will have the standard Django WSGI application here, but it also
might make sense to replace the whole Django WSGI application with a custom one
that later delegates to the Django one. For example, you could introduce WSGI
middleware here, or combine a Django application with an application of another
framework.
"""
import eventlet
eventlet.monkey_patch()
from eventlet import wsgi
import django.core.handlers.wsgi
import os
os.environ.setdefault("DJANGO_SETTINGS_MODULE", "proj.settings")
# This application object is used by any WSGI server configured to use this
# file. This includes Django's development server, if the WSGI_APPLICATION
# setting points here.
from django.core.wsgi import get_wsgi_application
application = get_wsgi_application()
# Apply WSGI middleware here.
# from helloworld.wsgi import HelloWorldApplication
# application = HelloWorldApplication(application)
So, as you can see there is a chain: nginx as an upstream server calls one of the gunicorn eventlet workers using two sockets proj1.sock or proj2.sock.
Note that according with the eventlet documentation I try to use eventlet.monkey_patch() as early as possible. The most appropriate place for that is proj.wsgi that is called by gunicorn in the first place.
However it seems that library isn't monkey patched.
To check this I added to the proj/proj/proj/__init__.py (the first module that called by the django application) the following code:
import eventlet
import os
print("monkey patched os is: " + str(eventlet.patcher.is_monkey_patched('os')))
print("monkey patched select is: " + str(eventlet.patcher.is_monkey_patched('select')))
print("monkey patched socket is: " + str(eventlet.patcher.is_monkey_patched('socket')))
print("monkey patched time is: " + str(eventlet.patcher.is_monkey_patched('time')))
print("monkey patched subprocess is: " + str(eventlet.patcher.is_monkey_patched('subprocess')))
then i issued **./manage.py check** and got that answer:
monkey patched os is: false
monkey patched select is: false
monkey patched socket is: false
monkey patched time is: false
monkey patched subprocess is: false
What am I doing wrong?
What if you change proj.wsgi file content to one line raise Exception? That should eliminate eventlet from suspects.
I'm not good with Django, here's a pure speculation:
based on name, proj.wsgi is executed when WSGI server is about to start
manage.py check doesn't seem to be related to remote network service (WSGI), seems to be a general management command, so it shouldn't execute WSGI related code
One possible solution, taken from your question text:
proj/proj/proj/init.py (the first module that called by the django application
Try to put monkey_patch call in there.
P.S.: you don't need supervisor for gunicorn, its master process (arbiter) is designed to run forever in spite of problems with workers.
After setting DEBUG = False, and SECURE_SSL_REDIRECT = True and deploying a version on my app to the server, I wish now to develop further locally. The problem is, I think at one point I forgot to remove the SECURE_SSL_REDIRECT = True from settings.py, and I ran the local dev server with heroku local. My local browser always tries not to connect to localhost with SSL, so it just hangs.
I tried removing the site-specific cookies for localhost in the browser settings (Chrome) but localhost now still always tries to establish an SSL connection.
I am trying just to get back to using a non-SSL local connection for development. Any Ideas?
Django version 1.10.2
Heroku
Thanks
EDIT
Seems if I clear ALL the cache and cookies and restart the browser then it will not ask for SSL again. So it seems to be a browser problem. Anyway if anyone has an idea of how to accomplish this without having to clear all the data in Chrome, that would be appreciated.
UPDATE
I have learned a better way to handle this situation. I have set up some code to automatically sense if the software is running on the local environment or the cloud production environment, like this:
if os.environ.get('LOCAL'):
DEBUG = True
SECURE_SSL_REDIRECT = False
else:
DEBUG = False
SECURE_SSL_REDIRECT = True
Of course you have to take care of setting up the environ object, which happens automatically in heroku.
I am using custom settings both for local (development) and production environments.
For instance:
myproject/settings/dev.py
DEBUG = True
SECURE_SSL_REDIRECT = False
...
myproject/settings/production.py
DEBUG = False
SECURE_SSL_REDIRECT = True
...
And then I specify the settings I want to use. On localhost like this:
python myproject/manage.py runserver --settings=settings.dev
and for production using the Heroku Procfile:
web: gunicorn myproject.wsgi.production --log-file -
Content of myproject/wsgi/production.py is:
import os
os.environ.setdefault("DJANGO_SETTINGS_MODULE", "myproject.settings.production")
from django.core.wsgi import get_wsgi_application
application = get_wsgi_application()
If you use heroku local for your local development, you can create similar Procfile.local with local wsgi file.
I'm trying to run Django 1.4 with Pyton 2.7, Flup and mod_fastcgi on Apache. So what I did was:
Add mod_fastcgi to httpd.conf
Create two files : .htaccess and index.fcgi in my public web root inside a directory called portal - c:\xampp\htdocs\portal - the actual Django project is in d:\projects\portal so inside index.fcgi I have the following:
#!C:/Python27/python.exe
import sys, os
from django.core.servers.fastcgi import runfastcgi
sys.path.append("D:/projects/portal")
os.environ['DJANGO_SETTINGS_MODULE'] = "portal.settings"
runfastcgi(method="threaded", daemonize="false")
But when running localhost/portal/ I get the following error in my Apache error.log
File "C:\Python27\lib\site-packages\flup\server\fcgi_base.py", line 1020, in _setupSocket
'If you want FCGI, please create an external FCGI server '
It's telling me to create an external FCGI server which I could but I need it to be on a dynamic server. Any ideas?
Update:
My setup is on a Windows server. I can also start a Linux server so the question is more of Apache, FastCGI and adding sites witout changing any config on the server - Apache/FastCGI Server
You did not start your fcgi server.