Using the Channels feature in Django

Preface: recently the background to write the game update version function, simply is the front-end send update request, the back-end needs to update many servers and a variety of operations, originally thought to achieve not difficult, then found that because the back-end needs to perform a long time, the front-end returns an error, the back-end will be executed, but the front-end first disconnected, so that in the front page I can not see the update results. By adjusting the nginx parameters, set the timeout time, or the log will report 499 status code error. Then I learned about websocket, and for requests that need to be processed for a long time, it is better to use websocket, and I implemented my own function by using websocket.

I. What is WebSocket

WebSocket is a protocol for full-duplex communication over a single TCP connection. webSocket allows the server to actively push data to the client.

In the WebSocket protocol, the client browser and server need to complete only one handshake to create a persistent connection and transfer data in both directions between the browser and the server.

Important fields in the response header of WebSocket.

HTTP/1.1 101 Swi tching Protocols: Switching protocols, the WebSocket protocol establishes TCP connections at the transport layer via the HTTP protocol

Connection and Upgrade: indicates the WebSocket response initiated by the server side

Sec-WebSocket-Accept: indicates that the server has accepted the client's request, as calculated by Sec-WebSocket-Key

Advantages of WebSocket protocol.

Support two-way communication, more real-time

Lightweight data format, low performance overhead, efficient communication

Support for extensions, users can extend the protocol or implement custom sub-protocols (e.g., support for custom compression algorithms, etc.)

Disadvantages of WebSocket protocol.

A small number of browsers do not support it, and there are differences in the degree and manner of browser support

Long connections require higher code stability for back-end processing, and back-end push functions are relatively complex

There are a large number of components that can be reused in a mature HTTP ecosystem, WebSocket is less

Application scenarios of WebSocket.

Live chat communication, website message notification

Online collaborative editing, such as Tencent documents

Multi-player online games, video pop-ups, stock fund implementation quotes

Second, what is Channels

Django itself does not support WebSocket, but can be implemented by integrating the Channels framework to WebSocket

Channels is an enhanced framework for the Django project , you can make Django not only support HTTP protocol , but also support WebSocket , MQTT and other protocols , while Channels also integrates Django auth and session system to facilitate user management and authentication .

2.1Channels files and configuration meaning

asgi.py: interface between the web protocol service and the Python application, capable of handling a variety of common protocol types, including HTTP, HTTP2 and WebSocket

channel_layers: configured in settings.py. Similar to a channel where the sender (producer) sends a message on one end and the consumer listens on the other

routings.py: equivalent to urls.py in Django

consumers.py: equivalent to Django's views.py

2.2channels documentation links

 
h ttps://channels.readthedocs.io/en/latest/introduction.html

2.3. WSGI and ASGI are different

WSGI (Python Web Server Gateway Interface): a simple and common interface between a web server and a web application or framework defined for the Python language.

ASGI (Asynchronous Web Server Gateway Interface): Asynchronous Gateway Protocol Interface, a standard interface between web protocol services and Python applications, capable of handling a variety of common protocol types, including HTTP, HTTP2 and WebSocket.

III. Using Channels in Django

3.1 Installing channels

 
 pip install channels==2.1.7

3.2 Modify the setting.py file

 
INSTALLED_APPS = [
   'django.contrib.staticfiles', 
   ... ... 
   'channels', 
]

 # Specify the ASGI routing address 
ASGI_APPLICATION = 'webapp.routing.application' #ASGI_APPLICATION specifies the location of the main route as the application in the routing.py file under webapp

3.3. Create routing.py in the same directory as setting.py. routing.py is similar to url.py in Django, which specifies the route for the websocket protocol

 
 from channels.auth import AuthMiddlewareStack
 from channels.routing import ProtocolTypeRouter, URLRouter
 from channels.security.websocket import AllowedHostsOriginValidator
 import webapp.routing

application = ProtocolTypeRouter({
   'websocket': AllowedHostsOriginValidator(
        AuthMiddlewareStack(
            URLRouter(
                webapp.routing.websocket_urlpatterns
            )
        )
    )
})

ProtocolTypeRouter: ASGI supports many different protocols, here you can specify the routing information for a specific protocol, here only the websocket protocol is used, here only the websocket can be configured

AllowedHostsOriginValidator: specify the IPs allowed to access, after setting it, it will go to settings.py in Django to find the IPs set by ALLOWED_HOSTS

AuthMiddlewareStack: used for WebSocket authentication, inherits Cookie Middleware, SessionMiddleware, SessionMiddleware.

django's channels encapsulates django's auth module, using this configuration we can get the user's information, and the url path of the request in the consumer with the following code

 
 def connect(self):
    self.user = self.scope ["user" ]
    self.request_url = self.scope ['path']

self.scope is similar to request in django and contains useful information about the request's type, path, header, cookie, session, user, etc.

URLRouter: specify the path to the routing file, you can also write the routing information directly here, the code configures the path to the routing file, it will go to the corresponding application under the routeing.py file to find the websocket_urlpatterns

3.4 The contents of webapp.routing.py are as follows

 
 from django.urls import path
 from webapp.consumers import ChatConsumer

websocket_urlpatterns = [
  path ('ws/chat/', ChatConsumer)
websocket_urlpatterns = [ path('ws/chat/',ChatConsumer)

The routing.py routing file is similar to django's url.py and has the same syntax, meaning that access to ws/chat/ is handled by ChatConsumer.

3.5 Create consumers.py in the application that will use WebSocket. consumers.py is used to develop python applications with ASGI interface specification, while view.py in Django is used to develop python applications with WSGI interface specification.

 
 from channels.generic.websocket import WebsocketConsumer
 from channels.generic.websocket import AsyncWebsocketConsumer
 import json,time

Channels supports both synchronous and asynchronous methods

The code for the synchronous method is as follows.

 
 class ChatConsumer(WebsocketConsumer):
   # websocket connection establishment execution method 
   def connect(self):
        self.accept()

   # Method to be executed when websocket is disconnected 
   def disconnect(self, close_code):
        self.close()

   # Execute the function when receiving a message from the websocket 
   def receive(self, text_data):
        text_data_json = json.loads(text_data)
        message = f 'result:{text_data_json}' 
        self.send(text_data=json.dumps(
        {
            'message': message
        }))

The code for the asynchronous method is as follows:

 
 class ChatConsumer(AsyncWebsocketConsumer):
   #Method to be executed when websocket connection is established 
   async def connect(self):
       await self.accept()

   # Methods to execute when websocket is disconnected 
   async def disconnect(self, close_code):
        print(close_code)


   # Execute the function when receiving a message from the websocket 
   async def   receive(self, text_data):
       for i in range (10 ):
            time.sleep(i)
            message = 'Result: ' + str(i)
           await self.send(text_data=json.dumps({
               'message': message
            })
        )

Note that all logic in asynchronous should be asynchronous, not a mix of synchronous and asynchronous code.

Fourth, the front-end Websocket use

WebSocket object supports four messages: onopen, onmessage, oncluse and onerror

onopen: when the browser and the websocket server connect successfully, the onopen message is triggered

onerror: The onerror message is triggered if the connection fails, or if there is a failure to send or receive data, or if there is a data processing error

onmessage: the onmessage message is triggered when the browser receives data from the websocket server, and the parameter e contains the data sent from the server

onclose: when the browser receives a request from the websocket server to close the connection, the onclose message is triggered

Splice the websocket request address to establish a long connection

 
 var chatSocket = new WebSocket ('ws://' + window.location.host + '/ws/ver_update/');

Connection events

 
chatSocket.onopen = function  () {
   console.log(getCurrentDate (2 ) + ' ' + 'websocket connection success') 
};

Error events

 
chatSocket.onerror = function   () {
    console. error (getCurrentDate ( 2) + ' ' + 'websocket connection error') 
};

Close events

 
chatSocket.onclose = function   (e) {
    layer.msg ('websocket closed, checking error log', {icon: 2} );
    console. error (getCurrentDate (2 ) + ' ' + 'websocket closed unexpectedly status code:' + e.code);
    chatSocket.close();
};

Receiving events

 
chatSocket.onmessage = function ( e  ) {
   var data = JSON.parse(e.data);
}

V. Testing the Channels function

Summary: Since using the Websocket function, no more sudden front-end disconnections, for long-running tasks, the use of websocket is a good choice ~, there is a shortage of places please tell us more