1. ASGI简介
在Django中 , ASGI ( Asynchronous Server Gateway Interface ) 的引入使得Django应用能够支持异步编程 .
从Django 3.0 开始 , Django就增加了对ASGI的支持 , 但直到Django 3.1 才正式推荐在生产环境中使用ASGI .
ASGI是一个用于Python的异步Web服务器的标准接口 , 它允许你运行异步的Django应用 .
要启动Django的ASGI服务器 , 通常需要使用一个支持ASGI的服务器 , 如Daphne , Uvicorn或Hypercorn .
以下是使用Uvicorn作为ASGI服务器来启动Django应用的基本步骤 :
* 1. 确保Django版本 : 首先 , 确保Django版本是 3.1 或更高 , 因为这些版本才正式支持ASGI .
* 2. 设置ASGI应用 : 在Django项目的settings . py文件中 , 确保已经设置了ASGI_APPLICATION变量 ,
它应该指向项目中的asgi . py文件中的application对象 .
ASGI_APPLICATION = 'MyDjango.asgi.application'
* 3. 检查asgi . py : 在Django项目的根目录下 , 有一个asgi . py文件 .
这个文件是Django自动生成的 , 用于配置ASGI应用 , 它通常看起来像这样 :
import os
from django. core. asgi import get_asgi_application
os. environ. setdefault( 'DJANGO_SETTINGS_MODULE' , 'MyDjango.settings' )
application = get_asgi_application( )
* 4. 安装ASGI服务器 : 接下来 , 需要安装一个ASGI服务器 .
以Uvicorn为例 , 可以通过pip安装它 : pip install uvicorn
* 5. 启动ASGI服务器 : 最后 , 使用Uvicorn ( 或其他ASGI服务器 ) 来启动你的Django应用 .
在命令行中 , 运行命令 : uvicorn MyDajngo . asgi : application --host 0.0 .0 .0 --port 8000 .
这里的 'MyDajngo' 是的Django项目的名称 ,
--host 0.0 .0 .0 表示服务器将监听所有可用的网络接口 , --port 8000 指定了服务器将使用的端口号 .
注意 : 在settings . py文件中设置了ASGI_APPLICATION = 'MyDjango.asgi.application' , 是可以直接通过快捷按钮启动的 .
* 6. 访问应用 : 现在 , Django应用已经以异步方式在ASGI服务器上运行了 .
可以在浏览器中访问 : http : / / 127.0 .0 .1 : 8000 来查看应用 .
由于ASGI是异步的 , 需要修改视图和其他代码来利用异步编程的优势 .
Django的ORM本身并不支持异步操作 , 因此如果需要在异步视图中进行数据库操作 ,
可能需要使用支持异步的数据库客户端库 , 如databases或aiopg .
此外 , 虽然ASGI为Django应用带来了异步编程的可能性 , 但它并不是必需的 .
如果应用不需要异步特性 , 仍然可以像往常一样使用WSGI服务器 ( 如Gunicorn或uWSGI ) 来运行你的Django应用 .
Django的路由系统 ( URLs ) 和视图 ( Views ) 是以同步方式编写的 , 即使使用ASGI来运行Django应用 .
然而 , 对于需要异步处理的功能 , 如WebSocket连接和异步HTTP请求 , 使用Django Channels是一个很好的解决方案 .
2. Channels简介
Channels是一个允许编写异步消费者 ( consumers ) 的框架 , 这些消费者可以处理WebSocket连接 , 异步HTTP请求等 .
Channels建立在ASGI之上 , 允许编写真正的异步代码 , 而不会阻塞Django的事件循环 .
Channels提供了与Django路由系统相似的机制 , 但它专注于异步操作 .
在Channels中定义路由将不同的协议 ( 如 : HTTP , WebSocket ) 和路径映射到相应的异步消费者上 .
以下是如何在Django项目中设置Channels的基本步骤 :
* 1. 安装 Channels : pip install channels .
* 2. 修改项目的settings . py .
将channels添加到INSTALLED_APPS中 .
设置ASGI_APPLICATION指向ASGI配置文件 .
由于Channels的功能依赖于Redis数据库 , 因此还需要在settings . py中设置Channels的功能配置 .
INSTALLED_APPS = [
. . .
'channels' ,
]
ASGI_APPLICATION = 'MyDjango.routing.application'
CHANNEL_LAYERS = {
'default' : {
'BACKEND' : 'channels_redis.core.RedisChannelLayer' ,
'CONFIG' : {
"hosts" : [ ( '127.0.0.1' , 6379 ) ] ,
} ,
} ,
}
* 3. 执行数据迁移 , 因为Channels需要使用Django内置的会话Session机制 , 用于区分和识别每个用户的身份信息 .
执行 : Python manage . py migrate .
* 4. 在应用中定义Channels路由 .
在应用目录下创建一个routing . py文件 , 并定义WebSocket路由 .
import os
from django. core. asgi import get_asgi_application
from channels. routing import ProtocolTypeRouter, URLRouter
from channels. auth import AuthMiddlewareStack
from index import routing
os. environ. setdefault( 'DJANGO_SETTINGS_MODULE' , 'MyDjango.settings' )
application = ProtocolTypeRouter( {
"http" : get_asgi_application( ) ,
"websocket" : AuthMiddlewareStack(
URLRouter(
routing. websocket_urlpatterns
)
) ,
} )
* 5. 设置WebSocket URL模式 .
在路由文件中定义WebSocket的URL模式 .
from django. urls import path, include
from . consumers import IndexConsumer
urlpatterns = [
path( '' , include( ( 'index.urls' , 'index' ) , namespace= 'index' ) )
]
websocket_urlpatterns = [
path( 'ws/index/' , IndexConsumer. as_asgi( ) ) ,
]
* 6. 编写消费者 .
在应用目录下创建一个consumers . py文件 , 并编写异步消费者 .
from channels. generic. websocket import AsyncWebsocketConsumer
import json
class IndexConsumer ( AsyncWebsocketConsumer) :
async def connect ( self) :
print ( "一个客户端连接了服务器" )
await self. accept( )
async def disconnect ( self, close_code) :
print ( "一个客户端断开了连接" )
pass
async def receive ( self, text_data= None , bytes_data= None ) :
text_data_json = json. loads( text_data)
message = text_data_json[ 'message' ]
print ( message)
await self. send( text_data= json. dumps( {
'message' : message
} ) )
异步方法的调用机 :
* connect ( ) 方法 : 当WebSocket客户端尝试连接到服务器时 , Channels会自动调用connect ( ) 方法 .
可以在这个方法中执行一些初始化操作 , 比如验证客户端身份 , 设置会话变量等 , 并通过调用await self . accept ( ) 来接受连接 .
* disconnect ( ) 方法 : 当WebSocket连接关闭时 , Channels会自动调用disconnect ( ) 方法 .
可以在这个方法中执行一些清理操作 .
* receive ( ) 方法 : 当WebSocket客户端发送消息到服务器时 , Channels会自动调用receive ( ) 方法 , 并将接收到的消息作为参数传递 .
需要在这个方法中处理接收到的消息 , 并可能通过await self . send ( ) 或await self . send_json ( ) 等方法向客户端发送响应 .
3. WebSocket介绍
在Django Channels中 , WebSocket并不是通过传统的HTTP请求来访问的 ,
因此不能直接通过浏览器地址栏 ( 如输入 : http : / / localhost : 8000 /ws/my-endpoint/)来访问WebSocket端点.
WebSocket是一种在单个TCP连接上进行全双工通讯的协议 , 它允许服务器和客户端之间建立持久的连接 , 并通过这个连接进行数据的双向传输 .
浏览器可以通过JavaScript的WebSocket API来建立与WebSocket服务器的连接 .
这个API允许创建一个WebSocket对象 , 并指定要连接的URL .
注意 , 这个URL应该以 : ws : / / ( 非加密 ) 或wss : / / ( 加密 ) 开头 , 而不是 http : / / 或https : / / .
然后 , 可以使用这个对象来发送和接收数据 .
以下是一个简单的例子 , 展示了如何在浏览器中使用JavaScript来连接WebSocket服务器 :
* 1. 编写路由 , 后续可以通过 : 127.0 .0 .1 : 8000 , 触发index视图函数 .
from django. urls import path
from . views import *
urlpatterns = [
path( '' , index, name= 'index' ) ,
]
* 2. 编写视图函数 .
在视图函数中放回一个模板页码 .
* 3. 编写模板页面 .
在模块页面中编写js代码 , 使用WebSocket API来建立与WebSocket服务器的连接 .
<! DOCTYPE html >
< html lang = " en" >
< head>
< meta charset = " UTF-8" >
< title> 访问异步视图</ title>
</ head>
< body>
< p> 访问: 异步视图...</ p>
< script>
var chatSocket = new WebSocket ( 'ws://' + window. location. host + '/ws/index/' ) ;
chatSocket. onopen = function ( event ) {
console. log ( 'WebSocket连接成功' ) ;
chatSocket. send ( JSON . stringify ( {
'message' : '你好!'
} ) ) ;
} ;
chatSocket. onmessage = function ( e ) {
try {
var data = JSON . parse ( e. data) ;
var message = data[ 'message' ] ;
alert ( message) ;
} catch ( error) {
console. error ( '解析消息时出错:' , error) ;
}
} ;
chatSocket. onclose = function ( event ) {
if ( event. wasClean) {
console. log ( 'WebSocket连接正常关闭' ) ;
} else {
console. error ( 'WebSocket连接异常关闭' ) ;
}
console. log ( '关闭代码:' , event. code, '关闭原因:' , event. reason) ;
} ;
chatSocket. onerror = function ( error ) {
console. error ( 'WebSocket发生错误:' , error) ;
} ;
</ script>
</ body>
</ html>
* 4. 启动项目 , 访问 : 127.0 .0 .1 : 8000 , 执行index视图函数 .
视图函数返回的页面会执行js代码先访问 : ws : / / 127.0 .0 .0 : 8000 /ws/index/ , 执行异步视图 .
结论 : 通过Django Channels , 可以编写异步的WebSocket消费者和其他类型的异步协议处理器 , 从而充分利用ASGI的优势 .
虽然Django的路由系统 ( URLs ) 和视图 ( Views ) 主要用于同步操作 , 但Channels提供了与Django路由相似的机制 , 专门用于异步操作 .