파이썬을 사용하여 socket.io-redis에 이벤트를 emit

소개



AJAX+ 폴링으로 구현하고 있던 채팅 서버 등을, 뒤따라 websocket을 사용해 리얼타임 통신으로 하려고 하면, 그림 1과 같은 구성이 될 수도 있다고 생각합니다.

python-websocket-1

첫째, 터미널 A와 터미널 B는 각각 거친 c 집 t. 이오 라이브러리를 사용하여 websocket 연결을 설정합니다.
1. AJAX로 메시지 POST
2. 메시지 내용을 DB에 저장
3. 터미널 B에 새 메시지가 생성되었음을 알리기 위해 socket.io-php-emitter 라이브러리를 사용하여 이벤트를 emit
4. node.js가 3인 이벤트를 socket.io-redis 라이브러리를 사용하여 구독
5. node.js는 단말 B, C, D...에 대한 새로운 메시지의 내용과 함께 화면을 업데이트하기 위한 이벤트를 emit한다

그림 1의 구성의 시스템이 이미 있는 상태에서 websocket으로 연결되어 있는 단말 A, B, C...에 Python으로 이벤트를 emit하고 싶은 경우의 방법을 소개합니다. 기본적으로 Python에서 그림 1의 3 단계에서 수행 한 것과 동일한 작업을 수행하기 만하면됩니다. 구성으로서는 그림 2와 같이 됩니다.

python-websocket-2

  • socket.io-redis 형식에 따라 이벤트를 emit(publish)
  • 1 이벤트 구독
  • 1 이벤트에 따른 처리를 수행하기위한 이벤트를 클라이언트에 emit
    클라이언트는 이벤트를 수신하고 처리합니다

  • Python에도 PHP처럼 socket.io-php-emitter 같은 라이브러리가 있다면 일부러 블로그에서 소개할 정도는 아니지만 Python에는 socket.io-emitter를 실현하는 라이브러리가 없습니다.
    socket.io-redis의 Protocol 에 대해 언급하고 있는 항목에 파이썬 용 socket.io-redis 가 소개되고 있습니다만, 네임스페이스를 지정한다 Of 와 룸명을 지정한다 In 가 기능하고 있지 않기 때문에 사용할 수 없습니다.
    socket.io-redis는 Redis의 Publish 명령을 발행하는 것에 지나지 않으므로 직접 publish 명령을 작성합니다.

    채널 이름



    채널 이름은 socket.io-redis에 따라 다음과 같이 지정됩니다.

    특정의 namespace 전체에 이벤트를 emit 하고 싶은 경우의 채널명은 이하와 같다
    prefix + '#' + namespace + '#'

    특정 namespace의 room에 이벤트를 emit하고 싶은 경우의 채널명은 이하와 같다
    prefix + '#' + namespace + '#' + room + '#'

    socket.io의 경우 prefix의 기본값은 socket.io입니다.
    namespace를 /로 설정하고 room 이름을 room_777로 설정하면 다음과 같이 channel_name을 준비합니다.
    prefix = 'socket.io'
    namespace_name = '/'
    room_name = 'room_777'
    
    channel_name = '{0}#{1}#{2}#'.format(prefix, namespace_name, room_name)
    

    emit하는 데이터 형식



    socket.io의 emit로 전송되는 데이터는 MessagePack이라는 형식으로 직렬화됩니다.
    socket.io로 전송할 때의 데이터 형식은 다음과 같습니다.
    data = [
        'emitter',
        {
            # socket.ioはv1からバイナリ形式のデータ(画像・動画)も送信できるようになりました。
            # 通常のテキストデータの送信と区別するためにtypeキーが用意されています。
            # テキストデータの場合は2をセットし、バイナリデータの場合はは5をセットします。
            'type': 2,
            'data': [
                # emitしたいイベント名をセットします
                'my_event_name',
                # 送信したいデータを辞書形式でセットします
                {
                    'id': 'message_id',
                    'created_at': '2017-12-19 14:00:00',
                    'body': 'message_body'
                }
            ],
            # namespaceをセットします
            'nsp': namespace_name
        },
        {
            # room_nameをセットします
            'rooms': [room_name],
            'flags': []
        }
    ]
    

    위의 데이터를 msgpack을 사용하여 직렬화합니다.
    import msgpack
    
    data_packed = msgpack.packb(data)
    
    print(data_packed)
    b'\x93\xa7emitter\x83\xa4type\x02\xa4data\x92\xadmy_event_name\x83\xa2id\xaamessage_id\xaacreated_at\xb32017-12-19 14:00:00\xa4body\xacmessage_body\xa3nsp\xa1/\x82\xa5rooms\x91\xa8room_777\xa5flags\x90'
    

    Publish



    node.js 측에 미리 설정해 둔 이벤트명으로 publish 하면 socket.io 의 이벤트가 emit 됩니다.

    파이썬의 Redis 클라이언트는 여기을 추천합니다.
    import redis
    
    r = redis.StrictRedis()
    r.publish(channel_name, data_packed)
    

    redis에서 다음과 같이 명령이 발행되면 정상적으로 작동합니다.
    redis-cli monitor | grep PUBLISH
    
    1513649216.0000000 [0 127.0.0.1:50283] "PUBLISH" "socket.io#/#room_777#" "\x93\xa7emitter\x83\xa4type\x02\xa4data\x92\xadmy_event_name\x83\xa2id\xaamessage_id\xaacreated_at\xb32017-12-19 14:00:00\xa4body\xacmessage_body\xa3nsp\xa1/\x82\xa5rooms\x91\xa8room_777\xa5flags\x90"
    

    좋은 웹페이지 즐겨찾기