django中websocket的使用

本文介绍如何在Django项目中使用WebSocket实现持久连接,用于实时数据更新。WebSocket作为HTTP协议的补充,解决了无状态连接的问题,适用于需要实时交互的场景。内容包括后端与前端的配置和交互细节。

websocket是一种持久化的协议,HTTP协议是一种无状态的协议,在特定场合我们需要使用长连接,做数据的实时更新,这种情况下我们就可以使用websocket做持久连接。http与websocket二者存在交集。

后端:

from dwebsocket.decorators import accept_websocket
import json
# 存储连接websocket的用户
clist = []

@accept_websocket
def websocketLink(request):
    # 获取连接
    if request.is_websocket:
        # 新增 用户  连接信息
        clist.append(request.websocket)
        # 监听接收客户端发送的消息 或者 客户端断开连接
        for message in request.websocket:
            break

 # 发送消息
def websocketMsg(client, msg):
    b1 = json.dumps(msg,ensure_ascii=False).encode('utf-8')
    client.send(b1)

# 服务端发送消息
def sendmsg():
    sql = "select * from customer"
    res = db1.find_all(sql)
    if len(clist)>0:
        for i in clist:
            i.send(json.dumps({'list': res},ensure_ascii=False).encode('utf-8'))
             # websocketMsg(i, {'list': res})
    return HttpResponse("ok")

from apscheduler.schedulers.blocking import BlockingScheduler

def getecharts(request):
    scheduler = BlockingScheduler()
    scheduler.add_job(sendmsg,'interval',seconds=1)
    scheduler.start()
    return HttpResponse('ok')

前端:

<template>
  <div class="bgpic">
    <van-row style="padding-top: 10px;padding-bottom: 10px">
      <van-col span="8">
        <div id="weekmain" style="width: 400px;height: 300px"></div>
      </van-col>
      <van-col span="8">http://api.map.baidu.com/marker </van-col>
      <van-col span="8">
        <div id="monthmain" style="width: 400px;height: 300px"></div>
      </van-col>
    </van-row>
    <van-row>
      <van-col span="8"></van-col>
      <van-col span="8"></van-col>
      <van-col span="8">{{infolist1}}</van-col>
    </van-row>
  </div>
</template>

<script>
import * as echarts from 'echarts';
// import myaxios from "../../../https/myaxios";
import axios from 'axios';
import {reactive} from 'vue';
export default {
  name: "myweek",
  setup(){
    let infolist1 = reactive({"data":[]})
    // let mydata = reactive([])
    const initdata=()=>{
      var socket = new WebSocket("ws:127.0.0.1:8000/websocketLink");

      socket.onopen = function () {
        console.log('连接成功');//成功连接上Websocket
      };
      socket.onmessage = function (e) {
        // alert('消息提醒: ' + e.data);
        //打印服务端返回的数据
        infolist1.data = e.data
        console.log(e.data)
        // mydata = infolist1.list
        // console.log(mydata)
      };
      socket.onclose=function(e){
        console.log(e);
        socket.close(); //关闭TCP连接
      };
    }
    return{
      infolist1,
      initdata
    }
  },
  data(){
    return{
      infolist:[],
    }
  },
  methods:{
    mget(){
      axios.get("http://127.0.0.1:8000/getecharts").then(res=>{
        console.log(res)
      })
    },
    infoshow(){
      axios.get("http://localhost:8000/infoshow","get").then(res=>{
        this.infolist=res.data.list
        this.getmonth()
        this.mget()
      })
    },
    getmonth(){
      var chartDom = document.getElementById('monthmain');
      var myChart = echarts.init(chartDom);
      var option;

// prettier-ignore

      let dataAxis = [];
// prettier-ignore
      let data = [];

      for(let i=0;i<this.infolist.length;i++){
        dataAxis.push(this.infolist[i]["name"])
        data.push(this.infolist[i]["tmoney"])
      }

      let yMax = 10000;
      let dataShadow = [];
      for (let i = 0; i < data.length; i++) {
        dataShadow.push(yMax);
      }
      option = {
        title: {
          text: '特性示例:渐变色 阴影 点击缩放',
          subtext: 'Feature Sample: Gradient Color, Shadow, Click Zoom'
        },
        xAxis: {
          data: dataAxis,
          axisLabel: {
            inside: true,
            color: '#fff'
          },
          axisTick: {
            show: false
          },
          axisLine: {
            show: false
          },
          z: 10
        },
        yAxis: {
          axisLine: {
            show: false
          },
          axisTick: {
            show: false
          },
          axisLabel: {
            color: '#999'
          }
        },
        dataZoom: [
          {
            type: 'inside'
          }
        ],
        series: [
          {
            type: 'bar',
            showBackground: true,
            itemStyle: {
              color: new echarts.graphic.LinearGradient(0, 0, 0, 1, [
                { offset: 0, color: '#83bff6' },
                { offset: 0.5, color: '#188df0' },
                { offset: 1, color: '#188df0' }
              ])
            },
            emphasis: {
              itemStyle: {
                color: new echarts.graphic.LinearGradient(0, 0, 0, 1, [
                  { offset: 0, color: '#2378f7' },
                  { offset: 0.7, color: '#2378f7' },
                  { offset: 1, color: '#83bff6' }
                ])
              }
            },
            data: data
          }
        ]
      };
// Enable data zoom when user click bar.
      const zoomSize = 6;
      myChart.on('click', function (params) {
        console.log(dataAxis[Math.max(params.dataIndex - zoomSize / 2, 0)]);
        myChart.dispatchAction({
          type: 'dataZoom',
          startValue: dataAxis[Math.max(params.dataIndex - zoomSize / 2, 0)],
          endValue:
              dataAxis[Math.min(params.dataIndex + zoomSize / 2, data.length - 1)]
        });
      });

      option && myChart.setOption(option);
    },
    getweek(){
      var chartDom = document.getElementById('weekmain');
      var myChart = echarts.init(chartDom);
      var option;

      option = {
        tooltip: {
          trigger: 'axis',
          axisPointer: {
            type: 'shadow'
          }
        },
        legend: {},
        grid: {
          left: '3%',
          right: '4%',
          bottom: '3%',
          containLabel: true
        },
        xAxis: [
          {
            type: 'category',
            data: ['Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat', 'Sun']
          }
        ],
        yAxis: [
          {
            type: 'value'
          }
        ],
        series: [
          {
            name: 'Direct',
            type: 'bar',
            emphasis: {
              focus: 'series'
            },
            data: [320, 332, 301, 334, 390, 330, 320]
          },
          {
            name: 'Email',
            type: 'bar',
            stack: 'Ad',
            emphasis: {
              focus: 'series'
            },
            data: [120, 132, 101, 134, 90, 230, 210]
          },
          {
            name: 'Union Ads',
            type: 'bar',
            stack: 'Ad',
            emphasis: {
              focus: 'series'
            },
            data: [220, 182, 191, 234, 290, 330, 310]
          },
          {
            name: 'Video Ads',
            type: 'bar',
            stack: 'Ad',
            emphasis: {
              focus: 'series'
            },
            data: [150, 232, 201, 154, 190, 330, 410]
          },
          {
            name: 'Search Engine',
            type: 'bar',
            data: [862, 1018, 964, 1026, 1679, 1600, 1570],
            emphasis: {
              focus: 'series'
            },
            markLine: {
              lineStyle: {
                type: 'dashed'
              },
              data: [[{ type: 'min' }, { type: 'max' }]]
            }
          },
          {
            name: 'Baidu',
            type: 'bar',
            barWidth: 5,
            stack: 'Search Engine',
            emphasis: {
              focus: 'series'
            },
            data: [620, 732, 701, 734, 1090, 1130, 1120]
          },
          {
            name: 'Google',
            type: 'bar',
            stack: 'Search Engine',
            emphasis: {
              focus: 'series'
            },
            data: [120, 132, 101, 134, 290, 230, 220]
          },
          {
            name: 'Bing',
            type: 'bar',
            stack: 'Search Engine',
            emphasis: {
              focus: 'series'
            },
            data: [60, 72, 71, 74, 190, 130, 110]
          },
          {
            name: 'Others',
            type: 'bar',
            stack: 'Search Engine',
            emphasis: {
              focus: 'series'
            },
            data: [62, 82, 91, 84, 109, 110, 120]
          }
        ]
      };

      option && myChart.setOption(option);

    },
  },
  mounted() {
    this.infoshow()
    this.getweek()
    this.initdata()
  }
}
</script>

<style scoped>
.bgpic{
  background-image: url("../../../https/4.jpg");
  width: 1269px;
  height: 781px;
}
</style>

### 实现 Django WebSocket 的方法 在 Django 中,实现 WebSocket 通信主要有两种方式: 1. **使用原生 Python 库** 可以借助 `websocket` 或 `autobahn` 这类库来实现 WebSocket 功能。这种方法适用于小型项目或简单的实时通信需求,但其扩展性和集成性不如官方解决方案。这种方式通常需要手动管理连接、路由和消息处理。 2. **使用 Django Channels** Django 官方推荐的异步通信解决方案是 [Django Channels](https://channels.readthedocs.io/),它为 WebSocket、HTTP/2 和其他协议提供了全面的支持。通过创建消费者(Consumer)类来处理连接,并结合 ASGI 协议服务器(如 Daphne 或 Uvicorn),可以构建结构清晰且可维护的实时功能[^1]。 ### 配置 Django Channels 的基本步骤 以下是一个基础指南,展示如何配置并使用 Django Channels 来支持 WebSocket: #### 1. 安装依赖 确保安装了必要的包: ```bash pip install channels channels-redis redis ``` #### 2. 修改 `settings.py` 将 `'channels'` 添加到 `INSTALLED_APPS`,并设置 ASGI 应用路径和通道层: ```python # settings.py INSTALLED_APPS = [ ... 'channels', ] ASGI_APPLICATION = 'your_project.asgi.application' CHANNEL_LAYERS = { 'default': { 'BACKEND': 'channels_redis.core.RedisChannelLayer', 'CONFIG': { "hosts": [('127.0.0.1', 6379)], }, }, } ``` #### 3. 创建 WebSocket 消费者 编写一个消费者类来处理 WebSocket 连接和消息: ```python # consumers.py from channels.generic.websocket import WebsocketConsumer import json class ChatConsumer(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 })) ``` #### 4. 配置 WebSocket 路由 定义 WebSocket 路由映射到相应的消费者: ```python # routing.py from django.urls import re_path from . import consumers websocket_urlpatterns = [ re_path(r'ws/chat/$', consumers.ChatConsumer), ] ``` 然后,在项目的 `asgi.py` 文件中包含这些路由: ```python # asgi.py import os from django.core.asgi import get_asgi_application from channels.routing import ProtocolTypeRouter, URLRouter from channels.auth import AuthMiddlewareStack import your_app.routing os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'your_project.settings') application = ProtocolTypeRouter({ "http": get_asgi_application(), "websocket": AuthMiddlewareStack( URLRouter( your_app.routing.websocket_urlpatterns ) ), }) ``` #### 5. 启动 ASGI 服务器 运行 ASGI 服务器以支持 WebSocket: ```bash daphne -p 8000 your_project.asgi:application ``` 或者使用 Uvicorn: ```bash uvicorn your_project.asgi:application --host 0.0.0.0 --port 8000 --reload ``` ### 使用前端建立 WebSocket 连接 在前端 JavaScript 中,可以通过如下方式连接到后端的 WebSocket 端点: ```javascript const chatSocket = new WebSocket("ws://" + window.location.host + "/ws/chat/"); chatSocket.onmessage = function(e) { const data = JSON.parse(e.data); console.log("Received message:", data.message); }; chatSocket.onclose = function(e) { console.error("WebSocket closed unexpectedly"); }; // 发送消息 document.querySelector("#sendButton").addEventListener("click", () => { const input = document.querySelector("#messageInput"); chatSocket.send(JSON.stringify({ message: input.value })); }); ``` ### 性能优化策略 - **使用 Redis Channel Layer** 当部署多个实例时,建议使用 Redis 作为通道层来实现跨进程的消息传递,这样可以保证多个服务器之间共享状态和数据流[^2]。 - **负载均衡与集群部署** 在生产环境中,建议将 Django Channels 与 Nginx、Redis、以及反向代理配合使用,以提升并发能力和稳定性。 - **限制连接数与超时机制** 对于长时间不活动的连接,应设置合理的超时时间以避免资源浪费。同时可以对每个用户的最大连接数进行限制。 ### 示例:实时大屏应用中的 WebSocket 在实时大屏系统中,Django Channels 可用于推送数据库更新或来自外部 API 的新数据。例如,当数据库发生变化时,后台任务可以触发事件并通过 WebSocket 主动推送到前端页面,从而实现实时刷新效果[^4]。 ---
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值