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
英文地址,請注明出處:http://en.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通常通過向人工智能模型發送提示或部分對話來工作&…