Channels 개념

7405 단어
Django의 전통적인 개념은 HTTP 요청과 응답을 중심으로 전개된다. 서버에서 요청을 받고 Django가 서비스를 위해 응답을 생성하고 발송한 다음에 Django가 떠나고 다음 요청을 기다리고 있다.
인터넷이 간단한 브라우저 행위만 포함할 때, 이것은 아무런 문제가 없다.그러나 현대 인터넷에는 웹소켓과 HTTP 2 서버 푸시 등 기능이 포함돼 있어 사이트가 전통적인 요청/응답 순환 외에 다른 통신을 할 수 있다.
이외에도 많은 비관건적인 작업들이 있는데, 프로그램이 그것을 나누어 주고, 응답을 보낸 후에 계속 처리해야 한다. 예를 들어 캐시를 저장하거나, 새로 업로드한 그림을 위한 미리 보기 그림을 만들어야 한다.
Channels는 Django의 실행 방식을 '이벤트 대상' 으로 변경합니다. 요청에 응답하는 것이 아니라 채널에서 보내는 대량의 이벤트에 응답합니다.여기에는 여전히 지속적인 상태가 없다. 모든 이벤트 처리 프로그램은 이벤트 소비자라고도 할 수 있고, 보기를 호출하는 것처럼 독립적으로 호출된다.
먼저 통로가 무엇인지 봅시다.
통로가 뭐예요?
당연히 핵심은 통로라는 데이터 구조다.통로가 뭐예요?그것은 질서정연하고 선진적인 대기열로 메시지가 기한이 지나 최대 한 번, 한 번에 한 개의 감청 앱만 보낸다.
이를 작업 대기열로 분류할 수 있다. 생산자는 메시지를 하나의 채널에 보내고, 그 채널을 감청하는 소비자에게 보낼 수 있다.
최대 한 번은 한 통의 메시지가 소비자가 받거나 아무도 받지 않는다는 것을 가리킨다. (예를 들어 통로가 갑자기 붕괴되었다.)이에 대응하는 것은 최소한 한 번이다. 정상적인 상황에서 한 메시지가 한 소비자가 받지만 프로그램이 붕괴되면 나중에 다시 보낼 가능성이 높기 때문에 다른 소비자가 다시 받을 수도 있다.후자는 우리가 원하는 것이 아니다.
또 다른 몇 가지 제한이 있다. 메시지는 서열화된 유형이어야 하고, 일정한 크기 제한 안에 있어야 한다. 그러나 더 높은 사용법을 접하기 전에 이런 세부 사항을 걱정할 필요가 없다.
채널에 용량이 있어 소비자가 없어도 생산자는 먼저 채널에 대량의 메시지를 쓸 수 있고 소비자가 잠시 후에 나타나서 대기열의 메시지를 받을 수 있다.
만약에 Go 채널을 사용한 적이 있다면 Go 채널은 Django 채널과 상당히 비슷하다. 관건적인 차이점은 Django 채널은 네트워크가 투명하고 서로 다른 프로세스, 심지어 서로 다른 컴퓨터에서 실행되는 생산자와 소비자가 인터넷을 통해 우리의 채널에 접근할 수 있다는 것이다.
네트워크에서, 우리는 이름 문자열을 통해 채널을 유일하게 식별한다. 모든 컴퓨터가 같은 채널의 백엔드에 접속하기만 하면, 메시지를 모든 이름 채널로 보낼 수 있다.예를 들어 두 대의 서로 다른 컴퓨터가 모두 'http.request' 라는 채널을 썼는데, 그들이 쓴 것은 같은 채널이다.
채널은 어떻게 사용합니까?
그럼 Django는 이 채널을 어떻게 사용합니까?Django에서 채널을 사용하는 함수를 작성할 수 있습니다.
def my_consumer(message):
    pass

그런 다음 채널 라우트에 채널을 할당합니다.
channel_routing = {
    "some-channel": "myapp.consumers.my_consumer",
}

이 채널에서 메시지를 받을 때마다 Django는 이 소비자 함수를 호출하고 메시지 파라미터를 전송합니다.메시지는 콘텐츠, 채널, 기타 일부 속성을 가지고 있습니다. 그 중에서 콘텐츠 속성은 반드시 사전 (dict) 형식이고 채널 속성은 메시지를 보내는 채널의 이름을 표시하는 데 사용됩니다.
채널스는 Django를 전통적인 요청/응답 모델에서 작업 프로세스 모델로 바꾸었다. 모든 소비자의 채널을 감청하고 메시지를 받으면 대응하는 소비자를 운행한다.현재 Django는 WSGI 서버에 연결된 프로세스에서만 실행되는 것이 아니라 세 개의 독립된 레이어에서 실행됩니다.
  • 인터페이스 서버로 Django와 외부 세계 간의 통신에 사용됩니다.이것은 WSGI 어댑터와 단독 웹소켓 서버를 포함한다. 이것은 실행 인터페이스 서버에서 소개되었다.
  • 채널 백엔드에는 확장 가능한 파이톤 코드와 메시지를 전송하는 데이터 저장 메커니즘(예를 들어 Redis나 공유 메모리 세그먼트)이 포함되어 있다.
  • 작업 프로세스는 모든 관련 채널을 감청하고 메시지가 준비될 때 소비자 코드를 실행한다.

  • 이것은 아마도 매우 간단해 보일 것이다. 그러나 우리는 이렇게 계획했다. 완전히 다른 구조를 시도하기보다는 기존의 Django 보기를 좀 더 복잡하고 추상적으로 하는 것이 낫다.
    A view takes a request and returns a response; a consumer takes a channel message and can write out zero to many other channel messages.
    현재 요청 채널 (이름 http.request) 과 단일 클라이언트를 위한 응답 채널 (예: http.response.o4f2h2fd) 을 만듭니다. 요청 채널의 메시지는 리플리channel 속성, 응답 채널의 이름입니다.이렇게 하면 정보 소비자는 하나의 보기와 유사하다.
    #   http.request
    def my_consumer(message):
        #       message   ,  Request  
        django_request = AsgiRequest(message)
        #     
        django_response = view(django_request)
        #       message  
        for chunk in AsgiHandler.encode_response(django_response):
            message.reply_channel.send(chunk)
    

    이것이 바로 통로의 메커니즘이다.인터페이스 서버는 외부 연결 (HTTP, WebSocket 등) 을 채널의 메시지로 변환하고, 이 메시지를 처리하기 위해 작업 프로세스를 작성합니다.일반적인 HTTP 요청은 Django에 내장된 소비자에게 맡기고 후자는 보기/템플릿 시스템에 요청을 전송하지만 필요하면 다시 써서 기능을 추가할 수 있다.
    관건적인 부분은 당신이 만든 이벤트를 포함하여 모든 이벤트에 응답할 수 있는 코드를 실행할 수 있고, 실행된 코드는 메시지를 더 보낼 수 있다는 것이다.모델, 다른 Views 및 Forms를 저장한 코드에서 이벤트를 트리거할 수 있습니다.이렇게 하면 WebSocket이나 HTTP 긴 윤문으로 클라이언트에게 실시간으로 알릴 수 있다. 예를 들어 채팅 정보나 Admin으로 다른 사용자가 업데이트한 내용을 편집하는 것을 실시간으로 볼 수 있다.
    채널 유형
    통로는 주로 두 가지 용도가 있다.첫 번째는 소비자에게 업무를 분배하는 것이다. 통로에 새로운 소식이 추가되어, 모든 업무 진행 과정이 소비자를 수신하고 운행할 수 있다는 것이다.
    두 번째 채널은 응답(HTTP, WebSocket) 요청에 사용됩니다.주의해야 할 것은 인터페이스 서버만이 이런 통로의 소식을 감청할 수 있다는 것이다.모든 응답 채널의 이름이 다르고 특정한 인터페이스 서버로 돌아가야 합니다.
    두 가지 차이는 크지 않다. 모두 채널의 핵심 정의에 부합되지만, 서버 집단이 규모를 확대할 때 문제가 있을 수 있다.첫 번째 일반 채널에 대해 우리는 채널 서버 집단과 작업 프로세스에서 무차별적으로 부하 균형을 잡을 수 있으며, 모든 작업 프로세스가 이 메시지를 통용적으로 처리할 수 있다.그러나 응답 메시지는 채널 서버 그룹에서 응답을 감청하고 있는 채널 서버에만 전송될 수 있습니다.
    따라서 Channels는 이를 두 가지 다른 채널 유형으로 간주하고 응답 채널 이름에 느낌표!를 포함하여 표시한다. 예를 들어http.response!f5g3fe21f.일반 채널 이름에는 느낌표가 없습니다.또한 채널 이름은 문자a-z A-Z 0-9 - _만 사용할 수 있으며 길이는 200자 미만입니다.
    백엔드 구현에 있어서 이 차이를 반드시 처리해야 하는 것은 아니다. 서버 집단의 규모를 확대해야만 이 두 가지 채널을 각각 처리할 필요가 있다.규모화에 대한 더 많은 정보와 백엔드나 인터페이스 서버를 작성할 때 채널 유형을 어떻게 처리하는지 참고하십시오: 규모화.
    조를 나누다
    채널은 한 감청자에게만 메시지를 보낼 수 있고 방송할 수 없다.한 무리의 클라이언트에게 메시지를 보내려면 모든 클라이언트가 대응하는 응답 채널을 기록해야 합니다.
    만약 내가 Liveblog이 있다면 새로운 댓글을 올릴 때 업데이트를 전송하려면 프로그램 처리post_save 신호를 등록하고 업데이트를 보낼 채널을 기억해야 한다(이 예는 Redis를 사용한다).
    redis_conn = redis.Redis("localhost", 6379)
    
    @receiver(post_save, sender=BlogUpdate)
    def send_update(sender, instance, **kwargs):
        #      reply channels,    
        for reply_channel in redis_conn.smembers("readers"):
            Channel(reply_channel).send({
                "text": json.dumps({
                    "id": instance.id,
                    "content": instance.content
                })
            })
    
    #    websocket.connect
    def ws_connect(message):
        #    readers  
        redis_conn.sadd("readers", message.reply_channel.name)
    

    이 코드는 실행할 수 있지만, 클라이언트가 연결을 끊을 때,readers에서 삭제하는 것을 기억해야 한다.이를 위해 우리는 또 다른 소비자 프로그램, 감청과 처리websocket.disconnect 메시지를 추가해야 한다.그리고 인터페이스 서버는 강퇴, 단전 등 상황에 부딪혀 disconnect 신호를 보내지 못할 수도 있습니다. 당신의 코드는 disconnect 알림을 영원히 받지 못할 수도 있습니다. 그러나 이 때 응답 채널은 완전히 효력을 상실했습니다. 이를 위해 우리는 기한이 지난 메커니즘을 추가해야 합니다. 응답 채널에 보내는 메시지는 일정 시간이 지나면 기한이 지나고 삭제됩니다.
    채널의 설계 기초가 무상태이기 때문에 인터페이스 서버가 연결을 끊으면 채널 서버도 하나의 채널을 닫는 것이 아니다. 채널의 임무는 소비자가 나타날 때까지 메시지를 저장하는 것이다. (어떤 유형의 인터페이스 서버, 예를 들어 SMS 인터페이스는 이론적으로 모든 인터페이스 서버에서 온 모든 클라이언트에게 서비스를 제공할 수 있다).
    우리는 연결을 끊은 클라이언트가 메시지를 받지 않았는지, 스스로 끊었는지 별로 개의치 않는다. 그러나 이러한 끊긴 클라이언트를 계속 유지하면 채널 백엔드가 혼란스러워지고, 채널 이름이 중복되고, 잘못된 메시지를 보낼 수 있기 때문에 우리는 매우 개의치 않는다.
    현재, 우리는 앞의 예로 돌아가서, 기한이 지난 집합을 추가하고, 기한이 지난 시간을 추적해야 하지만, 프레임워크를 사용하는 의미는 이러한 중복된 작업을 대신 실현하는 것이다.Channels는 이러한 추상화를 그룹이라는 핵심 개념으로 구현했습니다.
    @receiver(post_save, sender=BlogUpdate)
    def send_update(sender, instance, **kwargs):
        Group("liveblog").send({
            "text": json.dumps({
                "id": instance.id,
                "content": instance.content
            })
        })
    
    #    websocket.connect
    def ws_connect(message):
        #    readers 
        Group("liveblog").add(message.reply_channel)
        # Accept the connection request
        message.reply_channel.send({"accept": True})
    
    #    websocket.disconnect
    def ws_disconnect(message):
        # Remove from reader group on clean disconnect
        Group("liveblog").discard(message.reply_channel)
    

    그룹은 자신의send () 방법 (백엔드에서 효율적인 실현을 제공할 수 있음) 이 있을 뿐만 아니라, 그룹 구성원의 만료도 자동으로 관리합니다. 채널 메시지를 읽지 않고 만료가 시작되면, 이 채널을 포함하는 모든 그룹을 찾아서 삭제합니다.물론 disconnect 메시지를 정상적으로 받을 수 있다면 연결을 끊을 때 채널을 그룹에서 삭제해야 합니다. 만료 메커니즘은 disconnect 메시지를 정상적으로 받지 못하는 상황을 해결하기 위해서입니다.
    그룹은 보통 응답 채널 (느낌표 ! 를 포함하는 채널에만 사용되지만, 원한다면 일반 채널에도 사용할 수 있습니다.
    다음 단계
    이것은 통로와 그룹에 대한 고급 개술로 초보적인 개념을 형성하는 데 도움을 준다.Django는 일부 채널을 제공하지만, 모든 채널은 네트워크가 투명하다는 것을 기억하십시오.
    또한 채널은 메시지 발송의 성공을 보장하지 않습니다.만약 임무를 완성할 수 있도록 하려면, 전문적으로 설계된 재시도와 지구화 기능이 있는 시스템 (예: 셀러리) 을 사용하십시오.너도 관리 명령을 만들 수 있다. 만약 임무가 완성되지 않으면 채널에 다시 메시지를 제출할 수 있다. (다시 말하면 논리를 유지하고 다시 시도하는 것이다.)
    우리는 다른 문서에서 채널을 사용하기에 적합한 임무를 더 많이 소개할 것이다. 이제 시작해서 코드를 쓸 것이다.

    좋은 웹페이지 즐겨찾기