一、简述:django实现websocket,之前django-websocket退出到3.0之后,被废弃。官方推荐大家使用channels。
channels通过升级http协议 升级到websocket协议。保证实时通讯。也就是说,我们完全可以用channels实现我们的即时通讯。而不是使用长轮询和计时器方式来保证伪实时通讯。
他通过改造django框架,使django既支持http协议又支持websocket协议。
官方文档地址:https://channels.readthedocs.io/en/stable/
二:安装
python version:2.7 3.4 3.5
安装channels:
1 pip install -U channels
在安装在windows机器的时候。需要自信的C++支持,报错的时候,报错有地址告诉你下载URL。
配置:
需要在seting.py里配置,将我们的channels加入INSTALLED_APP里。
1 INSTALLED_APPS = (2 'django.contrib.auth',3 'django.contrib.contenttypes',4 'django.contrib.sessions',5 'django.contrib.sites',6 ...7 'channels',8 )
这样django就支持websocket了,接下来我们需要配置一些简单的配置。
三:概念阐述:
channels: It is an ordered, first-in first-out queue with message expiry and at-most-once delivery to only one listener at a time.
它是先进先出的消息队列,同一时刻只向一个消费者发送一个没有过期的消息。这里的消费者类似订阅者,或者客户端。
默认的channels是http.request.在这种情况下运行 django 和之前的没使用websocket来说没有什么特别。
通过查看源码我们可以看到其他的channels:
至于我们是否可以自定义channels目前没有验证!
介绍下channels结构:
首先需要建立一个django项目。其中在你自己的app下面 生成consumers.py和routing.py配置文件。
consumers.py:相当于django的视图,也就是说所有的websocket路由过来的执行的函数都在consumers.py类似于django的视图views.py
routing.py:是websocket中的url和执行函数的对应关系。相当于django的urls.py,根据映射关系,当websocket的请求进来的时候,根据用户的请求来触发我们的consumers.py里的方法。
四:代码示例
consumer.py
1 # In consumers.py2 3 def ws_message(message):4 # ASGI WebSocket packet-received and send-packet message types5 # both have a "text" key for their textual data.6 message.reply_channel.send({7 "text": message.content['text'],8 })
routing.py
1 # In routing.py2 from channels.routing import route3 from myapp.consumers import ws_message4 5 channel_routing = [6 route("websocket.receive", ws_message),7 ]
websocket.receive表示当用户请求的时候,自动触发后面的ws_message.
html code:html5支持websocket。
1 2 3 4 5测试websocket 6 7 45 46 47 48 4950 运行 WebSocket5152 53 54
演示:
1:
2:
3:
4:
5:
五:如上是简单实现 我们的websocket 例子 ,其中channels来还有如下类型:
1 websocket.connect 刚建立连接。2 3 websocket.disconnect 连接断开的时候
可以根据自己的需求来在routing里定义 在触发websocket各个阶段的时候执行函数。
目前实现的是一个客户端进行操作,也就是说一个consumer的情况,当我们的有多个consumer的时候,怎么保证server端发送消息所有的consumer都能接受到呢?
channels给咱们定义个group概念。也就是说只要consumer和这个组建立的关系,其他的各个consumer都会接受到消息。
consumer.py
1 # In consumers.py 2 from channels import Group 3 4 # Connected to websocket.connect 5 def ws_add(message): 6 message.reply_channel.send({ "accept": True}) 7 Group("chat").add(message.reply_channel) 8 9 # Connected to websocket.receive10 def ws_message(message):11 Group("chat").send({12 "text": "[user] %s" % message.content['text'],13 })14 15 # Connected to websocket.disconnect16 def ws_disconnect(message):17 Group("chat").discard(message.reply_channel)
routing.py:
1 from channels.routing import route2 from myapp.consumers import ws_add, ws_message, ws_disconnect3 4 channel_routing = [5 route("websocket.connect", ws_add),6 route("websocket.receive", ws_message),7 route("websocket.disconnect", ws_disconnect),8 ]
在浏览器输入如下js:
1 // Note that the path doesn't matter right now; any WebSocket 2 // connection gets bumped over to WebSocket consumers 3 socket = new WebSocket("ws://" + window.location.host + "/chat/"); 4 socket.onmessage = function(e) { 5 alert(e.data); 6 } 7 socket.onopen = function() { 8 socket.send("hello world"); 9 }10 // Call onopen directly if socket is already open11 if (socket.readyState == WebSocket.OPEN) socket.onopen();
我们打开2个浏览器进行测试:
当我们运行窗口二的js的时候窗口也能接受到消息。
这是因为服务端以组来发送消息。
1 Group("chat").send({2 "text": "[user] %s" % message.content['text'],3 })
根据以上特性 我们可以创建我们的聊天室。
其中routing.py中支持正则路径匹配,我们可以根据我们的需求,由用户根据路径不同请求不同的聊天室,想深入了解,请参考官方文档。
为什么研究websocket?
因为在实际生产中,我们需要有一个即时通讯的,不断请求后端结果。来反映在页面。但是,channels测试的过程中,consumer中的函数体不能加入while循环,
测试的结果是:只有当consumer里的函数执行完,才能全部发送到客户端消息,而不是有消息就能发送。
最后的解决方案:只能前端使用计时器同一个tcp连接不断发送消息,服务器端自执行函数触发我们的查询,造成一个伪实时。不知道是否有更好的方法?