Django:使用channels创建websocket,实现消息推送

39 篇文章 1 订阅
18 篇文章 0 订阅

感谢:(1)Django+channels配置与编写:https://blog.csdn.net/weixin_40105587/article/details/87375886

           (2)Django+channels配置:https://www.jianshu.com/p/d6394ca19d92

           (3)channels文档:https://channels.readthedocs.io/en/stable/introduction.html

一、环境版本

python 3.8

Django 3.1.5

channels 3.0.3

(安装channels时会自动安装Twisted、daphne等必须的依赖包)

 windows环境下安装channels,在安装twisted时会报错,提示

error: Microsoft Visual C++ 14.0 is required. Get it with "Microsoft Visual C++ Build Tools": https://visualstudio.microsoft.com/downloads/
  ----------------------------------------
  ERROR: Failed building wheel for twisted
  Running setup.py clean for twisted
Failed to build twisted

解决方法: 前往https://www.lfd.uci.edu/~gohlke/pythonlibs/#twisted下载.whl格式文件。

cp38对应python3.8版本,win_amd64对应64位系统

PS:一定要对应python的版本,否则可能会安装出错!!!

 将下载的.whl文件放进对应python3.8版本的安装目录的Scripts文件夹,在该文件夹下执行 pip install Twisted-20.3.0-cp38-cp38-win_amd64.whl

(如果是在虚拟环境,则在 项目所在盘:\项目名\venv\Scripts

安装完后再次,pip install channels

二、添加到项目

参考channels文档:https://channels.readthedocs.io/en/stable/installation.html

(我的项目配置与文档有一点区别)

1、在 项目的settings.py文件的INSTALLED_APPS添加 “channels”:

INSTALLED_APPS = (
    'django.contrib.auth',
    'django.contrib.contenttypes',
    'django.contrib.sessions',
    'django.contrib.sites',
    ...
    '你的项目app名',
    'channels',
)

ASGI_APPLICATION = '项目名.routing.application'

2、修改在settings.py同级目录的asgi.py(Django3以上版本会自动创建改文件)

(ps:我这里如果安装文档一样填写,用daphne启动项目时会报错,所以做了改动)

(daphne启动:daphne -p 8991 项目名.asgi:application)


import os

import django

from channels.routing import get_default_application

os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'pay_python.settings')

django.setup()
application = get_default_application()
# application = ProtocolTypeRouter({
#     "http": django_asgi_app,
#     'websocket': AuthMiddlewareStack(
#         URLRouter(
#             aliPay.ali_routing.websocket_urlpatterns
#         )
#     ),
# })

3、在settings.py 同级目录创建routing.py,其作用与 urls.py相同,主要用于分发webscoket请求,内容如下

# -*- coding: utf-8 -*-
"""
Created with:PyCharm
@Author: Jcsim
@Date: 2021-2-25 10:41
@Project: pay_python
@File: routing.py
@Blog:https://blog.csdn.net/weixin_38676276
@Description: 
@Python:
"""
from channels.auth import AuthMiddlewareStack
from channels.routing import ProtocolTypeRouter, URLRouter
from django.core.asgi import get_asgi_application


django_asgi_app = get_asgi_application()
application = ProtocolTypeRouter({
    # "http": django_asgi_app,
    'websocket': AuthMiddlewareStack(
        URLRouter(
            # 后面会完善
        )
    ),
})

4、在项目app目录下新增app_routing.py,app_consumers.py

app_routing.py内容:

# -*- coding: utf-8 -*-
"""
Created with:PyCharm
@Author: Jcsim
@Date: 2021-2-25 10:45
@Project: pay_python
@File: ali_routing.py
@Blog:https://blog.csdn.net/weixin_38676276
@Description: 接收ws请求处理
@Python:
"""
from django.urls import path
from aliPay.ali_consumers import OrderStatusConsumers, AsyncOrderStatusConsumers
# 建议url前缀使用 ws/xxx 用于区分ws请求和http请求
websocket_urlpatterns = [
    path('ws/qosws', OrderStatusConsumers.as_asgi()),
    # path('ws/asqosws/<tradeNo>', AsyncOrderStatusConsumers.as_asgi()),
]

 

app_consumers.py内容:

consumer类中的函数依次用于处理连接(connect)、断开连接(disconnect)、接收消息(receive)和处理对应类型的消息

# -*- coding: utf-8 -*-
"""
Created with:PyCharm
@Author: Jcsim
@Date: 2021-2-25 10:47
@Project: pay_python
@File: ali_consumers.py
@Blog:https://blog.csdn.net/weixin_38676276
@Description: 
@Python:
"""
from channels.generic.websocket import WebsocketConsumer, AsyncWebsocketConsumer
import json

# 我项目的日志操作
from aliPay.log import logger
#  数据库模型 
from aliPay.models import Orders, WsChannels
from utils import idCreater


class OrderStatusConsumers(WebsocketConsumer):
    def connect(self):
        self.accept()

    def disconnect(self, close_code):
        pass

    def receive(self, text_data):
        # 这里是接受数据后的操作,下面的方法按需修改
        # logger.info("接受订单号,发送数据")
        text_data_json = json.loads(text_data)
        trade_no = text_data_json['tradeNo']
        stop = False
        order_info = Orders.objects.filter(orders_id=trade_no).first()
        order_status = order_info.orders_status
        status = 201
        password = "******"
        if order_status == 20:
            password = order_info.project.password
            status = 200
            stop = True
        data = {
            "status": status,
            "password": password
        }
        # 推送信息到前端 
        self.send(text_data=json.dumps(data))
        # 我项目ws推送的结束逻辑
        if stop:
            self.close()


# 异步通讯 redis版本需为5.0及以上
class AsyncOrderStatusConsumers(AsyncWebsocketConsumer):

    async def connect(self):
        self.tradeNo = self.scope['url_route']['kwargs']['tradeNo']
        self.tradeNo_group_name = 'chat_%s' % self.tradeNo

        # Join room group
        await self.channel_layer.group_add(
            self.tradeNo_group_name,
            self.channel_name
        )

        await self.accept()

    async def disconnect(self, close_code):
        # Leave room group
        await self.channel_layer.group_discard(
            self.tradeNo_group_name,
            self.channel_name
        )

    # Receive message from WebSocket
    async def receive(self, text_data):
        # text_data_json = json.loads(text_data)
        # message = text_data_json['message']

        logger.info("接受订单号,发送数据aaaaaaaaaaaa")
        text_data_json = json.loads(text_data)
        trade_no = text_data_json['tradeNo']

        # Send message to room group
        await self.channel_layer.group_send(
            self.tradeNo_group_name,
            {   
                'type': 'chat_message', # type 用于指定该消息的类型,根据消息类型调用不同的函数去处理消息
                'message': trade_no  # message 内为消息主体
            }
        )

    # Receive message from room group
    async def chat_message(self, event):
        trade_no = event['tradeNo']
        stop = False
        # while not stop:
        order_info = Orders.objects.filter(orders_id=trade_no).first()
        order_status = order_info.orders_status
        password = "******"
        if order_status == 20:
            password = order_info.project.password
            stop = True
        data = {
            "order_status": order_status,
            "password": password
        }
        # Send message to WebSocket
        await self.send(text_data=json.dumps(data))
        if stop:
            await self.close()

 

5、修改 settings.py同级目录的routing.py

# -*- coding: utf-8 -*-
"""
Created with:PyCharm
@Author: Jcsim
@Date: 2021-2-25 10:41
@Project: pay_python
@File: routing.py
@Blog:https://blog.csdn.net/weixin_38676276
@Description: 
@Python:
"""
from channels.auth import AuthMiddlewareStack
from channels.routing import ProtocolTypeRouter, URLRouter
from django.core.asgi import get_asgi_application

import aliPay.ali_routing

django_asgi_app = get_asgi_application()
application = ProtocolTypeRouter({
    'websocket': AuthMiddlewareStack(
        URLRouter(
            app_name.app_routing.websocket_urlpatterns
        )
    ),
})

6、前端ws请求

前端websocket 教程:https://www.runoob.com/html/html5-websocket.html

<script type="text/javascript">
    let createWebSocket = (function () {
            return function (urlValue) {
                if (window.WebSocket) return new WebSocket(urlValue);
                if (window.MozWebSocket) return new MozWebSocket(urlValue);
                return false;
            }
        })();
    
    $(function () {
        const tradeNo = $(".orderNO").text()
        const socket = createWebSocket("ws://"+window.location.host+"/ws/qosws")
        {#console.log("传数据==")#}
        let send_data = {
            "tradeNo": tradeNo
        }
        socket.onopen = () => {
            $(document).ready(() => {
                {#console.log('WebSocket open');//成功连接上Websocket#}
                socket.send(JSON.stringify(send_data))
            })
        }
        {#console.log("获取数据==")#}
        socket.onmessage = (serverPush) => {
            {#console.log(serverPush)#}
            let data = JSON.parse(serverPush.data)
            $(".password").text(data.password)
            {#console.log(data.password)#}
            if (data.status === 200){
                alert("支付成功,请保存密码")
            }
        }

        const timer2 = self.setInterval(() =>{
            socket.send(JSON.stringify(send_data))
        }, 3000)
        const timer_close = setTimeout(() =>{
            {#console.log("===关闭定时器====",10)#}
            clearInterval(timer2)
            socket.close()
        }, 60000*10)

        socket.onclose = () =>{
            {#console.log("关闭连接")#}
            clearInterval(timer2)
            clearTimeout(timer_close)
            socket.close()
        }
    })



</script>

 

7、至此,操作完成。

 

  • 2
    点赞
  • 14
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值