Overview:
I have written several blog posts before about how Django uses channels to implement websocket functions. With the use and actual maintenance of the project, the relevant processing methods have been reset.
Generally speaking, the front and back ends only maintain a global connection and carry data to determine specific operations. The general business logic (non-group chat function):
1. The front-end actively initiates a connection and sends data to the back-end. After obtaining the data, the back-end parses out what data the front-end needs, queries the data, and returns it to the front-end. (One request and one return)
2. When some data changes, the back-end needs to proactively inform the front-end so that the front-end can re-query the corresponding data. (Real-time updated data)
1. Dependence
python=3.9.0
Bag:
pip install channels==3.0.0
pip install daphne==3.0.2
pip install redis==4.6.0
pip install channels-redis=3.1.0django-cors-headers==3.5.0
Project structure:
Item name
-
apps
-
user
-
websocket
-
routings.py
-
consumers.py
-
update.py
-
send_date.py
-
__init__.py
-
-
-
Item name
-
settings.py
-
asgi.py
-
urls.py
-
wsgi.py
-
__init__.py
-
-
manage.py
2. settings.py settings
#Register channels INSTALLED_APPS = [ ... 'channels', # Django implements websocket through it ] WSGI_APPLICATION = 'HeartFailure.wsgi.application' #channels requires adding ASGI_APPLICATION ASGI_APPLICATION = 'HeartFailure.asgi.application' #Using channel_layers requires configuring the channel CHANNEL_LAYERS = { "default": { #1. Use memory as a channel (for development use) "BACKEND": "channels.layers.InMemoryChannelLayer", #2. Use redis (online use) # 'BACKEND': 'channels.layers.RedisChannelLayer', # 'CONFIG': { # 'hosts': [('localhost', 6379)], # }, } } #####1. Cross-domain sharing configuration of cors resources CORS_ORIGIN_ALLOW_ALL = True CORS_ALLOW_METHODS = ( 'DELETE', 'GET', 'OPTIONS', 'PATCH', 'POST', 'PUT', 'VIEW', ) CORS_ALLOW_HEADERS = ( 'XMLHttpRequest', 'X_FILENAME', 'accept-encoding', 'authorization', 'content-type', 'dnt', 'origin', 'user-agent', 'x-csrftoken', 'x-requested-with', 'Pragma', 'token' #Request header allows custom strings )
3. Create websocket package
Overview: Put all wesocket-related requests into one package and manage them centrally.
Created under the websocket package:
-
routings.py
-
Store routing information related to websocket requests
-
-
consumers.py
-
Class that stores websocket request processing
-
-
update.py
-
When the data changes, the server actively notifies the front end to update the data.
-
-
send_data.py
-
When the front end initiates a request, the data returned
-
1. consumers.py
from channels.generic.websocket import WebsocketConsumer from channels.exceptions import StopConsumer from asgiref.sync import async_to_sync import time import json # Receive the front-end websocket request and directly send the required data to the individual from apps.websocket.send_data import base_send class AllDataConsumers(WebsocketConsumer): # Unified room name room_name = 'chat_all_data' def connect(self): cls = AllDataConsumers self.room_group_name = cls.room_name # Add to the room group, self.channel_name is the current async_to_sync(self.channel_layer.group_add)( self.room_group_name, self.channel_name ) headers = self.scope['headers'] print(headers) token=None for key, value in headers: if key == b'token': token = value.decode('utf-8') print(token) # Agree to create connection self.accept() def disconnect(self, close_code): print('A browser has exited websocket!!!!') # Leave room group async_to_sync(self.channel_layer.group_discard)( self.room_group_name, self.channel_name ) #Receive message from WebSocket def receive(self, text_data=None, bytes_data=None): ''' :param text_data: Receive string type data :param bytes_data: Receive bytes type data :return: If the browser directly requests it, the result will be returned to the browser alone without sending data to the room group. ''' try: text_data_json = json.loads(text_data) the_type = text_data_json.get('type', 'none') except Exception as e: self.send( json.dumps({'code': 400, 'msg': 'Please pass the data in the format of {"type":"xx","id":x,"params":{}}'}, ensure_ascii=False )) self.disconnect(400) return #1. When the front end actively requests the websocket, it gets the corresponding data and returns the data to the websocket separately. send_data = base_send(text_data_json) if isinstance(send_data,dict): #Need to return data to the requested front end self.send(json.dumps(send_data, ensure_ascii=False)) else: #No need to return data to the requested front end pass # 2. Send data to the room group (no need to do this in non-chat mode) # async_to_sync(self.channel_layer.group_send)( # self.room_group_name, {'type':'send_to_chrome','data':send_data} # ) ''' Parameter Description: self.room_group, which room group to send data to, {'type':'send_to_chrome','data':send_data} send_to_chrome is the processing function, which is responsible for sending the data in the room group to the browser send_data data to send ''' # Customized processing of data in room groups: real-time push is achieved using this def send_to_chrome(self, event): try: data = event.get('data') # Receive room group broadcast data and send the data to websocket self.send(json.dumps(data, ensure_ascii=False)) except Exception as e: print('Failed to push message to global websocket')
2. send_data.py
def base_send(data:dict): ''' Function: When initiating a websocket request, return data to the current websocket :param data: {'type':'Data type to be operated','id':'If you have an id, you specify each data','params':{'page':'Page number','page_size':'Page size', }} :return: ''' the_type = data.get('type') id = data.get('id') send_data = { 'type':the_type, 'data':'returned data' } #User management-search function, user information is updated in real time if the_type == 'search_user_data': #When the front end initiates a websocket request, there is no need to return data in this type return send_data
3. update.py
#channels package related from asgiref.sync import async_to_sync from channels.layers import get_channel_layer class AllDataConsumersUpdate: ''' Function: In the http view, push the specified message to the room group = chat_all_data ''' def _make_channel_layer(self,send_data): ''' :param send_data: Query the good data in the http view and send data to all websocket objects in the room group ''' channel_layer = get_channel_layer() #Get the room group name group_name = 'chat_all_data' #Send data to the room group. Note the group_send method. async_to_sync(channel_layer.group_send)( group_name, #room group name, send data to this room group { 'type':'send_to_chrome', #The consumer class that handles this room group must have a send_to_chrome method 'data':send_data #Data to be sent to the websocket object } ) ''' send_to_chrome: The consumer corresponding to the room group must have this function. In this function, data is sent to all websocket objects in the room group. send_data: queried data ''' #User management-search user page-update data in real time, and the front end will obtain the data by itself def search_user_data(self): send_data = { 'type':'search_user_data', 'page_update':1 } #Send data to the room self._make_channel_layer(send_data=send_data) return True
4. routings.py
from django.urls import path from .import consumers # This variable is the route to store websocket socket_urlpatterns = [ path('socket/all/',consumers.AllDataConsumers.as_asgi()), ]
4. Modify the asgi.py file at the same level as settings.py
asgi.py
import os from django.core.asgi import get_asgi_application #new module from channels.routing import ProtocolTypeRouter, URLRouter #Import the routing module of websocket from apps.websocket import routings #Project name, directory name where settings.py is located os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'project name.settings') application = ProtocolTypeRouter({ # http routing goes here "http": get_asgi_application(), # The routing variable socket_urlpatterns under the routings module under the chat application is the list of stored routes. "websocket": URLRouter(routings.socket_urlpatterns) })
5. How to send websocket notification in view function
from apps.websocket.update import websocket_update_obj #Interface for websocket to push data #Push can be achieved by directly calling the required method in the view function. websocket_update_obj.search_user_data()
Putting all the push methods into one class can be easily managed, and unified modifications can also be achieved during later modifications.
6. Start the project
python manage.py runserver 8005
Seeing ASGI/Channels Version xxx indicates that the startup is successful. At this time, the Django project only supports websocket.
7. Test
Visit: EasySwoole-WebSocket online testing tool
-
1. Service address: http://127.0.0.1:8005/socket/all/
-
2. Click Connect
-
3. Send data: {“type”:”search_user_data”}
-
4. Click to send