Django websocket之web端实时查看日志实践案例

  • 2019 年 10 月 3 日
  • 笔记

??Django Channels??????????web???tailf?????Channels????????Celery???

??????Django??Channels??WebSocket–?????????Channels???????????????????Channels????????Django?????WebSocket??????????Channels+Celery??web?tailf????????????Channels

???????????????????????tailf????????????????????????????????????????????????????????????????????????????

??????????

??????????????

????

??????????????

  • python==3.6.3
  • django==2.2
  • channels==2.1.7
  • celery==4.3.0

celery4?windows???????????linux?????

??????

???????????????????????????????settings.py???????????????

?settings.py??????TAILF??????????key????????value???????

TAILF = {      1: '/ops/coffee/error.log',      2: '/ops/coffee/access.log',  }

??Web????

????????????tailf?app??????settings.py?INSTALLED_APPS??app?????????

tailf      - migrations          - __init__.py      - __init__.py      - admin.py      - apps.py      - models.py      - tests.py      - views.py

??????????Django?????????

url:

from django.urls import path  from django.contrib.auth.views import LoginView,LogoutView    from tailf.views import tailf    urlpatterns = [      path('tailf', tailf, name='tailf-url'),        path('login', LoginView.as_view(template_name='login.html'), name='login-url'),      path('logout', LogoutView.as_view(template_name='login.html'), name='logout-url'),  ]

??????????????????????????Django???LoginView?logoutView????????Login?Logout??

?????????login.html??????????????post??username?password????????????

view:

from django.conf import settings  from django.shortcuts import render  from django.contrib.auth.decorators import login_required      # Create your views here.  @login_required(login_url='/login')  def tailf(request):      logDict = settings.TAILF      return render(request, 'tailf/index.html', {"logDict": logDict})

???login_required?????????????????????/login????

logDict ?setting????????TAILF???????????

template:

{% extends "base.html" %}    {% block content %}  <div class="col-sm-8">    <select class="form-control" id="file">      <option value="">????????</option>      {% for k,v in logDict.items %}      <option value="{{ k }}">{{ v }}</option>      {% endfor %}    </select>  </div>  <div class="col-sm-2">    <input class="btn btn-success btn-block" type="button" onclick="connect()" value="????"/><br/>  </div>  <div class="col-sm-2">    <input class="btn btn-warning btn-block" type="button" onclick="goclose()" value="????"/><br/>  </div>  <div class="col-sm-12">    <textarea class="form-control" id="chat-log" disabled rows="20"></textarea>  </div>  {% endblock %}

????TAILF???????????select?????????????????logDict.items???????????key?value

???????????????????????????????

??Channels??WebSocket

?????????????????????????websocket????????celery????while????????????????websocket?channel????????????

???????channels

  1. ???routing???????webapp/routing.py
from channels.auth import AuthMiddlewareStack  from channels.routing import ProtocolTypeRouter, URLRouter    from django.urls import path, re_path  from chat.consumers import ChatConsumer  from tailf.consumers import TailfConsumer    application = ProtocolTypeRouter({      'websocket': AuthMiddlewareStack(          URLRouter([              path('ws/chat/', ChatConsumer),              re_path(r'^ws/tailf/(?P<id>d+)/$', TailfConsumer),          ])      )  })

???????????URLRouter???????????????list?????????????????????

??????????????????????routing??P<id>d+???ID????????????ID????settings????TAILF???????

routing????Django??url?????????re_path????routing??

  1. ??consumer?tailf/consumers.py???
import json  from channels.generic.websocket import WebsocketConsumer  from tailf.tasks import tailf      class TailfConsumer(WebsocketConsumer):      def connect(self):          self.file_id = self.scope["url_route"]["kwargs"]["id"]            self.result = tailf.delay(self.file_id, self.channel_name)            print('connect:', self.channel_name, self.result.id)          self.accept()        def disconnect(self, close_code):          # ??????Task          self.result.revoke(terminate=True)          print('disconnect:', self.file_id, self.channel_name)        def send_message(self, event):          self.send(text_data=json.dumps({              "message": event["message"]          }))

????Channels?????????????????????channel?????????????????????????

connect

????self.scope???Django??request??????????????self.scope["url_route"]["kwargs"]["id"]??routing????????ID

???id?channel_name???celery?????tailf?tailf??id???????????????????????channel_name????channel

disconnect

?websocket?????????????Celery?Task??????celery?????

??Celery?????revoke????????????

self.result.revoke(terminate=True)

??self.result???result?????id

??terminate=True??????????Task??True???Task?????????????False?????????Task????????????????While??????True????????

??Celery???????????

from webapp.celery import app  app.control.revoke(result.id, terminate=True)

send_message

??????Django?view??Celery?task???channel????????????????

??Celery????????

???????Channels???WebSocket??connect????celery??tailf????????????

??Celery??????????????Django??Celery????????????????????????????????????task

task???????

from __future__ import absolute_import  from celery import shared_task    import time  from channels.layers import get_channel_layer  from asgiref.sync import async_to_sync  from django.conf import settings      @shared_task  def tailf(id, channel_name):      channel_layer = get_channel_layer()      filename = settings.TAILF[int(id)]        try:          with open(filename) as f:              f.seek(0, 2)                while True:                  line = f.readline()                    if line:                      print(channel_name, line)                      async_to_sync(channel_layer.send)(                          channel_name,                          {                              "type": "send.message",                              "message": "?????????????? ???? " + str(line)                          }                      )                  else:                      time.sleep(0.5)      except Exception as e:          print(e)

????????Channels????????????Channels????????Channel

??????????????????????????????????Channel??????????????????

async_to_sync(channel_layer.send)(      channel_name,      {          "type": "send.message",          "message": "?????????????? ???? " + str(line)      }  )

channel_name ???????????channel_name???????????channel

type ?????Channels?TailfConsumer???send_message????????_??.??

message ????????channel?????

???????Channel????????????Group??????????

async_to_sync(channel_layer.group_send)(      group_name,      {          'type': 'chat.message',          'message': '??????????????'      }  )

???????channel?send??group_send?channel_name??group_name??

????????????channel layer???????async_to_sync?????

????WebSocket??

????????????????????????WebSocket

  function connect() {      if ( $('#file').val() ) {        window.chatSocket = new WebSocket(          'ws://' + window.location.host + '/ws/tailf/' + $('#file').val() + '/');          chatSocket.onmessage = function(e) {          var data = JSON.parse(e.data);          var message = data['message'];          document.querySelector('#chat-log').value += (message);          // ???????          $('#chat-log').scrollTop($('#chat-log')[0].scrollHeight);        };          chatSocket.onerror = function(e) {          toastr.error('????????')        };          chatSocket.onclose = function(e) {          toastr.error('websocket????')        };      } else {        toastr.warning('???????????')      }    }

????????????websocket?????????????

??????????????????????????????????????????

Web??????WebSocket

web???“????”???????????WebSocket?onclose?????????Channels??consumer?disconnect???????Celery?????????

??????.close()??????WebSocket????????????????????WebSocket?onclose?????????Celery?????????

  function goclose() {      console.log(window.chatSocket);        window.chatSocket.close();      window.chatSocket.onclose = function(e) {        toastr.success('????????')      };    }

???????????Tailf???????????????

????

?????????????Channels?????????????????Channels?????????????????????Channels?????????channel layer????????????????????????????????????????????????demo??????????????????????????????????????????


?????????