在 systemd 上使用 daphne 运行 django 频道

Posted

技术标签:

【中文标题】在 systemd 上使用 daphne 运行 django 频道【英文标题】:Running django channels with daphne on systemd 【发布时间】:2018-03-21 04:43:17 【问题描述】:

首先,很抱歉这个问题太长了,希望你们中的一些人对此有耐心。

TL; DR:如何在 systemd 中正确加载 django 设置?

我正在关注本指南Deploying Django Channels Using Daphne,因此我可以运行一些实时应用程序(使用 WebSockets)。在没有 nginx 的情况下,从命令行运行 worker (python manage.py runworker) 和 interface (daphne),我可以访问正确的通道消费者类,如下面的日志所示(这些是从 javascript 客户端触发的) :

2017-10-09 21:10:35,210 - DEBUG - worker - Got message on websocket.connect (reply daphne.response.CYeWgnNQoY!mwuQrazQtv)
2017-10-09 21:10:35,211 - DEBUG - runworker - websocket.connect
2017-10-09 21:10:35,211 - DEBUG - worker - Dispatching message on websocket.connect to api.consumers.OrderConsumer
2017-10-09 21:10:48,132 - DEBUG - worker - Got message on websocket.receive (reply daphne.response.CYeWgnNQoY!mwuQrazQtv)
2017-10-09 21:10:48,132 - DEBUG - runworker - websocket.receive
2017-10-09 21:10:48,132 - DEBUG - worker - Dispatching message on websocket.receive to api.consumers.OrderConsumer

这些事件是由以下 javascript 调用触发的:

ws = new WebSocket("ws://localhost:8000/order/1/")
ws.send("test")

使用 nginx,并在 systemd 上同时运行 interface 和 worker,尽管使用了完全相同的触发器输入,但我得到了以下日志。

2017-10-09 20:38:35,503 - DEBUG - worker - Got message on websocket.connect (reply daphne.response.PPGuXtBmQD!EgUfaNZjUj)
2017-10-09 20:38:35,503 - DEBUG - runworker - websocket.connect
2017-10-09 20:38:35,503 - DEBUG - worker - Dispatching message on websocket.connect to channels.routing.connect_consumer
2017-10-09 20:38:42,993 - DEBUG - worker - Got message on websocket.receive (reply daphne.response.PPGuXtBmQD!EgUfaNZjUj)
2017-10-09 20:38:42,993 - DEBUG - runworker - websocket.receive
2017-10-09 20:38:42,993 - DEBUG - worker - Dispatching message on websocket.receive to channels.routing.null_consumer

请注意,接收通道正在路由到null_consumer。我相信这里的问题只是channels.routing 没有设置好。由于我在两个版本中使用相同的设置(Django 设置文件),这可能意味着设置本身没有被正确加载。请考虑以下文件。

## rest-api/farmaApp/settings.py

...
CHANNEL_LAYERS = 
    'default': 
        'BACKEND': 'asgi_redis.RedisChannelLayer',
        'CONFIG': 
            'hosts': [('localhost', 6379)],
        ,
        'ROUTING': 'farmaApp.routing.channel_routing',
    

...

应该将channels.routing 设置为:

## rest-api/farmaApp/routing.py

from channels.routing import route
from api.consumers import ws_connect, ws_disconnect, OrderConsumer

channel_routing = [
    route('websocket.connect', ws_connect, path=r'^/users/'),
    route('websocket.disconnect', ws_disconnect, path=r'^/users/'),
    OrderConsumer.as_route(path=r'^/order/(?P<order_id>[\d+])/'),
]

再次,我不认为配置本身是错误的,因为它可以在没有 systemd 的情况下工作。最后,这是我的 systemd 配置:

## /etc/systemd/system/daphne.service

[Unit]
Description=daphne daemon
After=network.target

[Service]
User=ubuntu
Group=www-data
WorkingDirectory=/home/ubuntu/rest-api
Environment=DJANGO_SETTINGS_MODULE=farmaApp.settings
ExecStart=/home/ubuntu/rest-api/env/bin/daphne --access-log /home/ubuntu/rest-api/access.log -b 0.0.0.0 -p 8001 farmaApp.asgi:channel_layer

[Install]
WantedBy=multi-user.target


## /etc/systemd/system/django_worker.service
[Unit]
Description=django_worker daemon
After=network.target

[Service]
User=ubuntu
Group=www-data
WorkingDirectory=/home/ubuntu/rest-api
Environment=DJANGO_SETTINGS_MODULE=farmaApp.settings
ExecStart=/home/ubuntu/rest-api/env/bin/python manage.py runworker -v 2

[Install]
WantedBy=multi-user.target

请注意我根据链接指南将 DJANGO_SETTINGS_MODULE 变量导出到环境的两个配置文件。我相信这没有按预期工作。

【问题讨论】:

我遇到了类似的问题并切换到 supervisord,但仍然必须在 conf 文件中列出完整的环境变量集。我太容易放弃了,所以如果你真的做到了,请发布你是如何使用通用环境文件完成的。我试图使用 EnvironmentFile 指令 - 也许你可以让它工作。 [没关系看到你有它 - 忽略此评论]或者可能需要在 daphne 服务部分设置 WorkingDirectory=/path/to/farmaApp。 @AMG 是否与 supervisord 一起使用?我可以试试。 【参考方案1】:

是的,supervisord 确实有效。 /etc/supervisor/conf.d/project_name.conf 的关键位是(注释部分中的一些进一步链接):

[program:platform_asgi_daphne]
; # https://***.com/questions/17055951/how-to-set-environment-variables-in-supervisor-service
; # https://github.com/django/daphne/pull/37
; # https://wiki.cac.washington.edu/display/infra/Extracting+Certificate+and+Private+Key+Files+from+a+.pfx+File
; # daphne -e ssl:8443:privateKey=localhost.key:certKey=localhost.crt <channel_layer>
environment =
    DJANGO_SECRET_KEY='xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx',
    DJANGO_DEBUG='True'
directory=/path/to/project/
; # http://channels.readthedocs.io/en/stable/deploying.html
; command=/path/to/venv/bin/daphne --port 80 --bind 0.0.0.0 classact.asgi:channel_layer
command=/path/to/venv/bin/daphne -e ssl:443:privateKey=../keys/server.key:certKey=../keys/server.crt --bind 0.0.0.0 projectfoldername.asgi:channel_layer
;user=webapps
;group=webapps
user=root
group=webapps

[program:platform_asgi_workers]
; # https://github.com/django/channels/issues/408#issuecomment-276384104
environment =
    DJANGO_SECRET_KEY='xxxxxxxxxxxxxxxxxxxxxxxxx',
    DJANGO_DEBUG='True'
command=/path/to/venv/bin/python /path/to/project/manage.py runworker
process_name=asgi_worker%(process_num)s
numprocs=2
;user=webapps
user=root
group=webapps

我作为用户 webapps 运行时遇到了一些问题,这些问题尚未解决,因此它们以 root 身份运行(写入具有奇数权限的加密文件夹)。我没有达到我没有在每个部分中重复一堆环境变量的地步(可能有一种方法)。还有其他几个,例如数据库、用户、静态根等,具体取决于平台(产品或开发)。

我还有一些证书要处理,因此也会显示出来(如果不需要,请取出 ssl 部分)。还要注意运行两个工人。

【讨论】:

【参考方案2】:

我刚刚部署了我的 django 频道应用程序,以下 systemd 服务文件在不使用主管的情况下为我工作:

/etc/systemd/system/django-channels-daphne.service

[Unit]
Description=daphne server script for my project
After=network.target

[Service]
User=webuser
Group=webuser
WorkingDirectory=/path/to/myproject
Environment=DJANGO_SECRET_KEY=xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
Environment=DJANGO_ALLOWED_HOSTS=myapp.chatbot.ai
ExecStart=/path/to/python/virtualenv/bin/daphne -b 0.0.0.0 -p 8000 myproject.asgi:channel_layer
Restart=always

[Install]
WantedBy=multi-user.target

/etc/systemd/system/django-channels-runworker.service

[Unit]
Description=python runworker server for myproject
After=network.target

[Service]
User=webuser
Group=webuser
WorkingDirectory=/path/to/myproject
Environment=DJANGO_SECRET_KEY=xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
Environment=DJANGO_ALLOWED_HOSTS=myapp.chatbot.ai
ExecStart=/path/to/python/virtualenv/bin/python /path/to/myproject/manage.py runworker --threads 4 
Restart=always

[Install]
WantedBy=multi-user.target

/path/to/myproject/myproject/asgi.py

import os
import channels

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

/path/to/myproject/myproject/settings.py 中的一些行:

ALLOWED_HOSTS = [os.environ['DJANGO_ALLOWED_HOSTS']]
SECRET_KEY = os.environ['DJANGO_SECRET_KEY']

【讨论】:

将 DJANGO_SECRET_KEY 和 DJANGO_ALLOWED_HOSTS 放在 daphne.service 文件中的目的是什么? @Darwin - 避免将其置于版本控制中或不得不在网络用户环境中全局设置。 还有其他方法可以做到这一点。值得注意的是,python-dotenv 文件允许您在 django settings.py 中指定文件 .env 的路径。我只是将.env 添加到.gitignore 并将其包含在脚本中。然后我 chmod 600 .env 将其锁定。【参考方案3】:

在您发布的配置中,daphne 端口似乎已关闭。

你写道:

ws = new WebSocket("ws://localhost:8000/order/1/")
ws.send("test")`

在您的 systemd 启动文件中使用 8001 时

ExecStart=/home/ubuntu/rest-api/env/bin/daphne --access-log /home/ubuntu/rest-api/access.log -b 0.0.0.0 -p 8001 farmaApp.asgi:channel_layer

【讨论】:

以上是关于在 systemd 上使用 daphne 运行 django 频道的主要内容,如果未能解决你的问题,请参考以下文章

在负载均衡器后面运行多个 daphne 实例:django-channels

在 nginx 后面使用 Daphne

使用daphne部署django channles websocket 项目

使用 Daphne 和 Nginx 部署 django 频道的问题

ERR_TOO_MANY_REDIRECTS 当我去域:Nginx,Daphne,Django,DigitalOcean

获取 AttributeError:“ASGIRequest”对象在 daphne django 中没有属性“get”