Django Management Command에서 websockets 및 asyncio 사용하기

10348 단어 websocketwebdev
WebSocket을 사용하여 웹 응용 프로그램의 프런트 엔드에서 기존 AJAX가 많이 사용되는 부분을 대체했습니다. 이것은 내가 이전에 많이 사용하지 않았던 asyncio 및 websockets Python 모듈의 영역으로 나를 데려갔고, 나는 이것을 쓸 것이라고 생각했습니다.

AJAX 폴링에서 WebSocket 기반 업데이트로



모든 프론트엔드 클라이언트가 AJAX 호출을 수행하는 대신 백그라운드에서 지속적으로 실행되는 백그라운드 Django 관리 명령을 필요로 하는 잠시 동안 다시 작성하려는 의도가 있었습니다. 매우 빠르게 확장할 수 없게 되는 동일한 처리가 1초마다 발생합니다.

아이디어는 중앙 집중식 처리로 인해 프런트 엔드 클라이언트가 새 데이터의 AJAX에 필요한 데이터베이스 업데이트가 발생할 때마다 WebSocket 메시지로 프런트 엔드 클라이언트에 알리는 것이었습니다.

저는 Python에서 websocket이나 asyncio 코딩을 많이 해본 적이 없었고, 매우 재미있었습니다. C++와 같은 언어로 이벤트 핸들과 메시지 큐를 직접 처리하는 대신 Python은 await/async, coroutine/Task/Futures를 사용하여 매우 강력한 비동기 처리를 제공합니다.



다음은 Django 관리 명령의 약간 수정된 버전입니다.
  • 프런트 엔드 클라이언트 등록/등록 취소,
  • 주기적으로 일부 처리를 수행합니다.
  • 처리 중 변경 사항이 있는 경우 현재 등록된 클라이언트에 알립니다.

  • import logging
    
    from optparse import make_option
    
    from django.core.management.base import BaseCommand
    
    import asyncio
    import websockets
    
    from.utils import configure_logging
    from.models import Task
    from.settings import (WEBSOCKET_BIND_PORT,
                          WEBSOCKET_BIND_ADDRESS)
    
    
    clients = set()
    
    
    async def notify_clients():
        if clients:  # asyncio.wait doesn't accept an empty list
            await asyncio.wait([client.send('updated') for client in clients])
    
    
    async def main_loop():
        while True:
            modified = Task.objects.sync()
            if modified > 0:
                await notify_clients()
            await asyncio.sleep(0.5)
    
    
    async def register(websocket):
        clients.add(websocket)
        logging.info(f'Registered new user: {websocket}')
        await notify_clients()
    
    
    async def unregister(websocket):
        clients.remove(websocket)
        logging.info(f'Unregistered user: {websocket}')
        await notify_clients()
    
    
    async def handle_client(websocket, path):
        await register(websocket)
        try:
            async for message in websocket:
                if message == 'success':
                    pass
                else:
                    await websocket.send('updated')
        finally:
            await unregister(websocket)
    
    
    class Command(BaseCommand):
        """ Monitor tasks and notify websocket clients when they are updated """
    
        help = __doc__
    
        def handle(self, *args, **options):
            configure_logging(**options)
    
            start_server = websockets.serve(handle_client,
                                            WEBSOCKET_BIND_ADDRESS,
                                            WEBSOCKET_BIND_PORT)
            asyncio.get_event_loop().run_until_complete(start_server)
            asyncio.get_event_loop().run_until_complete(main_loop())
            asyncio.get_event_loop().run_forever()
    


    프론트엔드에서는 '업데이트된' 메시지를 기다리고 AJAX 호출만 트리거합니다.

    var ws = new WebSocket("ws{{ websocket_secure | yesno:'s,' }}://" + window.location.hostname + ":{{ websocket_port }}/");
    ws.onmessage = function (event) {
      var type = event.data;
      if (type == 'updated') {
        $.getJSON('/task-list', function(data) {
          // Populate some stuff
        });
      }
    };
    

    좋은 웹페이지 즐겨찾기