zoukankan      html  css  js  c++  java
  • Django Channels 学习笔记

    一.为什么要使用Channels

      在Django中,默认使用的是HTTP通信,不过这种通信方式有个很大的缺陷,就是不能很好的支持实时通信。如果硬是要使用HTTP做实时通信的话只能在客户端进行轮询了,不过这样做的开销太大了。
      因此,在1.9版本之后,Django实现了对Channels的支持,他所使用的是WebSocket通信,解决了实时通信的问题,而且在使用WebSocket进行通信的同时依旧能够支持HTTP通信。

    二.创建一个Django Channels样例的完整流程

    1. 首先下载相应插件

    pip install channels
    pip install asgi_redis

    2. 创建一个新工程

    django-admin.py startproject channels_example


    工程目录如下:
    |-- channels_example
    |    |--channels_example
    |        |-- __init__.py
    |        |-- settings.py
    |        |-- urls.py
    |        |-- wsgi.py
    |    |-- manage.py

    这里我们要在内层的channels_example目录下创建几个channels所需的必要文件。
    包括:
    routing.py
    consumer.py
    asgi.py

    这几个文件先放着,之后我会一一介绍。
    新的目录如下:
    |-- channels_example
    |    |--channels_example
    |        |-- __init__.py
    |        |-- settings.py
    |        |-- urls.py
    |        |-- wsgi.py
    |        |-- routing.py
    |        |-- consumer.py
    |        |-- asgi.py
    |    |-- manage.py

    3. 设置settings.py中的参数

    这里首先将channels加入到INSTALLED_APPS中,如下:

    INSTALLED_APPS = [
        'django.contrib.admin',
        'django.contrib.auth',
        'django.contrib.contenttypes',
        'django.contrib.sessions',
        'django.contrib.messages',
        'django.contrib.staticfiles',
        'channels',
    ]

    然后,添加新的参数CHANNEL_LAYERS,如下:

    CHANNEL_LAYERS = {
        "default": {
            "BACKEND": "asgiref.inmemory.ChannelLayer",
            "ROUTING": "channels_example.routing.channel_routing",
        },
    }

    这里,需要注意的是 ROUTING 参数,他是用来指定WebSocket表单的位置,当有WebSocket请求访问时,就会根据这个路径找到相应表单,调用相应的函数进行处理。
    channels_example.routing 就是我们刚才建好的routing,py文件,里面的channel_routing我们下面会进行填充。

    4. 填写新的表单——routing.py文件

    上一步我们已经了解到,routing.py文件其实就是个表单,功能和Django原来的urls.py文件一样,不过这里使用的不是URL,而是请求的类型。

    from channels.routing import route
    from channels_example import consumers  #导入处理函数
    
    channel_routing = [
        #route("http.request", consumers.http_consumer), 这个表项比较特殊,他响应的是http.request,也就是说有HTTP请求时就会响应,同时urls.py里面的表单会失效
    
    
        route("websocket.connect", consumers.ws_connect),        #当WebSocket请求连接上时调用consumers.ws_connect函数
        route("websocket.receive", consumers.ws_message),        #当WebSocket请求发来消息时。。。
        route("websocket.disconnect", consumers.ws_disconnect),    #当WebSocket请求断开连接时。。。
    ]

    5. 填写新的视图文件——consumers.py

    上一步我们已经了解到,routing.py相当于新的urls.py,而consumers.py就相当于新的view.py。

    代码:

    from django.http import HttpResponse
    from channels.handler import AsgiHandler
    
    #message.reply_channel    一个客户端通道的对象
    #message.reply_channel.send(chunk)  用来唯一返回这个客户端
    
    #一个管道大概会持续30s
    
    #当连接上时,发回去一个connect字符串
    def ws_connect(message):
        message.reply_channel.send({"connect"})
    
    #将发来的信息原样返回
    def ws_message(message):
        message.reply_channel.send({
            "text": message.content['text'],
        })
    #断开连接时发送一个disconnect字符串,当然,他已经收不到了
    def ws_disconnect(message):
        message.reply_channel.send({"disconnect"})

    6. asgi.py文件

    类似于Django自己生成的wsgi.py文件,内容如下:

    import os
    import channels.asgi
    
    os.environ.setdefault("DJANGO_SETTINGS_MODULE", "channels_example.settings")    #这里填的是你的配置文件settings.py的位置
    channel_layer = channels.asgi.get_channel_layer()

    7. 发布运行

    python manage.py runserver 0.0.0.0:8000

    这是一个测试用的JS代码,能够发送和接收WebSocket请求:

    <!DOCTYPE HTML>
    <html>
       <head>
       <meta charset="utf-8">
       <title></title>
        
          <script type="text/javascript">
             function WebSocketTest()
             {
                if ("WebSocket" in window)
                {
                    alert("您的浏览器支持 WebSocket!");
                   
                    socket = new WebSocket("ws://" + "localhost:8000" + "/channels_example/");
                    socket.onmessage = function(e) {
                        alert(e.data);
                    }
                    socket.onopen = function() {
                        socket.send("hello world");
                    }
                    // Call onopen directly if socket is already open
                    if (socket.readyState == WebSocket.OPEN) socket.onopen();
                }
                
                else
                {
                   // 浏览器不支持 WebSocket
                   alert("您的浏览器不支持 WebSocket!");
                }
             }
          </script>
            
       </head>
       <body>
       
          <div id="sse">
             <a href="javascript:WebSocketTest()">运行 WebSocket</a>
          </div>
          
       </body>
    </html>

    三.进阶 —— Group

    四.进阶 —— message详解

    五.遇到的问题

    1. 后来的请求可能会打断之前请求的执行

    比如,我写了下面三个函数响应connect,send和disconnect

    @channel_session
    def ws_message(message):
        #送给全组人
        print message.channel_session['room']
        Group("chat-%s" % message.channel_session['room']).send({
            "text": message.content['text'],
        })
    
    @channel_session
    def ws_connect(message):
        room = message.content['path'].strip("/")
        message.channel_session['room'] = room
        Group("chat-%s" % room).add(message.reply_channel)
    
    
    @channel_session
    def ws_disconnect(message):
        print message.channel_session['room']
        Group("chat-%s" % message.channel_session['room']).discard(message.reply_channel)

    客户端我这样写:

    socket.onopen = function() {
            socket.send("hello world");
    }

    这样就会产生一个问题,当客户端连接到服务端时,服务端执行ws_connect()函数,然后客户端又send()了数据,服务端就会去调用ws_message()函数,而终止了ws_connect()函数的执行。

    此时,我的ws_connect()函数还未对message.channel_session['room']进行赋值,而ws_message()函数就调用了他,所以会报错。

    然后我尝试了断开连接,运行正常,这就说明ws_connect()函数最终还是执行完了,不过是在ws_message()函数执行之后。

    综上,当服务端接收到新的请求时,可能会打断上个请求的执行,这点需要特殊注意一下。

  • 相关阅读:
    ORACLE时间字段取年、月、日、季度【转】
    Oracle查询指定索引提高查询效率【转】
    ORACLE常用命令【转】
    ORACLE中LOB字段的使用和维护
    Oracle建立DBLINK的详细步骤记录【转】
    Oracle实用日期函数总结[转]
    js脚本中过滤特殊字符的正则表达式
    获取上一页面的URL的方法
    Repeat控件绑定数据格式显示
    我刚做的一个TreeView的CheckBox进行选中插入数据库,从数据库中读取数据后让CheckBox勾选的代码!
  • 原文地址:https://www.cnblogs.com/14061216chen/p/5998376.html
Copyright © 2011-2022 走看看