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

一、背景

  在最近的项目中的一个需求是消息实时推送消息以及通知功能,项目使用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==2.1.4-redis==2.3.1==2.1.6-redis==1.4.3

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

-redis

redis安装可以参考博客:https://www.cnblogs.com/wdliu/p/9360286.html

四、开始使用

一、配置settings.py

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

注册channles app:

INSTALLED_APPS =, ]

配置channels layer:

ASGI_APPLICATION = =: : [(,6379)],

二、路由配置

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

<span style="color: #0000ff;">from channels.auth <span style="color: #0000ff;">import<span style="color: #000000;"> AuthMiddlewareStack
<span style="color: #0000ff;">from channels.routing <span style="color: #0000ff;">import<span style="color: #000000;"> ProtocolTypeRouter,URLRouter
<span style="color: #0000ff;">import<span style="color: #000000;"> deploy.routing

application =<span style="color: #000000;"> ProtocolTypeRouter({
<span style="color: #800000;">'<span style="color: #800000;">websocket<span style="color: #800000;">'<span style="color: #000000;">: AuthMiddlewareStack(
URLRouter(
deploy.routing.websocket_urlpatterns<span style="color: #008000;">#<span style="color: #008000;"> 指明路由文件是devops/routing.py
<span style="color: #000000;"> )
),})

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

django.conf.urls <span style="color: #0000ff;">from . <span style="color: #0000ff;">import<span style="color: #000000;"> consumers

websocket_urlpatterns =<span style="color: #000000;"> [
url(r<span style="color: #800000;">'<span style="color: #800000;">^ws/deploy/(?P[^/]+)/$<span style="color: #800000;">',consumers.DeployResult),<span style="color: #008000;">#<span style="color: #008000;">consumers.DeployResult 是该路由的消费者
]

项目目录结构如下:

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

deploy/consumers.py:

<span style="color: #0000ff;">from channels.generic.websocket <span style="color: #0000ff;">import<span style="color: #000000;"> AsyncWebsocketConsumer
<span style="color: #0000ff;">import<span style="color: #000000;"> json

<span style="color: #0000ff;">class<span style="color: #000000;"> DeployResult(AsyncWebsocketConsumer):
async <span style="color: #0000ff;">def<span style="color: #000000;"> connect(self):
self.service_uid = self.scope[<span style="color: #800000;">"<span style="color: #800000;">url_route<span style="color: #800000;">"][<span style="color: #800000;">"<span style="color: #800000;">kwargs<span style="color: #800000;">"][<span style="color: #800000;">"<span style="color: #800000;">service_uid<span style="color: #800000;">"<span style="color: #000000;">]
self.chat_groupname = <span style="color: #800000;">'<span style="color: #800000;">chat%s<span style="color: #800000;">' %<span style="color: #000000;"> self.service_uid
<span style="color: #008000;">#<span style="color: #008000;"> 收到连接时候处理,
<span style="color: #000000;"> await self.channel_layer.group_add(
self.chat_group_name,self.channel_name
)

    await self.accept()

async </span><span style="color: #0000ff;"&gt;def</span><span style="color: #000000;"&gt; disconnect(self,close_code):
    </span><span style="color: #008000;"&gt;#</span><span style="color: #008000;"&gt; 关闭channel时候处理</span>

<span style="color: #000000;"> await self.channel_layer.group_discard(
self.chat_group_name,self.channel_name
)

</span><span style="color: #008000;"&gt;#</span><span style="color: #008000;"&gt; 收到消息</span>
async <span style="color: #0000ff;"&gt;def</span><span style="color: #000000;"&gt; receive(self,text_data):
    text_data_json </span>=<span style="color: #000000;"&gt; json.loads(text_data)
    message </span>= text_data_json[<span style="color: #800000;"&gt;'</span><span style="color: #800000;"&gt;message</span><span style="color: #800000;"&gt;'</span><span style="color: #000000;"&gt;]
    </span><span style="color: #0000ff;"&gt;print</span>(<span style="color: #800000;"&gt;"</span><span style="color: #800000;"&gt;收到消息--》</span><span style="color: #800000;"&gt;"</span><span style="color: #000000;"&gt;,message)
    </span><span style="color: #008000;"&gt;#</span><span style="color: #008000;"&gt; 发送消息到组</span>

<span style="color: #000000;"> await self.channel_layer.group_send(
self.chat_group_name,{
<span style="color: #800000;">'<span style="color: #800000;">type<span style="color: #800000;">': <span style="color: #800000;">'<span style="color: #800000;">client.message<span style="color: #800000;">'<span style="color: #000000;">,<span style="color: #800000;">'<span style="color: #800000;">message<span style="color: #800000;">'<span style="color: #000000;">: message
}
)

</span><span style="color: #008000;"&gt;#</span><span style="color: #008000;"&gt; 处理客户端发来的消息</span>
async <span style="color: #0000ff;"&gt;def</span><span style="color: #000000;"&gt; client_message(self,event):
    message </span>= event[<span style="color: #800000;"&gt;'</span><span style="color: #800000;"&gt;message</span><span style="color: #800000;"&gt;'</span><span style="color: #000000;"&gt;]
    </span><span style="color: #0000ff;"&gt;print</span>(<span style="color: #800000;"&gt;"</span><span style="color: #800000;"&gt;发送消息。。。</span><span style="color: #800000;"&gt;"</span><span style="color: #000000;"&gt;,message)
    </span><span style="color: #008000;"&gt;#</span><span style="color: #008000;"&gt; 发送消息到 WebSocket</span>
    await self.send(text_data=<span style="color: #000000;"&gt;json.dumps({
        </span><span style="color: #800000;"&gt;'</span><span style="color: #800000;"&gt;message</span><span style="color: #800000;"&gt;'</span><span style="color: #000000;"&gt;: message
    }))</span></pre>

以上代码部分说明:

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

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

四、发起webscoket请求

利用js发起websocket请求

websocket = 'ws://' + window.location.host + '/ws/deploy/tasks/' websocket.onmessage </span>= <span style="color: #0000ff;"&gt;function</span><span style="color: #000000;"&gt; (e) { </span><span style="color: #0000ff;"&gt;var</span> data =<span style="color: #000000;"&gt; JSON.parse(e.data); </span><span style="color: #0000ff;"&gt;var</span> message = '\n' + data['message'<span style="color: #000000;"&gt;]; document.querySelector(</span>'#deploy-res').innerText += (message + '\n'<span style="color: #000000;"&gt;); }; }</span></pre>

五、发送消息到channel

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

channel_layer =<span style="color: #000000;"> get_channel_layer()
def send_channel_msg(channel_name,msg):
"""<span style="color: #000000;">
send msg to channel
:param channel_name:
:param msg:
:return:
"""<span style="color: #000000;">
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进行生产部署。

  

版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。

相关推荐


注:所有源代码均实测运行过。所有源代码均已上传CSDN,请有需要的朋友自行下载。
继承APIView和ViewSetMixin;作用也与APIView基本类似,提供了身份认证、权限校验、流量管理等。ViewSet在开发接口中不经常用。
一、Django介绍Python下有许多款不同的 Web 框架。Django是重量级选手中最有代表性的一位。许多成功的网站和APP都基于Django。Django 是一个开放源代码的 Web 应用框架,由 Python 写成。Django 遵守 BSD 版权,初次发布于 2005 年 7 月, 并于 2008 年 9 月发布了第一个正式版本 1.0 。Django学习线路Django 采用了 MVT 的软件设计模式,即模型(Model),视图(View)和模板(Template)。这个MVT模式并
本文从nginx快速掌握到使用,gunicorn快速掌握到使用,实现小白快速搭建django项目,并对可能出现的报错进行了分析
uniapp微信小程序订阅消息发送服务通知
Django终端打印SQL语句 1 Setting配置: 2 默认python 使用的MysqlDB连接,Python3 支持支持pymysql 所有需要在app里面的__init__加上下面配置:
url: re_path(&#39;authors/$&#39;, views.AuthorView.as_view()), re_path(&#39;book/(?P\d+)/$&#39;, vie
前提 关于html寻找路线: template 如果在各个APP中存在, Django 会优先找全局template 文件下的html文件,如果全局下的template文件没有相关的html Djan
// GET请求request.GET // POST请求request.POST // 处理文件上传请求request.FILES // 处理如checkbox等多选 接受列表request.get
from bs4 import BeautifulSoup#kindeditordef kindeditor(request): s = &#39;&#39;&#39; &lt;li&gt;&lt;s
view.py 配置 html 配置
from django.http import JsonResponse JsonResponse 里面代码会加这一个响应头 kwargs.setdefault(&#39;content_type&#
#下面两种是基于QuerySet查询 也就是说SQL中用的jion连表的方式查询books = models.UserInfo.objects.all() print(type(books)) &gt
return HttpResponse(&quot;OK&quot;) 返回一个字符串 return redirect(&quot;/index/&quot;) 返回URL return render
from django.http import JsonResponse JsonResponse 里面代码会加这一个响应头 kwargs.setdefault(&#39;content_type&#
浏览器有一个很重要的概念——同源策略(Same-Origin Policy)。所谓同源是指,域名,协议,端口相同。不同源的客户端脚本(javascript、ActionScript)在没明确授权的情况
自动发送 &gt; 依赖jQuery文件 实例--&gt;GET请求: 手动发送 &gt; 依赖浏览器XML对象(也叫原生ajax) Ajax主要就是使用 【XmlHttpRequest】对象来完成请
#下面两种是基于QuerySet查询 也就是说SQL中用的jion连表的方式查询books = models.UserInfo.objects.all() print(type(books)) &gt
// GET请求request.GET // POST请求request.POST // 处理文件上传请求request.FILES // 处理如checkbox等多选 接受列表request.get
return HttpResponse(&quot;OK&quot;) 返回一个字符串 return redirect(&quot;/index/&quot;) 返回URL return render