使用Apache指导流量在类似生产的环境中提供Django频道

时间:2017-06-14 21:44:01

标签: django websocket django-channels

我一直在玩Django频道一段时间,我一直坚持如何让它在本地开发服务器设置之外工作。在有人向我发贴文档页面之前,我已经用尽了搜索Django Channels文档以及我能找到的其他任何地方。我可以让本地设置工作正常,但不是外部。多年来我一直坚持使用Django开发服务器的理念,无论如何都是因为这种情况。

所以这是:

我有一个django网站,多年来一直由apache服务并使用LDAP用户身份验证(这个问题超出了我的控制范围并且支付等级)。我已经安装了Django频道asgi_redisredis-server,接口服务器Daphne自动安装了Django频道。我也在CentOS 6/7工作。

到目前为止,我已经知道我需要使用apache作为反向代理来与ASGI / Daphne交谈,但显然我找不到我需要的信息或自己弄明白。

这是我能想到的最接近的配置。我将我的apache配置文件设置为(URL是外部的,因为我的开发服务器是远程的;敏感信息当然是编辑的):

< VirtualHost *:80 >
    # Django Channels
    ProxyPass        "/ws/" "ws://192.168.xx.xx/"
    ProxyPassReverse "/ws/" "ws://192.168.xx.xx/"
    ProxyPass        "/"    "http://192.168.xx.xx/"
    ProxyPassReverse "/"    "http://192.168.xx.xx/"

    WSGIDaemonProcess dashboard_jnett python-path=/home/jnett/dashboard_jnett:/home/jnett/airview_env/lib/python2.7/site-packages
    WSGIScriptAlias /dashboard_jnett /home/jnett/dashboard_jnett/apache/dashboard_jnett.wsgi process-group=dashboard_jnett
    <Directory /home/jnett/dashboard_jnett>
        AuthType Basic
        AuthName "Web Utilities"
        AuthBasicProvider ldap
        AuthGroupFile /dev/null
        require valid-user
        AuthLDAPBindDN "uid=authenticate,ou=system,dc=intranet,dc=row44,dc=com"
        AuthLDAPBindPassword "xxxxxxx"
        AuthLDAPURL ldap://192.168.xx.xx/ou=users,dc=intranet,dc=row44,dc=com?cn??(&(objectclass=inetOrgPerson)(member=cn=status))
        Require ldap-filter objectClass=inetOrgPerson
    </Directory>

    Alias /static/dashboard_jnett /var/www/html/static/dashboard_jnett

    <Directory /var/www/html/static/dashboard_jnett>
        AllowOverride None
        Require all granted
        Options FollowSymLinks
    </Directory>
</VirtualHost>

我通过以下网址访问浏览器中的网站:http://192.168.xx.xx/dashboard_jnett/

在我的项目代码中,我有asgi.py文件:     进口口     import channels.asgi

os.environ.setdefault("DJANGO_SETTINGS_MODULE", "settings_jnett")
channel_layer = channels.asgi.get_channel_layer()

在ASGI文件中的那个设置文件settings_jnett.py引用中,我有:

import asgi_redis
...
CHANNEL_LAYERS = {
    "default": {
        "BACKEND": "asgi_redis.RedisChannelLayer",
        "CONFIG": {
            "hosts": [os.environ.get('REDIS_URL', 'redis://192.168.xx.xx:6379')],
            "prefix": u"dashboard_jnett",
        },
        "ROUTING": "routing.channel_routing",
    },
}

以及添加到INSTALLED_APPS的相应软件包,我遗漏了这些软件包。

这确实指向routing.py文件,其中包含:

from channels.routing import route
from channeltest.consumers import ws_connect, ws_message, ws_disconnect, http_consumer

channel_routing = [
    route("websocket.connect",    ws_connect),
    route("websocket.receive",    ws_message),
    route("websocket.disconnect", ws_disconnect),
    route("http.request",         consumers.http_consumer),
    #route("websocket.receive", "consumers.ws_message"),
]

从此consumers.py文件

导入
from django.http import HttpResponse
from channels.handler import AsgiHandler

from channels import Group
from channels.sessions import channel_session

# Connected to websocket.connect
@channel_session
def ws_connect(message):

    # Accept connection
    message.reply_channel.send({"accept": True})

    # Work out room name from path (ignore slashes)
    room = message.content['path'].strip("/")

    # Save room in session and add us to the group
    message.channel_session['room'] = room
    Group("chat-%s" % room).add(message.reply_channel)

# Connected to websocket.receive
@channel_session
def ws_message(message):
    Group("chat-%s" % message.channel_session['room']).send({
        "text": message['text'],
    })

# Connected to websocket.disconnect
@channel_session
def ws_disconnect(message):
    Group("chat-%s" % message.channel_session['room']).discard(message.reply_channel)

def http_consumer(message):
    response = HttpResponse("Hello world! You asked for %s" % message.content['path'])
    for chunk in AsgiHandler.encode_response(response):
        message.reply_channel.send(chunk)

我让Daphne在终端中运行:

[jnett@dev03.nlv ~/dashboard_jnett ]$(airview_env)[jnett@dev03.nlv ~/dashboard_jnett ]$daphne -b 192.168.xx.xx asgi:channel_layer --port 6379
2017-08-23 18:57:56,147 INFO     Starting server at tcp:port=6379:interface=192.168.xx.xx, channel layer asgi:channel_layer.
2017-08-23 18:57:56,147 INFO     HTTP/2 support not enabled (install the http2 and tls Twisted extras)
2017-08-23 18:57:56,147 INFO     Using busy-loop synchronous mode on channel layer
2017-08-23 18:57:56,148 INFO     Listening on endpoint tcp:port=6379:interface=192.168.xx.xx
2017-08-23 18:57:56,148 INFO     HTTPFactory starting on 6379
2017-08-23 18:57:56,148 INFO     Starting factory <daphne.http_protocol.HTTPFactory instance at 0x54aca28>

我在另一个终端运行:

[jnett@dev03.nlv ~/dashboard_jnett ]$python manage.py runworker
2017-06-14 20:46:47,988 - INFO - runworker - Using single-threaded worker.
2017-06-14 20:46:47,988 - INFO - runworker - Running worker against channel layer default (asgi_redis.core.RedisChannelLayer)
2017-06-14 20:46:47,989 - INFO - worker - Listening on channels http.request, websocket.connect, websocket.disconnect, websocket.receive

我的redis服务器在另一个终端上运行:

[jnett@dev03.nlv ~/dashboard_jnett ]$~jnett/redis-stable/src/redis-server 
...
10940:M 14 Jun 20:41:25.224 * The server is now ready to accept connections on port 6379

我甚至没有尝试使用网络套接字 - 我只是尝试提供正常的HTTP流量,但我似乎只能从apache获取代理错误:

  

代理错误

     

代理服务器收到来自上游服务器的无效响应。   代理服务器无法处理请求GET   / dashboard_jnett / channeltest /.

     

原因:从远程服务器读取错误

apache错误日志给了我很多行,比如

  

[Wed Jun 14 21:39:52.718388 2017] [proxy_http:error] [pid 13123]   (70007)指定的超时已过期:[client 192.168.xx.xx:51814]   AH01102:从远程服务器192.168.xx.xx:80读取状态行的错误   [Wed Jun 14 21:39:52.718426 2017] [代理:错误] [pid 13123] [客户   192.168.xx.xx:51814] AH00898:从/ dashboard_jnett / channeltest /

返回的远程服务器读取时出错

有没有人成功将这样的设置投入生产?我希望如果我能弄明白如何让apache正确地将流量引导到Daphne以获得正常的HTTP,我可以从那里找到我的方式。

1 个答案:

答案 0 :(得分:0)

我也很难使它在我的Raspberry上运行,但最终我做到了。

来自https://mikesmithers.wordpress.com/2017/02/21/configuring-django-with-apache-on-a-raspberry-pi/ 我有很好的建议。

Apache需要一些其他软件包来提供Django应用程序中的页面。

sudo apt-get install apache2-dev
sudo apt-get install libapache2-mod-wsgi-py3

还必须设置MPM(多处理模块)。

a2dismod mpm_prefork
a2enmod mpm_worker
service apache2 restart

从Django渠道文档中创建asgi.py示例

https://channels.readthedocs.io/en/latest/deploying.html#run-protocol-servers

就我而言,我还必须将sys路径添加到我的项目中

"""
ASGI entrypoint. Configures Django and then runs the 
application
defined in the ASGI_APPLICATION setting.
"""

import os
import sys
import django
from channels.routing import get_default_application

sys.path.append("/home/pi/Dev/WeatherStation")
os.environ.setdefault("DJANGO_SETTINGS_MODULE", 
"WeatherStation.settings")
django.setup()
application = get_default_application()

达芙妮现在不应该抱怨

daphne -p 8001 WeatherStation.asgi:application

在Apache中为Websocket配置ASGI和Daphne,对HTTP请求使用Apache。 Apache就像反向代理一样,将所有websocket请求重定向到运行在不同端口上的Daphne服务器

leafpad /etc/apache2/sites-available/000-default.conf

和内容

...
        RewriteEngine on
        RewriteCond %{HTTP:UPGRADE} ^WebSocket$ [NC,OR]
        RewriteCond %{HTTP:CONNECTION} ^Upgrade$ [NC]
        RewriteRule .* ws://127.0.0.1:8001%{REQUEST_URI} [P,QSA,L]


Alias /static /home/pi/Dev/WeatherStation/static
    <Directory /home/pi/Dev/WeatherStation/static> 
        Require all granted
    </Directory>

    <Directory /home/pi/Dev/WeatherStation/WeatherStation>
        <Files wsgi.py>
            Require all granted
        </Files>
    </Directory>

    WSGIDaemonProcess Dev python-path=/home/pi/Dev python-home=/home/pi/Dev/WSenv
    WSGIProcessGroup Dev
    WSGIScriptAlias / /home/pi/Dev/WeatherStation/WeatherStation/wsgi.py
</VirtualHost>

确保Apache有权访问您的数据库和其他内容

chmod g+w ~/dvds/db.sqlite3
chmod g+w ~/dvds
sudo chown :www-data db.sqlite3
sudo chown :www-data ~/dvds

重新启动Apache,以使这些更改生效:

sudo service apache2 restart

现在您已经有一个运行在Apache中的WSGI服务器和一个用于网络套接字的Daphne服务器