Django Channels WebSocket实时通信实战:从聊天功能到消息推送

引言

在Web开发中,实时通信功能(如在线聊天、实时通知、数据推送)已成为许多应用的核心需求。传统的HTTP协议由于其请求-响应模式的限制,无法高效实现实时通信。WebSocket作为一种全双工通信协议,为实时Web应用提供了理想的解决方案。本文将详细介绍如何使用Django Channels构建WebSocket应用,实现实时聊天和后端主动消息推送功能。

一、技术背景

1.1 Django Channels简介

Django Channels是Django官方提供的扩展,它将Django的功能扩展到HTTP之外,支持WebSocket、聊天协议、IoT协议等。Channels基于ASGI(Asynchronous Server Gateway Interface)规范构建,在保留Django核心功能的同时,引入了异步处理能力,使Django能够处理长期运行的连接。

1.2 ASGI工作原理

ASGI(异步服务器网关接口)是Python Web应用程序的异步标准,旨在替代WSGI。它将网络请求分为三个处理层面:

  1. 协议服务器(Interface Server):负责解析不同的网络协议(HTTP、WebSocket等)
  2. 频道层(Channel Layer):基于消息队列的通信系统,实现不同消费者之间的通信
  3. 消费者(Consumer):处理具体的业务逻辑,类似于Django视图,但支持异步操作

二、环境准备

2.1 技术栈版本说明

组件版本说明
Python3.6+编程语言
Django2.2+Web框架
Channels2.4+Django异步扩展
channels-redis2.4+Redis频道层后端
Redis5.0+消息代理
jQuery3.5.0前端JavaScript库
Bootstrap3.3.7前端UI框架

2.2 安装依赖

# 创建虚拟环境
python -m venv venv
source venv/bin/activate  # Linux/Mac
venv\Scripts\activate     # Windows# 安装Django及Channels
pip install django==2.2 channels==2.4.0 channels-redis==2.4.2# 确保Redis已安装并启动
# Ubuntu示例
sudo apt-get install redis-server
sudo systemctl start redis-server# 验证Redis是否运行
redis-cli ping  # 应返回PONG

三、项目创建与配置

3.1 创建项目和应用

# 创建Django项目
django-admin startproject mysite# 进入项目目录
cd mysite# 创建聊天应用
python manage.py startapp chat

3.2 配置settings.py

修改mysite/settings.py文件,添加Channels和应用配置:

# mysite/settings.pyINSTALLED_APPS = ['django.contrib.admin','django.contrib.auth','django.contrib.contenttypes','django.contrib.sessions','django.contrib.messages','django.contrib.staticfiles','chat.apps.ChatConfig',  # 添加聊天应用'channels',  # 添加Channels
]# 配置ASGI应用
ASGI_APPLICATION = 'mysite.routing.application'# 配置Channel Layer(使用Redis)
CHANNEL_LAYERS = {'default': {'BACKEND': 'channels_redis.core.RedisChannelLayer','CONFIG': {"hosts": [('127.0.0.1', 6379)],  # Redis服务器地址,本地使用127.0.0.1},},
}

四、前端实现

4.1 创建房间选择页面

chat目录下创建templates/chat目录,并创建index.html

<!-- chat/templates/chat/index.html -->
<!DOCTYPE html>
<html>
<head><meta charset="utf-8" /><title>Chat Rooms</title>
</head>
<body>What chat room would you like to enter?<br><input id="room-name-input" type="text" size="100"><br><input id="room-name-submit" type="button" value="Enter"><script>// 自动聚焦到输入框document.querySelector('#room-name-input').focus();// 回车触发提交document.querySelector('#room-name-input').onkeyup = function(e) {if (e.keyCode === 13) {  // Enter键document.querySelector('#room-name-submit').click();}};// 点击提交按钮进入聊天室document.querySelector('#room-name-submit').onclick = function(e) {var roomName = document.querySelector('#room-name-input').value;window.location.pathname = '/chat/' + roomName + '/';};</script>
</body>
</html>

4.2 创建聊天与推送页面

创建chat/templates/chat/room.html文件:

<!-- chat/templates/chat/room.html -->
<!DOCTYPE html>
<html>
<head><!-- 引入jQuery和Bootstrap --><script src="https://cdn.bootcdn.net/ajax/libs/jquery/3.5.0/jquery.min.js" type="text/javascript"></script><link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bootstrap@3.3.7/dist/css/bootstrap.min.css"><script src="https://cdn.jsdelivr.net/npm/bootstrap@3.3.7/dist/js/bootstrap.min.js"></script><meta charset="utf-8" /><title>Chat Room</title>
</head>
<body><!-- 聊天日志区域 --><textarea id="chat-log" cols="150" rows="30" class="text"></textarea><br><!-- 消息输入区域 --><input id="chat-message-input" type="text" size="150"><br><input id="chat-message-submit" type="button" value="发送消息" class="input-sm"><!-- 实时推送按钮 --><button id="get_data" class="btn btn-success">获取后端数据</button><!-- 房间名称(通过Django模板传递) -->{{ room_name|json_script:"room-name" }}<script>// 获取房间名称const roomName = JSON.parse(document.getElementById('room-name').textContent);// 建立聊天WebSocket连接const chatSocket = new WebSocket('ws://' + window.location.host + '/ws/chat/' + roomName + '/');// 建立推送WebSocket连接const pushSocket = new WebSocket('ws://' + window.location.host + '/ws/push/' + roomName);// 处理聊天消息接收chatSocket.onmessage = function(e) {const data = JSON.parse(e.data);document.querySelector('#chat-log').value += (data.message + '\n');};// 处理推送消息接收pushSocket.onmessage = function(e) {const data = JSON.parse(e.data);document.querySelector('#chat-log').value += (data.message + '\n');};// 处理连接关闭chatSocket.onclose = function(e) {console.error('Chat socket closed unexpectedly');};pushSocket.onclose = function(e) {console.error('Push socket closed unexpectedly');};// 消息输入框事件处理document.querySelector('#chat-message-input').focus();document.querySelector('#chat-message-input').onkeyup = function(e) {if (e.keyCode === 13) {  // Enter键发送消息document.querySelector('#chat-message-submit').click();}};// 发送消息按钮点击事件document.querySelector('#chat-message-submit').onclick = function(e) {const messageInputDom = document.querySelector('#chat-message-input');const message = messageInputDom.value;// 发送消息到WebSocketchatSocket.send(JSON.stringify({'message': message}));// 清空输入框messageInputDom.value = '';};// "获取后端数据"按钮点击事件$("#get_data").click(function() {$.ajax({url: "{% url 'push' %}",type: "GET",data: {"room": "{{ room_name }}","csrfmiddlewaretoken": "{{ csrf_token }}"},});});</script>
</body>
</html>

五、后端实现

5.1 编写视图函数

修改chat/views.py文件:

# chat/views.py
from django.shortcuts import render
from django.http import JsonResponse
from channels.layers import get_channel_layer
from asgiref.sync import async_to_syncdef index(request):"""房间选择页面视图"""return render(request, "chat/index.html")def room(request, room_name):"""聊天室页面视图"""return render(request, "chat/room.html", {"room_name": room_name})def push_redis(request):"""触发后端主动推送消息的视图"""room_name = request.GET.get("room")# 定义推送消息的函数def push_message(message):channel_layer = get_channel_layer()# 使用async_to_sync将异步函数转换为同步调用async_to_sync(channel_layer.group_send)(room_name,  # 房间组名称{"type": "push.message",  # 对应消费者中的方法名"message": message,"room_name": room_name})# 发送测试消息push_message("后端开始实时推送数据...")return JsonResponse({"status": "success"})

5.2 配置URL路由

应用路由(chat/urls.py)

创建chat/urls.py文件:

# chat/urls.py
from django.urls import path
from . import viewsurlpatterns = [path('', views.index, name='index'),path('<str:room_name>/', views.room, name='room'),
]
项目路由(mysite/urls.py)

修改项目根路由:

# mysite/urls.py
from django.contrib import admin
from django.urls import path, include
from chat.views import push_redisurlpatterns = [path('admin/', admin.site.urls),path('chat/', include("chat.urls")),  # 聊天应用路由path('push', push_redis, name="push"),  # 推送触发路由
]

5.3 实现WebSocket消费者

创建chat/consumers.py文件,实现WebSocket消息处理逻辑:

# chat/consumers.py
import time
import json
from channels.generic.websocket import AsyncWebsocketConsumer, WebsocketConsumer
from asgiref.sync import async_to_syncclass ChatConsumer(AsyncWebsocketConsumer):"""异步聊天消费者"""async def connect(self):"""建立WebSocket连接时调用"""# 从URL中获取房间名称self.room_name = self.scope["url_route"]["kwargs"]["room_name"]# 构造房间组名称self.room_group_name = f"chat_{self.room_name}"# 将当前连接加入房间组await self.channel_layer.group_add(self.room_group_name,self.channel_name)# 接受WebSocket连接await self.accept()async def disconnect(self, close_code):"""关闭WebSocket连接时调用"""# 将连接从房间组中移除await self.channel_layer.group_discard(self.room_group_name,self.channel_name)async def receive(self, text_data=None, bytes_data=None):"""从WebSocket接收消息时调用"""text_data_json = json.loads(text_data)message = text_data_json["message"]# 将消息发送到房间组中的所有连接await self.channel_layer.group_send(self.room_group_name,{"type": "chat_message",  # 调用chat_message方法处理消息"message": message})async def chat_message(self, event):"""处理房间组消息并发送到WebSocket"""message = event["message"]response_message = f"[收到消息] {message}"# 将消息发送回前端await self.send(text_data=json.dumps({"message": response_message}))class PushMessage(WebsocketConsumer):"""同步推送消费者,实现后端主动推送功能"""def connect(self):"""建立WebSocket连接时调用"""self.room_group_name = self.scope["url_route"]["kwargs"]["room_name"]# 将当前连接加入房间组(同步方式)async_to_sync(self.channel_layer.group_add)(self.room_group_name,self.channel_name)self.accept()def disconnect(self, close_code):"""关闭WebSocket连接时调用"""# 将连接从房间组中移除(同步方式)async_to_sync(self.channel_layer.group_discard)(self.room_group_name,self.channel_name)def push_message(self, event):"""处理推送消息并发送到WebSocket"""# 模拟实时数据推送while True:time.sleep(2)  # 每2秒推送一次current_time = time.strftime("%Y-%m-%d %H:%M:%S")message = f"[{current_time}] 实时推送 - 房间: {event['room_name']}"# 发送消息到前端self.send(text_data=json.dumps({"message": message}))

5.4 配置WebSocket路由

应用WebSocket路由(chat/routing.py)

创建chat/routing.py文件:

# chat/routing.py
from django.urls import re_path, path
from . import consumerswebsocket_urlpatterns = [# 聊天WebSocket路由re_path(r'ws/chat/(?P<room_name>\w+)/$', consumers.ChatConsumer.as_asgi()),# 推送WebSocket路由path('ws/push/<room_name>', consumers.PushMessage),
]
项目ASGI路由(mysite/routing.py)

创建项目级ASGI路由文件:

# mysite/routing.py
from channels.routing import ProtocolTypeRouter, URLRouter
from channels.auth import AuthMiddlewareStack
import chat.routingapplication = ProtocolTypeRouter({# WebSocket路由配置"websocket": AuthMiddlewareStack(  # 支持Django认证URLRouter(chat.routing.websocket_urlpatterns  # 导入应用WebSocket路由)),
})

六、项目结构

最终项目文件结构如下:

mysite/                  # 项目根目录
├── chat/                # 聊天应用目录
│   ├── __init__.py
│   ├── admin.py
│   ├── apps.py
│   ├── consumers.py     # WebSocket消费者
│   ├── migrations/
│   ├── models.py
│   ├── routing.py       # 应用WebSocket路由
│   ├── templates/       # 模板目录
│   │   └── chat/
│   │       ├── index.html  # 房间选择页面
│   │       └── room.html   # 聊天与推送页面
│   ├── tests.py
│   ├── urls.py          # 应用URL路由
│   └── views.py         # 视图函数
├── manage.py
├── mysite/              # 项目配置目录
│   ├── __init__.py
│   ├── asgi.py          # ASGI配置
│   ├── settings.py      # 项目设置
│   ├── routing.py       # 项目ASGI路由
│   ├── urls.py          # 项目URL路由
│   └── wsgi.py
└── venv/                # 虚拟环境

七、运行与测试

7.1 启动方式一:使用Django开发服务器

python manage.py runserver 0.0.0.0:8000

7.2 启动方式二:使用Daphne ASGI服务器(推荐生产环境)

# 安装Daphne(通常已随Channels一起安装)
pip install daphne# 启动ASGI服务器
daphne -b 0.0.0.0 -p 8000 mysite.asgi:application

7.3 测试步骤

  1. 访问房间选择页面:打开浏览器访问 http://127.0.0.1:8000/chat/
  2. 创建/加入房间:输入房间名称(如"testroom"),点击"Enter"进入聊天室
  3. 测试聊天功能:在输入框中输入消息,点击"发送消息",查看聊天日志
  4. 测试实时推送:点击"获取后端数据"按钮,应看到每2秒收到一条实时推送消息
  5. 多客户端测试:打开另一个浏览器窗口,加入相同房间,验证消息同步

八、常见问题解决

8.1 Redis连接失败

问题:启动时报错 Could not connect to Redis at 127.0.0.1:6379: Connection refused

解决

  • 确认Redis服务是否已启动:sudo systemctl start redis-server
  • 检查Redis配置是否正确,特别是主机地址和端口
  • 测试Redis连接:redis-cli ping 应返回 PONG

8.2 WebSocket连接失败

问题:浏览器控制台显示 WebSocket connection failed

解决

  • 确认Channels已正确安装并添加到INSTALLED_APPS
  • 检查ASGI_APPLICATION配置是否正确指向routing.application
  • 验证WebSocket路由配置是否正确
  • 确保使用支持WebSocket的浏览器

8.3 异步与同步混合问题

问题:在同步上下文中调用异步函数导致错误

解决

  • 使用async_to_sync将异步函数转换为同步调用:from asgiref.sync import async_to_sync
  • 区分异步消费者(AsyncWebsocketConsumer)和同步消费者(WebsocketConsumer)的使用场景

九、总结

本文详细介绍了使用Django Channels实现WebSocket实时通信的完整流程,包括:

  1. 技术背景:Django Channels和ASGI的基本概念
  2. 环境搭建:依赖安装和项目配置
  3. 前端实现:房间选择和聊天界面
  4. 后端实现:视图函数、URL路由和WebSocket消费者
  5. 运行测试:两种启动方式和功能测试步骤

通过这个实例,我们实现了一个具有实时聊天和后端主动推送功能的Web应用。Django Channels不仅扩展了Django的能力,还保持了Django的易用性,使开发者能够轻松构建复杂的实时Web应用。

扩展思考

  • 如何添加用户认证功能?
  • 如何实现私聊功能?
  • 如何处理WebSocket连接的断线重连?
  • 如何在生产环境中部署Channels应用?

这些问题可以通过深入学习Django Channels官方文档和实践进一步探索和解决。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。
如若转载,请注明出处:http://www.pswp.cn/bicheng/87703.shtml
繁体地址,请注明出处:http://hk.pswp.cn/bicheng/87703.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

day52 神经网络调参指南

目录 随机种子 内参的初始化 神经网络调参指南 参数的分类 调参顺序 初始化参数 batchsize的选择 学习率调整 激活函数的选择 损失函数的选择 模型架构中的参数 正则化系数 其他补充 随机种子 import torch import torch.nn as nn# 定义简单的线性模型&#xf…

.NET9 实现斐波那契数列(FibonacciSequence)性能测试

在 .NET 平台上实现 斐波那契数列 并使用 BenchmarkDotNet 进行性能测试&#xff0c;是评估不同算法实现方式性能表现的一种高效且标准化的方法。通过该方式&#xff0c;可以对比递归、迭代、记忆化递归以及结合高性能优化技术&#xff08;如 Span<T>、Memory<T> 和…

三、docker软件安装:gitlab,nexus,mysql8,redis,nacos,nginx

目录 1.gitlab安装 2.nexus安装 (1)下载启动 (2)设置中央仓库远程地址 (3)配置maven的settings.xml 3.mysql8安装 4.redis安装 5.nacos安装 6.nginx安装 1.gitlab安装 #创建目录 cd /usr/local/ mkdir docker cd docker/ mkdir gitlab_docker cd gitlab_docker…

【与AI+】SAP WEBGUI集成开发与SAP INTERNET服务的关系

前言&#xff1a;这是我的水水专栏第五篇文章&#xff0c;这个专栏呢&#xff0c;是放一些我向AI提问的问题&#xff0c;以及AI的回答。因为感觉真的好方便哈哈哈~ 我不是很确定我的专栏文章内容是否涉及版权&#xff0c;以及也不确定这些整合过的文字是否涉嫌抄袭&#xff0c…

浅谈几种js设计模式

JavaScript设计模式是开发中常用的一种解决方案&#xff0c;它们帮助开发者以一种更结构化、更易维护的方式编写代码。本文将深入介绍几种常见的JavaScript设计模式&#xff0c;包括单例模式、工厂模式、观察者模式和策略模式。 一、单例模式&#xff08;Singleton Pattern&am…

手写 Vue 中虚拟 DOM 到真实 DOM 的完整过程

目录 一、虚拟 DOM 的核心概念 二、虚拟 DOM 到真实 DOM 的流程 三、手写虚拟 DOM 到真实 DOM 的实现 1. 定义虚拟 DOM 的结构&#xff08;VNode&#xff09; 2. 创建虚拟 DOM 转真实 DOM 的函数 3. 挂载虚拟 DOM 到页面 4. 更新虚拟 DOM 的过程&#xff08;Diff 算法简化…

jmm--volatile

指令重排基础概念 在现代处理器和编译器为了提高程序执行效率&#xff0c;会对指令进行优化&#xff0c;其中一种优化方式就是指令重排序。在单线程环境下&#xff0c;指令重排序不会影响最终执行结果&#xff0c;因为处理器和编译器会保证重排序后的执行结果与按照代码顺序执行…

【硬件开发】滤波电容的选择:原理、计算与多电压值应用实践

滤波电容的选择&#xff1a;原理、计算与多电压值应用实践 1. 引言 在现代电子系统中&#xff0c;稳定的电源供应是保证电路可靠运行的基础。然而&#xff0c;电源线上往往不可避免地存在各种噪声和纹波&#xff0c;这些干扰可能源自电源本身&#xff08;如整流后的脉动直流&…

【seismic unix数据生成-unif2】

Seismic Unix简介 Seismic Unix&#xff08;SU&#xff09;是由科罗拉多矿业学院&#xff08;Colorado School of Mines&#xff09;开发的开源地震数据处理软件包&#xff0c;专为地震勘探数据分析和研究设计。它提供了一系列命令行工具&#xff0c;支持从数据加载、处理到可…

【逆向思考 并集查找】P2391 白雪皑皑|省选-

本文涉及知识点 C并集查找 P2391 白雪皑皑 题目背景 “柴门闻犬吠&#xff0c;风雪夜归人”&#xff0c;冬天&#xff0c;不期而至。千里冰封&#xff0c;万里雪飘。空中刮起了鸭毛大雪。雪花纷纷&#xff0c;降落人间。 美能量星球&#xff08;pty 在 spore 上的一个殖民地…

一文讲清楚React中setState的使用方法和机制

文章目录 一文讲清楚React中setState的使用方法和机制1. setState是什么2. setState方法详解2.1 setState参数详解2.2 setState同步异步问题2.2.1 setState异步更新2.2.2 setState同步更新 一文讲清楚React中setState的使用方法和机制 1. setState是什么 React中&#xff0c;…

01_软件卓越之道:功能性与需求满足

引言 在软件的世界里&#xff0c;功能性是产品与用户之间的第一桥梁。一个软件即使拥有华丽的界面和极致的性能&#xff0c;如果不能解决用户的核心需求&#xff0c;也终将被市场淘汰。本文将深入探讨如何确保软件的功能性与用户需求完美契合。 1. 需求理解&#xff1a;从模糊…

StarRocks × Tableau 连接器完整使用指南 | 高效数据分析从连接开始

一、导语&#xff1a;为什么选择 StarRocks Tableau 连接器&#xff1f; 在当今数据驱动的商业环境中&#xff0c;企业不仅需要一个能够处理海量数据的高性能分析数据库&#xff0c;还需要一个直观、强大的可视化工具来解读数据背后的故事。StarRocks 作为新一代极速全场景 MP…

基于 SpringBoot+VueJS 助农生鲜销售系统设计与实现7000字论文实现

摘要本论文设计并实现了一个基于 SpringBoot 和 VueJS 的助农生鲜销售系统。系统采用前后端分离架构&#xff0c;前端使用 VueJS 框架实现用户界面&#xff0c;后端使用 SpringBoot 框架构建服务&#xff0c;通过 MyBatis 实现数据持久化。系统实现了农产品展示、在线购物、订单…

Pytest 测试发现机制详解:自动识别测试函数与模块

概述 在编写自动化测试时,如何让 Pytest 自动找到你的测试代码 是一个非常基础但重要的问题。Pytest 通过其强大的 测试发现(Test Discovery)机制,能够自动扫描项目目录、识别测试模块和测试函数,从而大大简化了测试流程。 本文将为你详细讲解 Pytest 的测试发现机制,包…

MySQL 时间日期函数

时间日期类型 MySQL中主要支持以下几种时间日期类型&#xff1a; DATE - 日期类型 格式&#xff1a;YYYY-MM-DD范围&#xff1a;1000-01-01 到 9999-12-31示例&#xff1a;2023-05-20 TIME - 时间类型 格式&#xff1a;HH:MM:SS范围&#xff1a;-838:59:59 到 838:59:59示例&…

408第三季part2 - 计算机网络 - 物理层

理解 这里有8个波形&#xff0c;每个波形代表一个马原&#xff0c;一个马原代表多个比特&#xff0c;这里3个比特 求波特率就直接2W 求比特率就要乘log2V 这块记两公式就行&#xff0c;一个下面一个上面 题目 4个相位加4种幅度就是有16种波形 这里无噪声就是奈奎斯特定理 这…

iOS 集成RN Installing glog (0.3.5)报错的解决方案

在集成执行RN bundle exec pod install 命令到Installing glog (0.3.5)时报错,报错信息如下: Installing glog (0.3.5) [!] /bin/bash -c set -e #!/bin/bash # Copyright (c) Facebook, Inc. and its affiliates. # # This source code is licensed under the MIT license …

【进阶篇-消息队列】——MQTT协议如何支持海量的在线IoT设备

目录 一、什么是IoT二、MQTT 和其他消息队列的传输协议有什么不同三、如何选择 MQTT 产品四、MQTT 集群如何支持海量在线的 IoT 设备五、总结本文来源:极客时间vip课程笔记 一、什么是IoT IoT,也就是物联网,物联网这个词儿,它的含义还不那么直观,但你看它的英文:IoT,也就…

Chat Model API

聊天模型API为开发人员提供了将人工智能聊天完成功能集成到应用程序中的能力。它利用预训练的语言模型&#xff0c;如GPT&#xff08;生成预训练转换器&#xff09;&#xff0c;以自然语言对用户输入生成类似人类的响应。 API通常通过向人工智能模型发送提示或部分对话来工作&…