[Django]Channels

Jay·2022년 10월 20일
0
post-thumbnail
post-custom-banner

해당 글은 해외 블로그, 공식 문서 등을 읽고 제가 이해한 내용을 바탕으로 작성하였습니다. 잘못된 내용에 관한 많은 지적 부탁드립니다..!

HTTP와 WebSocket

HTTP와 WebSocket은 모두 응용계층에서 사용되는 프로토콜이다. 두 프로토콜 모두 TCP를 기반으로 동작하지만 여러 차이점이 존재한다. 어떠한 차이점이 존재하고 그로 인해 두 프로토콜이 어떻게 달리 사용되는지 알아보자.

HTTP

HTTP는 client-server 모델을 기반으로 사용되는 프로토콜이다. client와 server가 통신하기 위해서는 connection생성 - request/response - connection종료 단계로 진행되게 된다. 이러한 HTTP에는 여러 제한이 있었다.
첫번째는 매번 request와 response를 주고 받기 위해서 connection을 생성하고 종료했어야 했다. HTTP 1.1 이후로는 1번의 connection 생성으로 여러번의 request/response를 주고 받을 수 있게 되었지만 여전히 connection 생성하고 종료하는 오버헤드가 존재하였다.
두번째는 반드시 client 측에서 request를 요청해야 server 측에서 response를 응답할 수 있다는 것이다. server에서 client로 요청하지 않은 데이터를 전송할 수 없기 때문에, 실시간으로 데이터의 변화나 이벤트를 감지하기 위해서는 client가 일정 시간 단위로 계속해서 request를 전송해야했다. 이러한 방식을 polling이라고 한다. 하지만 계속 request와 response를 주고 받기 위해서는 connection을 계속해서 생성/종료해야하기 때문에 네트워크 비용이 상당했다.
이와 같은 특징으로 인해 HTTP는 실시간으로 데이터를 주고받거나 이벤트를 감지하는 서비스에서 사용되기에는 적합하지 않았다.


WebSocket

WebSocket 또한 데이터를 주고받기 위해서 HTTP 프로토콜을 사용하여 connection을 생성한다. 하지만 connection을 생성한 이후에는 프로토콜 스위칭을 통해 WebSocket 프로토콜로 통신하게 된다.
WebSocket은 전이중통신(Full-Duplex)방식을 사용한다. 전이중통신방식은 통신선이 수신선/송신선으로 분리되어있어 동시에 전송과 수신이 가능하다. 따라서 client의 요청없이 server에서 client로 데이터를 전송하는 server push가 가능해졌다. 또한 하나의 connection 생성은 client나 server에서 connection을 닫을 때까지 유효했기 때문에 하나의 connection 생성으로 많은 데이터를 주고 받을 수 있게 되었다.
이러한 특징으로 인해 WebSocket 프로토콜은 polling 방식보다 적은 네트워크 비용으로 실시간성을 가질 수 있는 특성을 가지게 되었다. 덕분에 실시간 시스템과 높은 트래픽을 가진 애플리케이션에서 WebSocket 프로토콜이 자주 사용되게 되었다.




Channels와 Channels Layer

Django의 기본 프로토콜은 HTTP로, 기본의 request-response 방식으로 동작하는 웹 애플리케이션을 구현하는데는 문제가 없다. 하지만 이후 WebSocket이 등장하였다. 기존의 Django view의 경우 life span이 하나의 request-response였기 때문에 connection을 연결한 채 여러 데이터를 주고받을 수 없었다. 이러한 한계를 극복하기 위해 Channels가 등장하였다.

Interface Server
인터페이스 서버는 HTTP/WebSocket 프로토콜과 파이썬으로 작성된 애플리케이션의 인터페이스 역할을 수행하는 서버이다. 인터페이스를 통해 HTTP/WebSocket Request를 파이썬 언어로 처리할 수 있게 된다.

Channel
Channel은 기본적으로 Message Queue이다. queue로 들어온 메세지는 해당 queue를 주시하고 있는 리스너에게 전달된다.

Producer
메세지를 생성하고 WebSocket 서버로 전달하는 요소이다. Channel을 통해 메시지를 생성하고 전달하며 비동기적으로 consumer와 통신한다.

Consumer
메세지(WebSocket request)를 channel을 통해 전달받아 처리하는 요소이다. 결과를 반환하거나 반환하지 않을 수 있다.

Channel Layer
메세지를 producer에게서 전달받아 consumer에게 전달하는 매커니즘이다. producer로부터 전달 받은 메세지를 라우터를 통해 알맞은 consumer에게 전달하게 된다.

Channels 의 동작

WebSocket Connection이 생성되고, producer에게서 channel layer로 메세지가 전달된다. channel layer로 전달된 메시지는 해당 메세지가 HTTP/Websocket 종류에 따라 다르게 전달된다.

project/project/asgi.py

django_asgi_app = get_asgi_application()

application = ProtocolTypeRouter({
  "http": django_asgi_app,
  "websocket": JwtAuthMiddleware( //JwtAuthMiddleware는 토큰검증방식 사용을 위해 추가한 미들웨어이다. 무시해도 상관없다.
        URLRouter(
            alarms.routing.websocket_urlpatterns
        )
    ),
})

application은 장고 애플리케이션이다. ProtocolTypeRouter는 들어온 Request의 프로토콜 타입에 따라 알맞은 consumer에게 전달될 수 있도록 한다. http 프로토콜인 경우 기존의 view로 요청을 처리하고 response를 반환한다.
websocket request인 경우에는 정의된 라우팅 url을 따라 consumer를 찾아가게 된다.

project/app/routing.py

from django.urls import re_path

from . import consumers

websocket_urlpatterns = [
    re_path(r'ws/alarms/(?P<user_id>\w+)/$', consumers.AlarmConsumer.as_asgi()),
]

위의 routing.py 파일에 정의된 url에는 url과 매칭되는 Consumer를 정의하고 있다.

class AlarmConsumer(AsyncWebsocketConsumer):
    
    async def connect(self):
        self.user = self.scope['user']
        self.channel_layer = get_channel_layer()
        self.group_name = str(self.user.username)
        
        await self.channel_layer.group_add(
            self.group_name,
            self.channel_name
        )
        
        await self.accept()
        
    async def send_alarms(self, event):
        ...
        
    async def receive(self, text_data):
        ...

    async def disconnect(self, close_code):
        await self.channel_layer.group_discard(
            self.group_name,
            self.channel_name
        )

Consumer 클래스이다. connect와 disconnect는 producer와 데이터를 주고받기 위한 channel을 생성, 제거하는데 사용되는 필수적인 메소드이다.
여기서 이루어지는 channel을 통한 producer와 consumer는 앞서 http와 websocket 프로토콜에서 connection이 생성되는 것이 아니다. http, websocket 프로토콜에서 connection 생성은 데이터를 주고받을 두 컴퓨터를 확인하고 준비하는 단계라면, 여기서 channel을 통해 producer와 consumer를 연결하는 것은 두 프로세스가 데이터를 주고받을 통로를 지정하게 되는 것이다.




reference

https://channels.readthedocs.io/en/stable/topics/channel_layers.html
https://ably.com/periodic-table-of-realtime/django-channels
https://blog.heroku.com/in_deep_with_django_channels_the_future_of_real_time_apps_in_django
https://nitro04.blogspot.com/2020/01/django-python-asgi-wsgi-analysis-of.html

post-custom-banner

0개의 댓글