使用 Apache 和 Daphne 提供静态文件
Serving static files with Apache and Daphne
我正在尝试学习使用 django-channels,并且已经完成了教程和 this multichat example。我现在正尝试使用 Apache 和 Daphne 将它部署在 Digital Ocean Droplet 上。
我有 2 个问题:
我的静态文件没有被使用(我有 运行 collectstatic)
我只能在输入 daphne
multichat.asgi:application --port 8001 --bind 0.0.0.0 -v2
时让 Daphne 工作
比使用下面的 daphne.service
文件
这是我的 Apache
conf 文件,我希望它能为静态文件提供服务:
<VirtualHost *:80>
ServerAdmin webmaster@hexiawebservices.co.uk
ServerName multichat.hexiawebservices.co.uk
ServerAlias www.multichat.hexiawebservices.co.uk
DocumentRoot /var/www/multichat
ProxyPreserveHost On
ProxyPass / http://0.0.0.0:8001/
ProxyPassReverse / http://0.0.0.0:8001/
Alias /robots.txt /var/www/multichat/static/robots.txt
Alias /favicon.ico /var/www/multichat/static/favicon.ico
Alias /media/ /var/www/multichat/media/
Alias /static/ /var/www/multichat/static/
<Directory /var/www/multichat/static>
Require all granted
</Directory>
<Directory /var/www/multichat/media>
Require all granted
</Directory>
<Directory /var/www/multichat/multichat>
<Files wsgi.py>
Require all granted
</Files>
</Directory>
ErrorLog ${APACHE_LOG_DIR}/error.log
CustomLog ${APACHE_LOG_DIR}/access.log combined
</VirtualHost>
这是我的 /etc/systemd/system/daphne.service
文件
[Unit]
Description=daphne daemon for multichat
After=network.target
[Service]
User=root
Group=www-data
WorkingDirectory=/var/www/multichat/multichat
ExecStart=/var/www/multichat/env/bin/daphne -b 0.0.0.0 -p 8001 multichat.asgi:application
# Not sure if should use 'on-failure' or 'always'.
Restart=on-failure
[Install]
WantedBy=multi-user.target
编辑 1
应要求,这里是routing.py
from django.urls import path
from channels.http import AsgiHandler
from channels.routing import ProtocolTypeRouter, URLRouter
from channels.auth import AuthMiddlewareStack
from chat.consumers import ChatConsumer
application = ProtocolTypeRouter({
"websocket": AuthMiddlewareStack(
URLRouter([
path("chat/stream/", ChatConsumer),
]),
),
})
一个consumers.py
from django.conf import settings
from channels.generic.websocket import AsyncJsonWebsocketConsumer
from .exceptions import ClientError
from .utils import get_room_or_error
class ChatConsumer(AsyncJsonWebsocketConsumer):
"""
This chat consumer handles websocket connections for chat clients.
It uses AsyncJsonWebsocketConsumer, which means all the handling functions
must be async functions, and any sync work (like ORM access) has to be
behind database_sync_to_async or sync_to_async. For more, read
http://channels.readthedocs.io/en/latest/topics/consumers.html
"""
##### WebSocket event handlers
async def connect(self):
"""
Called when the websocket is handshaking as part of initial connection.
"""
# Are they logged in?
if self.scope["user"].is_anonymous:
# Reject the connection
await self.close()
else:
# Accept the connection
await self.accept()
# Store which rooms the user has joined on this connection
self.rooms = set()
async def receive_json(self, content):
"""
Called when we get a text frame. Channels will JSON-decode the payload
for us and pass it as the first argument.
"""
# Messages will have a "command" key we can switch on
command = content.get("command", None)
try:
if command == "join":
# Make them join the room
await self.join_room(content["room"])
elif command == "leave":
# Leave the room
await self.leave_room(content["room"])
elif command == "send":
await self.send_room(content["room"], content["message"])
except ClientError as e:
# Catch any errors and send it back
await self.send_json({"error": e.code})
async def disconnect(self, code):
"""
Called when the WebSocket closes for any reason.
"""
# Leave all the rooms we are still in
for room_id in list(self.rooms):
try:
await self.leave_room(room_id)
except ClientError:
pass
##### Command helper methods called by receive_json
async def join_room(self, room_id):
"""
Called by receive_json when someone sent a join command.
"""
# The logged-in user is in our scope thanks to the authentication ASGI middleware
room = await get_room_or_error(room_id, self.scope["user"])
# Send a join message if it's turned on
if settings.NOTIFY_USERS_ON_ENTER_OR_LEAVE_ROOMS:
await self.channel_layer.group_send(
room.group_name,
{
"type": "chat.join",
"room_id": room_id,
"username": self.scope["user"].username,
}
)
# Store that we're in the room
self.rooms.add(room_id)
# Add them to the group so they get room messages
await self.channel_layer.group_add(
room.group_name,
self.channel_name,
)
# Instruct their client to finish opening the room
await self.send_json({
"join": str(room.id),
"title": room.title,
})
async def leave_room(self, room_id):
"""
Called by receive_json when someone sent a leave command.
"""
# The logged-in user is in our scope thanks to the authentication ASGI middleware
room = await get_room_or_error(room_id, self.scope["user"])
# Send a leave message if it's turned on
if settings.NOTIFY_USERS_ON_ENTER_OR_LEAVE_ROOMS:
await self.channel_layer.group_send(
room.group_name,
{
"type": "chat.leave",
"room_id": room_id,
"username": self.scope["user"].username,
}
)
# Remove that we're in the room
self.rooms.discard(room_id)
# Remove them from the group so they no longer get room messages
await self.channel_layer.group_discard(
room.group_name,
self.channel_name,
)
# Instruct their client to finish closing the room
await self.send_json({
"leave": str(room.id),
})
async def send_room(self, room_id, message):
"""
Called by receive_json when someone sends a message to a room.
"""
# Check they are in this room
if room_id not in self.rooms:
raise ClientError("ROOM_ACCESS_DENIED")
# Get the room and send to the group about it
room = await get_room_or_error(room_id, self.scope["user"])
await self.channel_layer.group_send(
room.group_name,
{
"type": "chat.message",
"room_id": room_id,
"username": self.scope["user"].username,
"message": message,
}
)
##### Handlers for messages sent over the channel layer
# These helper methods are named by the types we send - so chat.join becomes chat_join
async def chat_join(self, event):
"""
Called when someone has joined our chat.
"""
# Send a message down to the client
await self.send_json(
{
"msg_type": settings.MSG_TYPE_ENTER,
"room": event["room_id"],
"username": event["username"],
},
)
async def chat_leave(self, event):
"""
Called when someone has left our chat.
"""
# Send a message down to the client
await self.send_json(
{
"msg_type": settings.MSG_TYPE_LEAVE,
"room": event["room_id"],
"username": event["username"],
},
)
async def chat_message(self, event):
"""
Called when someone has messaged our chat.
"""
# Send a message down to the client
await self.send_json(
{
"msg_type": settings.MSG_TYPE_MESSAGE,
"room": event["room_id"],
"username": event["username"],
"message": event["message"],
},
)
这是帮我整理的 conf 文件:
<VirtualHost *:80>
ServerAdmin webmaster@hexiawebservices.co.uk
ServerName multichat.hexiawebservices.co.uk
ServerAlias www.multichat.hexiawebservices.co.uk
DocumentRoot /var/www/multichat
RewriteEngine on
RewriteCond %{HTTP:UPGRADE} ^WebSocket$ [NC,OR]
RewriteCond %{HTTP:CONNECTION} ^Upgrade$ [NC]
RewriteRule .* ws://0.0.0.0:8001%{REQUEST_URI} [P,QSA,L]
ProxyPreserveHost On
ProxyRequests Off
ProxyPassMatch ^/(ws(/.*)?)$ ws://0.0.0.0:8001/
ProxyPass / http://0.0.0.0:8001/
ProxyPassReverse / http://0.0.0.0:8001/
Alias /robots.txt /var/www/multichat/static/robots.txt
Alias /favicon.ico /var/www/multichat/static/favicon.ico
Alias /media/ /var/www/multichat/media/
Alias /static/ /var/www/multichat/static/
<Directory /var/www/multichat/static>
Require all granted
</Directory>
<Directory /var/www/multichat/media>
Require all granted
</Directory>
<Directory /var/www/multichat/multichat>
<Files wsgi.py>
Require all granted
</Files>
</Directory>
ErrorLog ${APACHE_LOG_DIR}/error.log
CustomLog ${APACHE_LOG_DIR}/access.log combined
</VirtualHost>
我正在尝试学习使用 django-channels,并且已经完成了教程和 this multichat example。我现在正尝试使用 Apache 和 Daphne 将它部署在 Digital Ocean Droplet 上。
我有 2 个问题:
我的静态文件没有被使用(我有 运行 collectstatic)
我只能在输入
daphne multichat.asgi:application --port 8001 --bind 0.0.0.0 -v2
时让 Daphne 工作 比使用下面的daphne.service
文件
这是我的 Apache
conf 文件,我希望它能为静态文件提供服务:
<VirtualHost *:80>
ServerAdmin webmaster@hexiawebservices.co.uk
ServerName multichat.hexiawebservices.co.uk
ServerAlias www.multichat.hexiawebservices.co.uk
DocumentRoot /var/www/multichat
ProxyPreserveHost On
ProxyPass / http://0.0.0.0:8001/
ProxyPassReverse / http://0.0.0.0:8001/
Alias /robots.txt /var/www/multichat/static/robots.txt
Alias /favicon.ico /var/www/multichat/static/favicon.ico
Alias /media/ /var/www/multichat/media/
Alias /static/ /var/www/multichat/static/
<Directory /var/www/multichat/static>
Require all granted
</Directory>
<Directory /var/www/multichat/media>
Require all granted
</Directory>
<Directory /var/www/multichat/multichat>
<Files wsgi.py>
Require all granted
</Files>
</Directory>
ErrorLog ${APACHE_LOG_DIR}/error.log
CustomLog ${APACHE_LOG_DIR}/access.log combined
</VirtualHost>
这是我的 /etc/systemd/system/daphne.service
文件
[Unit]
Description=daphne daemon for multichat
After=network.target
[Service]
User=root
Group=www-data
WorkingDirectory=/var/www/multichat/multichat
ExecStart=/var/www/multichat/env/bin/daphne -b 0.0.0.0 -p 8001 multichat.asgi:application
# Not sure if should use 'on-failure' or 'always'.
Restart=on-failure
[Install]
WantedBy=multi-user.target
编辑 1
应要求,这里是routing.py
from django.urls import path
from channels.http import AsgiHandler
from channels.routing import ProtocolTypeRouter, URLRouter
from channels.auth import AuthMiddlewareStack
from chat.consumers import ChatConsumer
application = ProtocolTypeRouter({
"websocket": AuthMiddlewareStack(
URLRouter([
path("chat/stream/", ChatConsumer),
]),
),
})
一个consumers.py
from django.conf import settings
from channels.generic.websocket import AsyncJsonWebsocketConsumer
from .exceptions import ClientError
from .utils import get_room_or_error
class ChatConsumer(AsyncJsonWebsocketConsumer):
"""
This chat consumer handles websocket connections for chat clients.
It uses AsyncJsonWebsocketConsumer, which means all the handling functions
must be async functions, and any sync work (like ORM access) has to be
behind database_sync_to_async or sync_to_async. For more, read
http://channels.readthedocs.io/en/latest/topics/consumers.html
"""
##### WebSocket event handlers
async def connect(self):
"""
Called when the websocket is handshaking as part of initial connection.
"""
# Are they logged in?
if self.scope["user"].is_anonymous:
# Reject the connection
await self.close()
else:
# Accept the connection
await self.accept()
# Store which rooms the user has joined on this connection
self.rooms = set()
async def receive_json(self, content):
"""
Called when we get a text frame. Channels will JSON-decode the payload
for us and pass it as the first argument.
"""
# Messages will have a "command" key we can switch on
command = content.get("command", None)
try:
if command == "join":
# Make them join the room
await self.join_room(content["room"])
elif command == "leave":
# Leave the room
await self.leave_room(content["room"])
elif command == "send":
await self.send_room(content["room"], content["message"])
except ClientError as e:
# Catch any errors and send it back
await self.send_json({"error": e.code})
async def disconnect(self, code):
"""
Called when the WebSocket closes for any reason.
"""
# Leave all the rooms we are still in
for room_id in list(self.rooms):
try:
await self.leave_room(room_id)
except ClientError:
pass
##### Command helper methods called by receive_json
async def join_room(self, room_id):
"""
Called by receive_json when someone sent a join command.
"""
# The logged-in user is in our scope thanks to the authentication ASGI middleware
room = await get_room_or_error(room_id, self.scope["user"])
# Send a join message if it's turned on
if settings.NOTIFY_USERS_ON_ENTER_OR_LEAVE_ROOMS:
await self.channel_layer.group_send(
room.group_name,
{
"type": "chat.join",
"room_id": room_id,
"username": self.scope["user"].username,
}
)
# Store that we're in the room
self.rooms.add(room_id)
# Add them to the group so they get room messages
await self.channel_layer.group_add(
room.group_name,
self.channel_name,
)
# Instruct their client to finish opening the room
await self.send_json({
"join": str(room.id),
"title": room.title,
})
async def leave_room(self, room_id):
"""
Called by receive_json when someone sent a leave command.
"""
# The logged-in user is in our scope thanks to the authentication ASGI middleware
room = await get_room_or_error(room_id, self.scope["user"])
# Send a leave message if it's turned on
if settings.NOTIFY_USERS_ON_ENTER_OR_LEAVE_ROOMS:
await self.channel_layer.group_send(
room.group_name,
{
"type": "chat.leave",
"room_id": room_id,
"username": self.scope["user"].username,
}
)
# Remove that we're in the room
self.rooms.discard(room_id)
# Remove them from the group so they no longer get room messages
await self.channel_layer.group_discard(
room.group_name,
self.channel_name,
)
# Instruct their client to finish closing the room
await self.send_json({
"leave": str(room.id),
})
async def send_room(self, room_id, message):
"""
Called by receive_json when someone sends a message to a room.
"""
# Check they are in this room
if room_id not in self.rooms:
raise ClientError("ROOM_ACCESS_DENIED")
# Get the room and send to the group about it
room = await get_room_or_error(room_id, self.scope["user"])
await self.channel_layer.group_send(
room.group_name,
{
"type": "chat.message",
"room_id": room_id,
"username": self.scope["user"].username,
"message": message,
}
)
##### Handlers for messages sent over the channel layer
# These helper methods are named by the types we send - so chat.join becomes chat_join
async def chat_join(self, event):
"""
Called when someone has joined our chat.
"""
# Send a message down to the client
await self.send_json(
{
"msg_type": settings.MSG_TYPE_ENTER,
"room": event["room_id"],
"username": event["username"],
},
)
async def chat_leave(self, event):
"""
Called when someone has left our chat.
"""
# Send a message down to the client
await self.send_json(
{
"msg_type": settings.MSG_TYPE_LEAVE,
"room": event["room_id"],
"username": event["username"],
},
)
async def chat_message(self, event):
"""
Called when someone has messaged our chat.
"""
# Send a message down to the client
await self.send_json(
{
"msg_type": settings.MSG_TYPE_MESSAGE,
"room": event["room_id"],
"username": event["username"],
"message": event["message"],
},
)
这是帮我整理的 conf 文件:
<VirtualHost *:80>
ServerAdmin webmaster@hexiawebservices.co.uk
ServerName multichat.hexiawebservices.co.uk
ServerAlias www.multichat.hexiawebservices.co.uk
DocumentRoot /var/www/multichat
RewriteEngine on
RewriteCond %{HTTP:UPGRADE} ^WebSocket$ [NC,OR]
RewriteCond %{HTTP:CONNECTION} ^Upgrade$ [NC]
RewriteRule .* ws://0.0.0.0:8001%{REQUEST_URI} [P,QSA,L]
ProxyPreserveHost On
ProxyRequests Off
ProxyPassMatch ^/(ws(/.*)?)$ ws://0.0.0.0:8001/
ProxyPass / http://0.0.0.0:8001/
ProxyPassReverse / http://0.0.0.0:8001/
Alias /robots.txt /var/www/multichat/static/robots.txt
Alias /favicon.ico /var/www/multichat/static/favicon.ico
Alias /media/ /var/www/multichat/media/
Alias /static/ /var/www/multichat/static/
<Directory /var/www/multichat/static>
Require all granted
</Directory>
<Directory /var/www/multichat/media>
Require all granted
</Directory>
<Directory /var/www/multichat/multichat>
<Files wsgi.py>
Require all granted
</Files>
</Directory>
ErrorLog ${APACHE_LOG_DIR}/error.log
CustomLog ${APACHE_LOG_DIR}/access.log combined
</VirtualHost>