django使用channels2.x实现实时通讯


Posted in Javascript onNovember 28, 2018

一、背景

在最近的项目中的一个需求是消息实时推送消息以及通知功能,项目使用django写的所以决定采用django-channels来实现websocket进行实时通讯。目前官方已经更新到2.1版本,相对于老的channels 1.x版本有了很大变化,无论是使用方式还是功能,其中最大的变化莫过于2.x版本中带来的asyncio特性,可使用异步处理模式。本文内容将介绍channels2版本使用,由于项目django是1.11,其中也遇到了一些坑,比如在channels在处理一次请求后hang住然后报错,后面修改了下django1.11版本的一点源码得以解决,2.0版本应该不会有问题。

二、channels介绍

channels是以django插件的形式存在,它不仅能处理http请求,还提供对websocket、MQTT等长连接支持。不仅如此,channels在保留了原生django的同步和易用的特性上还带来了异步处理方式(channels2.X版本),并且将django自带的认证系统以及session集成到模块中,扩展性非常强。官方文档:https://channels.readthedocs.io/en/latest/index.html

三、安装以及安装需求

channels2.0最低django版本要求是1.11+,python3.5+。笔者的版本是django1.11,直接安装可能有问题,以下是测试通过的版本。

笔者的相关版本如下:

Django==1.11.10
channels==2.1.4
channels-redis==2.3.1
asgiref==2.1.6
asgi-redis==1.4.3

如果django版本比较高直接采用pip安装:

pip3 install channels
pip3 install channels-redis #可选的,官方推荐如果使用redis作为channel layer

redis安装可以参考博客:https://3water.com/article/151522.htm

四、开始使用

一、配置settings.py

笔者采用的redis作为channel layer(关于其介绍请移步至https://channels.readthedocs.io/en/latest/topics/channel_layers.html),它是实现消息推送的核心,在项目的settings.py中:

注册channles app:

INSTALLED_APPS = [
 'django.contrib.admin',
 'django.contrib.auth',
 'django.contrib.contenttypes',
 'django.contrib.sessions',
 'django.contrib.messages',
 'django.contrib.staticfiles',
 'cmdb',
 'channels', #注册app
]

配置channels layer:

ASGI_APPLICATION = 'devops.routing.application'
CHANNEL_LAYERS = {
 'default': {
  'BACKEND': 'channels_redis.core.RedisChannelLayer',
  'CONFIG': {
   "hosts": [('10.1.210.33', 6379)], #需修改
  },
 },
}

二、路由配置

在项目settings文件同级目录中新增routing.py

#!/usr/bin/env python3
# -*- coding:utf-8 -*-
# Author:wd

from channels.auth import AuthMiddlewareStack
from channels.routing import ProtocolTypeRouter, URLRouter
import deploy.routing

application = ProtocolTypeRouter({
 'websocket': AuthMiddlewareStack(
  URLRouter(
   deploy.routing.websocket_urlpatterns# 指明路由文件是devops/routing.py
  )
 ),
})

最后在app里配置路由和对应的消费者,笔者这里是devops下的routing.py:

#!/usr/bin/env python3
# -*- coding:utf-8 -*-
# Author:wd
from django.conf.urls import url

from . import consumers

websocket_urlpatterns = [
 url(r'^ws/deploy/(?P<service_name>[^/]+)/$', consumers.DeployResult), #consumers.DeployResult 是该路由的消费者
]

项目目录结构如下:

django使用channels2.x实现实时通讯

三、编写webscoket消息处理方法(消费者)

首先说明,消费者是Channels代码的基本单元,当一个新的Socket进入的时候,Channels会根据路由表找到正确的消费者,以下代码中每个方法都可以看作一个消费者,他们消费不同的event,比如刚刚接受连接时候connect方法进行消费处理并接受连接,关闭websocket时候使用disconnect进行消费处理。

deploy/consumers.py:

#!/usr/bin/env python3
# -*- coding:utf-8 -*-
# Author:wd

from channels.generic.websocket import AsyncWebsocketConsumer
import json

class DeployResult(AsyncWebsocketConsumer):
 async def connect(self):
  self.service_uid = self.scope["url_route"]["kwargs"]["service_uid"]
  self.chat_group_name = 'chat_%s' % self.service_uid
  # 收到连接时候处理,
  await self.channel_layer.group_add(
   self.chat_group_name,
   self.channel_name
  )

  await self.accept()

 async def disconnect(self, close_code):
  # 关闭channel时候处理
  await self.channel_layer.group_discard(
   self.chat_group_name,
   self.channel_name
  )

 # 收到消息
 async def receive(self, text_data):
  text_data_json = json.loads(text_data)
  message = text_data_json['message']
  print("收到消息--》",message)
  # 发送消息到组
  await self.channel_layer.group_send(
   self.chat_group_name,
   {
    'type': 'client.message',
    'message': message
   }
  )

 # 处理客户端发来的消息
 async def client_message(self, event):
  message = event['message']
  print("发送消息。。。",message)
  # 发送消息到 WebSocket
  await self.send(text_data=json.dumps({
   'message': message
  }))

以上代码部分说明:

1.self.scope是单个连接传入的详细信息,其中包含了请求的session、以及django认证系统中的用户信息等;

2.async...await 是python3.5之后的新异步特性,基于asyncio模块;

四、发起webscoket请求

利用js发起websocket请求

function InitWebSocket() {
   var websocket = new WebSocket( 
    'ws://' + window.location.host + '/ws/deploy/tasks/' );

   websocket.onmessage = function (e) {
    var data = JSON.parse(e.data);
    var message = '\n' + data['message'];
    document.querySelector('#deploy-res').innerText += (message + '\n');
   };
  }

五、发送消息到channel
无论是消息的推送或者消息的接受,都是经过channel layer进行传输,以下是发送消息示例,

from channels.layers import get_channel_layer
from asgiref.sync import async_to_sync


channel_layer = get_channel_layer()
def send_channel_msg(channel_name, msg):
  """
  send msg to channel
  :param channel_name: 
  :param msg: 
  :return: 
  """
 async_to_sync(channel_layer.group_send)(channel_name,
             {"type": "deploy.run", "text": msg})

六、生产部署

大多数django的应用部署方式都采用的是nginx+uwsgi进行部署,当django集成channels时候,由于uwsgi不能处理websocket请求,所以我们需要asgi服务器来处理websocket请求,官方推荐使用daphne。下一篇文章将介绍nginx+supervisor+daphne+uwsgi进行生产部署。

以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持三水点靠木。

Javascript 相关文章推荐
基于jquery的防止大图片撑破页面的实现代码(立即缩放)
Oct 24 Javascript
javascript实现网页字符定位的方法
Jul 14 Javascript
jQuery鼠标事件汇总
Aug 30 Javascript
JS组件系列之Bootstrap table表格组件神器【二、父子表和行列调序】
May 10 Javascript
vue.js开发环境安装教程
Mar 17 Javascript
详解js几个绕不开的事件兼容写法
Aug 30 Javascript
vue实现验证码输入框组件
Dec 14 Javascript
Node解决简单重复问题系列之Excel内容的获取
Jan 02 Javascript
解决layui前端框架 form表单,table表等内置控件不显示的问题
Aug 19 Javascript
微信小程序下拉框组件使用方法详解
Dec 28 Javascript
使用Vue.js中的过滤器实现幂方求值的方法
Aug 27 Javascript
如何在Vue项目中添加接口监听遮罩
Jan 25 Vue.js
在 Vue-CLI 中引入 simple-mock实现简易的 API Mock 接口数据模拟
Nov 28 #Javascript
详解Vue中watch的详细用法
Nov 28 #Javascript
vscode下的vue文件格式化问题
Nov 28 #Javascript
微信小程序如何获取用户收货地址
Nov 27 #Javascript
详解vue2.0 资源文件assets和static的区别
Nov 27 #Javascript
微信小程序实现点击图片旋转180度并且弹出下拉列表
Nov 27 #Javascript
js canvas实现画图、滤镜效果
Nov 27 #Javascript
You might like
修改php.ini以达到屏蔽错误信息并记录日志
2013/06/16 PHP
thinkphp实现面包屑导航(当前位置)例子分享
2014/05/10 PHP
Eclipse的PHP插件PHPEclipse安装和使用
2014/07/20 PHP
CI框架出现mysql数据库连接资源无法释放的解决方法
2016/05/17 PHP
一段效率很高的for循环语句使用方法
2007/08/13 Javascript
JS模拟面向对象全解(二、类型与赋值)
2011/07/13 Javascript
Prototype源码浅析 Number部分
2012/01/16 Javascript
jquery缓动swing liner控制动画过程不同时刻的速度
2014/05/29 Javascript
bootstrap Validator 模态框、jsp、表单验证 Ajax提交功能
2017/02/17 Javascript
jQuery Chosen通用初始化
2017/03/07 Javascript
js实现三级联动效果(简单易懂)
2017/03/27 Javascript
angular 基于ng-messages的表单验证实例
2017/05/04 Javascript
使用axios实现上传图片进度条功能
2017/12/21 Javascript
详解Angular-ui-BootStrap组件的解释以及使用
2018/07/13 Javascript
Vue3 源码导读(推荐)
2019/10/14 Javascript
python脚本实现分析dns日志并对受访域名排行
2014/09/18 Python
Python脚本实现代码行数统计代码分享
2015/03/10 Python
在Python 2.7即将停止支持时,我们为你带来了一份python 3.x迁移指南
2018/01/30 Python
python matplotlib绘图,修改坐标轴刻度为文字的实例
2018/05/25 Python
python监控文件并且发送告警邮件
2018/06/21 Python
Python面向对象之接口、抽象类与多态详解
2018/08/27 Python
Python实现插入排序和选择排序的方法
2019/05/12 Python
Python数据可视化实现正态分布(高斯分布)
2019/08/21 Python
python多进程(加入进程池)操作常见案例
2019/10/21 Python
Python爬取豆瓣数据实现过程解析
2020/10/27 Python
Schutz鞋官方网站:Schutz Shoes
2017/12/13 全球购物
幼儿园教师培训方案
2014/02/04 职场文书
培训研修方案
2014/06/06 职场文书
体育运动会广播稿
2014/10/05 职场文书
2014购房个人委托书范本
2014/10/12 职场文书
2015年八一建军节演讲稿
2015/03/19 职场文书
中学教师师德师风承诺书
2015/04/28 职场文书
法定代表人资格证明书
2015/06/18 职场文书
Python 读写 Matlab Mat 格式数据的操作
2021/05/19 Python
Python Pandas常用函数方法总结
2021/06/15 Python
Java实现经典游戏泡泡堂的示例代码
2022/04/04 Java/Android