Django Channels 2.0 channel_layers не общается

Итак, я переносил свой сервер, который использовал Django Channels 1.x -> 2.x+.

Первоначальный дизайн отправлял задачу сельдерею, используя getAFTreeTask.delay(message.reply_channel.name), и, имея доступ к channel_name, он, возможно, мог отвечать асинхронно.

from celery import task
from channels import Channel

@task
def getAFTreeTask(channel_name):
    tree = Request().cache_af_tree()
    Channel(channel_name).send({
        "text": json.dumps({
            "channel": "AF_INIT",
            "payload": tree
        })
    })

Теперь я перевел свой сервер на Channels 2.x+ по разным причинам. Согласно документам

class Consumer(JsonWebsocketConsumer):

     def connect(self):
        print("Client Connected: ", self.channel_name)
        self.accept()

    def receive_json(self, content, **kwargs):
        print(content)
        parse_request(self.channel_name, content)

    def disconnect(self, content):
        print(content)

    def chat_message(self, event):
        print("Entered reply channel")
        print(event)

Потребитель, установленный таким образом, должен получать запрос через уровень канала, если я использую правильное имя_канала, теперь потребитель работает как веб-сокет отправки-получения, если ответ имеет доступ к self.send_json() или self.send() для других общих потребителей, поэтому я предполагая, что все мои настройки верны, моя проблема заключается в том, что я пытаюсь использовать уровень канала для отправки чего-то вроде этого (согласно https://channels.readthedocs.io/en/latest/topics/channel_layers.html#single-channels)

from channels.layers import get_channel_layer
from asgiref.sync import AsyncToSync

def parse_request(channel_name, content):

    print("parsed ", channel_name, content)
    channel_layer = get_channel_layer()
    AsyncToSync(channel_layer.send)(channel_name, {
        "type": "chat.message",
        "text": "Hello there!",
    })

я получил

изменить (полная трассировка стека):

    2018-02-02 18:28:35,984 ERROR    Exception inside application: There is no current event loop in thread 'Thread-3'.
  File "/usr/lib/python3.5/asyncio/tasks.py", line 241, in _step
    result = coro.throw(exc)
  File "/home/chris/Env/myapp/lib/python3.5/site-packages/channels/consumer.py", line 51, in __call__
    await await_many_dispatch([receive, self.channel_receive], self.dispatch)
  File "/home/chris/Env/myapp/lib/python3.5/site-packages/channels/utils.py", line 48, in await_many_dispatch
    await dispatch(result)
  File "/home/chris/Env/myapp/lib/python3.5/site-packages/asgiref/sync.py", line 81, in inner
    return await async_func(*args, **kwargs)
  File "/home/chris/Env/myapp/lib/python3.5/site-packages/asgiref/sync.py", line 65, in __call__
    return await asyncio.wait_for(future, timeout=None)
  File "/usr/lib/python3.5/asyncio/tasks.py", line 373, in wait_for
    return (yield from fut)
  File "/usr/lib/python3.5/asyncio/futures.py", line 361, in __iter__
    yield self  # This tells Task to wait for completion.
  File "/usr/lib/python3.5/asyncio/tasks.py", line 296, in _wakeup
    future.result()
  File "/usr/lib/python3.5/asyncio/futures.py", line 274, in result
    raise self._exception
  File "/usr/lib/python3.5/concurrent/futures/thread.py", line 55, in run
    result = self.fn(*self.args, **self.kwargs)
  File "/home/chris/Env/myapp/lib/python3.5/site-packages/asgiref/sync.py", line 74, in thread_handler
    raise e
  File "/home/chris/Env/myapp/lib/python3.5/site-packages/asgiref/sync.py", line 72, in thread_handler
    self.func(*args, **kwargs)
  File "/home/chris/Env/myapp/lib/python3.5/site-packages/channels/consumer.py", line 93, in dispatch
    handler(message)
  File "/home/chris/Env/myapp/lib/python3.5/site-packages/channels/generic/websocket.py", line 40, in websocket_receive
    self.receive(text_data=message["text"])
  File "/home/chris/Env/myapp/lib/python3.5/site-packages/channels/generic/websocket.py", line 104, in receive
    self.receive_json(self.decode_json(text_data), **kwargs)
  File "./MYAPP/API/consumers.py", line 13, in receive_json
    parse_api_request(self.channel_name, content)
  File "./MYAPP/API/api_request.py", line 16, in parse_api_request
    AsyncToSync(channel_layer.send)(channel_name, {
  File "/home/chris/Env/myapp/lib/python3.5/site-packages/asgiref/sync.py", line 17, in __init__
    self.main_event_loop = asyncio.get_event_loop()
  File "/usr/lib/python3.5/asyncio/events.py", line 632, in get_event_loop
    return get_event_loop_policy().get_event_loop()
  File "/usr/lib/python3.5/asyncio/events.py", line 578, in get_event_loop
    % threading.current_thread().name)
  There is no current event loop in thread 'Thread-3'.

и если я не использую AsyncToSync, я получаю (что, согласно документам, я не должен делать, просто проверить)

2018-02-02 18:34:27,965 WARNING  ./MYAPP/API/api_request.py:18: builtins.RuntimeWarning: coroutine 'RedisChannelLayer.send' was never awaited

что я не понимаю, поскольку я точно следовал руководству, я также пытался ответить из задачи сельдерея (отдельный поток) и не получил ту же ошибку, но ничего не происходит, журнал сельдерея просто говорит, что задача выполнена, но я не получил ответа.

Кроме того, попытался отправить ответ непосредственно через

AsyncToSync(channel_layer.send)(channel_name, {
            "type": "websocket.send",
            "text": "Hello there!",
        })

изнутри и вне потока и получил те же самые нерезультаты....

Кто-нибудь смог отправить через channel_layers вне объекта Consumers.

К вашему сведению, мои настройки.py

INSTALLED_APPS = [
    'django.contrib.admin',
    'django.contrib.auth',
    'django.contrib.contenttypes',
    'django.contrib.sessions',
    'django.contrib.messages',
    'django.contrib.staticfiles',
    'channels',
    'myapp',
]
ASGI_APPLICATION = "myapp.routing.application"

CHANNEL_LAYERS = {
    "default": {
        "BACKEND": "channels_redis.core.RedisChannelLayer",
        "CONFIG": {
            "hosts": [("localhost", 6379)],
        },
    },
}

person Chris    schedule 02.02.2018    source источник
comment
не могли бы вы опубликовать полную трассировку?   -  person jpic    schedule 03.02.2018
comment
@jpic Я добавил полную трассировку стека   -  person Chris    schedule 03.02.2018


Ответы (1)


После ответа от Андреса Годвина:

Оказывается, это была ошибка в asgiref ‹ 2.1.3, путем обновления возвращаемые значения из SyncToAsync/AsyncToSync были исправлены!

Итак, моя рабочая реализация, для всех, кто заинтересован:

consumers.py

from channels.consumer import AsyncConsumer

class My_Consumer(AsyncConsumer):

async def websocket_connect(self, event):
    print("Connected")
    print(event)
    print(self.channel_name)
    await self.send({
        "type": "websocket.accept",
    })

async def websocket_receive(self, event):
    print("Received")
    print(event)
    parse_api_request(self.channel_name, json.loads(event['text']))

async def celery_message(self, event):
    print("Service Received")
    print(event)
    await self.send({
        "type": "websocket.send",
        "text": event["text"],
    })


task.py 

from channels.layers import get_channel_layer
from asgiref.sync import AsyncToSync


def async_send(channel_name, text):
    channel_layer = get_channel_layer()
    AsyncToSync(channel_layer.send)(
            channel_name,
            {"type": "celery.message",
             "text": json.dumps(text)
             })


def getAFTree(channel_name, message):
    getAFTreeTask.delay(channel_name, message)



@task
def getAFTreeTask(channel_name, message):
    tree = Request().cache_af_tree()
    async_send(channel_name, {
                "channel": "AF_INIT",
                "payload": tree
             })
person Chris    schedule 05.02.2018