关于django使用websocket

我想在django中使用websocket的主要目的是我们项目中有一个关于log日志的实时推送,这个时候就需要使用websocket把我们数据库的日志推送到前端去

这里我使用的模块是channels,这里有个坑需要注意,如果直接pip install的话下载的是最新版本的,会可能与你的django版本不匹配,所以如果你的django是3版本的,那么就需要下载对应3版本的channels,否则asgi可能会启动不起来

pip install channels==3.0.3 

配置

1.首先需要有一个websocket的app,我们需要在项目中创建一个app(app的名字随意)

python .\manage.py startapp web_socket

 2.修改settings.py(app里面要新增)

INSTALLED_APPS = [
    'channels',
    'web_socket'
]

添加asgi

WSGI_APPLICATION = 'account_book.wsgi.application'
# websocket
ASGI_APPLICATION = 'account_book.asgi.application'

3.修改asgi.py

import os

from django.core.asgi import get_asgi_application

os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'account_book.settings')
# websocket新加的
from channels.routing import ProtocolTypeRouter, URLRouter
from . import routing
# application = get_asgi_application()
# websocket
application = ProtocolTypeRouter({
    # 处理http请求
    'http': get_asgi_application(),
    # 处理websocket请求
    'websocket':URLRouter(routing.websocket_urlpatterns)
})

4.在项目settings同级创建routing.py,用来存放websocket路由

from django.urls import re_path
from web_socket import consumers

# 示例路由
websocket_urlpatterns = [
    re_path(r'ws/(?P<group>\w+)/$', consumers.ChatConsumer.as_asgi())
]

5.我们这里还需要使用在web_socket app中有一个路由匹配的函数,新建consumers.py,这是基于同步方式,但是会出现堵塞http的进程

from channels.generic.websocket import WebsocketConsumer
from channels.exceptions import StopConsumer
from .models import Book
import json
import asyncio
class ChatConsumer(WebsocketConsumer):
    def websocket_connect(self, message):
        # 接受客户端连接
        # 服务端允许和客户端创建连接
        print('有连接进入')
        self.accept()

    def websocket_receive(self, message):
        # 如果浏览器基于websocket向后端发送数据,接收客户端传过来的消息
        # 发送消息
        print('接受到消息:', message)
        # 接受到消息: {'type': 'websocket.receive', 'text': '你好'}  字典可以直接取出来
        # 向后端发送消息
        # self.send('Hello ' + message['username'])
        # 如果服务端向主动断开连接
        if message['text'] == '关闭':
            self.close()
            # 如果服务端断开连接不想执行websocket_disconnect,那么可以直接执行raise StopConsumer(),不return
            return

    def websocket_disconnect(self, close_code):
        # 这里是停止
        raise StopConsumer()

这样我们就直接可以启动我们的django,他会变成一个这样的形式

System check identified no issues (0 silenced).
February 24, 2024 - 17:47:12
Django version 3.2.2, using settings 'account_book.settings'
Starting ASGI/Channels version 3.0.3 development server at http://127.0.0.1:8000/
Quit the server with CTRL-BREAK.

 前端

前端可以通过产生websocket对象来进行websocket连接,这样可以通过websocket进行数据的交互,简单的示例如下:

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
    <p>index页面</p>
    <style>
        .messsage {
            height: 80px;
            border: 1px solid #dddd;
            width: 100%;
        }
    </style>
</head>
<body>
<div class="messsage" id="message"></div>
<div>
    <input type="text" pattern="请输入">
    <input type="button" onclick="sendMessage()" value="发送">
</div>
<script>
    socket = new WebSocket('ws://127.0.0.1:8000/ws/123/')


    // 创建好连接之后自动触发(服务端执行self.accept(),会触发)
    socket.onopen = function () {

    }
    // 当websocket接收到服务端发送的消息时,自动会触发这个函数
    socket.onmessage = function (event) {
        console.log(event.data)
    }
    // 断开连接,服务端接收到会自动执行websocket_disconnect
    // socket.close()

    // 服务端主动断开连接时,这个方法也会被处罚

    socket.onclose = function (){}
    function sendMessage() {
        socket.send('你好')
    }
</script>
</body>
</html>

这样就可以写一个简单的websocket连接了,我们可以通过前端连接websocket,后端会打印有有连接进入,然后也可以触发前端的回调进行数据的回去

目的功能的视线:

我想实现的不停给前端推送某个数据库,之前的代码一直会堵塞住htpp,这样就会产生一个问题,http没办法通过,所以采用异步的方式

后端代码:

from channels.generic.websocket import WebsocketConsumer
from channels.exceptions import StopConsumer
from .models import Book

# 这里是将数据库的查询操作变为异步的操作
@sync_to_async
def get_book_list():
    return list(Book.objects.all())


class ChatConsumer(AsyncWebsocketConsumer):
    def __init__(self, *args, **kwargs):
        super().__init__(*args, **kwargs)
        self.connected = False  # 增加一个标志来表示是否有连接进入

    async def connect(self):
        # 接受客户端连接
        await self.accept()

        # 启动异步任务
        asyncio.create_task(self.send_book_list_periodically())
        print('有连接进入')
        # await self.channel_layer.group_add("book_update", self.channel_name)

        self.connected = True  # 设置连接标志为True

    async def send_book_list_periodically(self):
        while self.connected:
            book_list = await get_book_list()  # 使用异步方式获取书籍列表
            send_list = [{'name': book.name, 'price': book.price} for book in book_list]
            send_list_json = json.dumps(send_list)
            await self.send(text_data=send_list_json)

            # 休眠5秒
            await asyncio.sleep(5)

    async def disconnect(self, close_code):
        # 连接断开时的处理
        self.connected = False  # 设置连接标志为False
        print('这里关闭了页面')
        raise StopConsumer()

上面就可以实现不停的推送数据库的消息,但是这里要注意,如果前端页面在关闭的时候并没有关闭我们的websocket,他进入其他页面还是会继续推送,所以需要我们在前端结束的时候进行断开连接,我是在vue里面写的,如下在mounted里面进行连接的建立,在beforeDestroy 进行websocket连接的关闭

mounted() {
    this.socket = new WebSocket('ws://127.0.0.1:8000/ws/123/')
    this.socket.onmessage = function (event) {
      console.log(event.data)
    }
  },
  beforeDestroy () {  // 在组件销毁前关闭 WebSocket 连接
    if (this.socket) {
      this.socket.close()
    }
  },

这样可以基本实现一个这样方式

同步堵塞大概的猜测,具体又看了一下同步的请求,发现前端不变的情况下,同步关闭了页面但是并没有触发到后端websocket的关闭,导致这个websocket一直在连接状态,所以会将其他的http请求进行堵塞,暂时想不到解决办法

  • 5
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
Django可以使用第三方库来实现WebSocket功能,常用的有Django Channels和django-websocket-redis。 这里以Django Channels为例,步骤如下: 1. 安装Django Channels ``` pip install channels ``` 2. 创建一个新的Django项目 ``` django-admin startproject myproject ``` 3. 创建一个新的Django应用程序 ``` python manage.py startapp myapp ``` 4. 在myapp中创建一个名为consumers.py的文件,用于处理WebSocket请求 ```python from channels.generic.websocket import WebsocketConsumer import json class MyConsumer(WebsocketConsumer): def connect(self): self.accept() def disconnect(self, close_code): pass def receive(self, text_data): text_data_json = json.loads(text_data) message = text_data_json['message'] self.send(text_data=json.dumps({ 'message': message })) ``` 5. 在myproject的settings.py文件中添加Channels配置信息 ```python INSTALLED_APPS = [ # ... 'channels', 'myapp', ] ASGI_APPLICATION = 'myproject.routing.application' ``` 6. 创建一个名为routing.py的文件,用于定义路由规则 ```python from django.urls import re_path from myapp.consumers import MyConsumer websocket_urlpatterns = [ re_path(r'ws/myurl/$', MyConsumer.as_asgi()), ] ``` 7. 在myproject的urls.py文件中包含路由规则 ```python from django.urls import path, include from django.contrib import admin from .routing import websocket_urlpatterns urlpatterns = [ path('admin/', admin.site.urls), path('myapp/', include('myapp.urls')), ] # Add WebSocket URL to urlpatterns urlpatterns += websocket_urlpatterns ``` 现在,你可以使用JavaScript来连接WebSocket并发送消息了。在前端代码中,可以使用以下代码: ```javascript const socket = new WebSocket('ws://localhost:8000/ws/myurl/'); socket.onopen = function(e) { console.log('WebSocket connected.'); }; socket.onmessage = function(event) { const data = JSON.parse(event.data); console.log('Received message:', data.message); }; socket.onclose = function(event) { console.log('WebSocket disconnected.'); }; socket.send(JSON.stringify({ 'message': 'Hello, server!' })); ``` 这是一个简单的示例,你可以根据自己的需求添加更多功能。
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值