この非常に単純な作業コード サンプルから始めます。
from channels.generic.websocket import JsonWebsocketConsumer
class IsacomptaManagementFeesConsumer(JsonWebsocketConsumer):
pass
JavaScript からこの websocket コンシューマーに接続すると、これは期待どおりに機能します。接続は適切に発行され、次のログが取得されます。
WebSocket HANDSHAKING /manager/accounting/isacompta/2020/03/management-fees.ws [192.168.96.1:38108]
WebSocket CONNECT /manager/accounting/isacompta/2020/03/management-fees.ws [192.168.96.1:38108]
WebSocket DISCONNECT /manager/accounting/isacompta/2020/03/management-fees.ws [192.168.96.1:38108]
ここで、グループを使用するために、コードを次のコードに変更すると:
from channels.generic.websocket import JsonWebsocketConsumer
class IsacomptaManagementFeesConsumer(JsonWebsocketConsumer):
groups = ['foobar']
次に、接続が失敗します。JavaScript コンソールに次のように表示されます。
Firefox can’t establish a connection to the server at ws://antoine.cocoonr.hq:3001/manager/accounting/isacompta/2020/03/management-fees.ws.
error { target: WebSocket, isTrusted: true, srcElement: WebSocket, currentTarget: WebSocket, eventPhase: 2, bubbles: false, cancelable: false, defaultPrevented: false, composed: false, timeStamp: 7804, … }
サーバーログは次のようになります。
WebSocket HANDSHAKING /manager/accounting/isacompta/2020/03/management-fees.ws [192.168.96.1:38128]
WebSocket DISCONNECT /manager/accounting/isacompta/2020/03/management-fees.ws [192.168.96.1:38128]
ただし、サーバー側では例外は発生しません。
グループを使用せずに同様の動作を取得することもできます。もう少し大きな作業コード サンプルを見てみましょう。
from channels.generic.websocket import JsonWebsocketConsumer
class IsacomptaManagementFeesConsumer(JsonWebsocketConsumer):
def connect(self):
print("one")
self.accept()
print("two")
self.send_json({'text': "Foobar"})
print("three")
このコードは正常に動作し、接続は適切に発行され、サーバー ログは次のとおりです。
WebSocket HANDSHAKING /manager/accounting/isacompta/2020/03/management-fees.ws [192.168.96.1:38168]
one
WebSocket CONNECT /manager/accounting/isacompta/2020/03/management-fees.ws [192.168.96.1:38168]
two
three
WebSocket DISCONNECT /manager/accounting/isacompta/2020/03/management-fees.ws [192.168.96.1:38168]
しかし、コードに次の小さな変更を加えると:
from asgiref.sync import async_to_sync
from channels.generic.websocket import JsonWebsocketConsumer
class IsacomptaManagementFeesConsumer(JsonWebsocketConsumer):
def connect(self):
print("one")
self.accept()
print("two")
async_to_sync(self.channel_layer.send)(self.channel_name, {
'type': 'foobar.send',
'text': "Foobar",
})
print("three")
def foobar_send(self, event):
print("AAA")
self.send_json({'text': event['text'])
print("BBB")
次に、接続は適切に発行されますが、すぐに閉じられ、「three」を出力する機会がないままプロセスが停止し、関数「foobar_send」も実行されません。
WebSocket HANDSHAKING /manager/accounting/isacompta/2020/03/management-fees.ws [192.168.96.1:38224]
one
WebSocket CONNECT /manager/accounting/isacompta/2020/03/management-fees.ws [192.168.96.1:38224]
two
WebSocket DISCONNECT /manager/accounting/isacompta/2020/03/management-fees.ws [192.168.96.1:38224]
例外も発生しないのに、「three」が出力されない理由がわかりません。これは、?self.channel_layers.send()
の呼び出し元によって例外が発生し、サイレントにキャッチされることを意味します。IsacomptaManagementFeesConsumer.connect()
最後の段落を書いた後、試してみることにしました。
import traceback
from asgiref.sync import async_to_sync
from channels.generic.websocket import JsonWebsocketConsumer
class IsacomptaManagementFeesConsumer(JsonWebsocketConsumer):
def connect(self):
print("one")
self.accept()
print("two")
try:
async_to_sync(self.channel_layer.send)(self.channel_name, {
'type': 'foobar.send',
'text': "Foobar",
})
except Exception as e:
print(e)
traceback.print_stack()
print("three")
def foobar_send(self, event):
print("AAA")
self.send_json({'text': event['text'])
print("BBB")
それだけです。隠れた「ファイルが見つかりません」というエラーがあります。
WebSocket HANDSHAKING /manager/accounting/isacompta/2020/03/management-fees.ws [192.168.96.1:42562]
one
WebSocket CONNECT /manager/accounting/isacompta/2020/03/management-fees.ws [192.168.96.1:42562]
two
[Errno 2] No such file or directory
File "/usr/lib64/python3.6/threading.py", line 884, in _bootstrap
self._bootstrap_inner()
File "/usr/lib64/python3.6/threading.py", line 916, in _bootstrap_inner
self.run()
File "/usr/lib64/python3.6/threading.py", line 864, in run
self._target(*self._args, **self._kwargs)
File "/usr/lib64/python3.6/concurrent/futures/thread.py", line 69, in _worker
work_item.run()
File "/usr/lib64/python3.6/concurrent/futures/thread.py", line 56, in run
result = self.fn(*self.args, **self.kwargs)
File "/home/tony/.venvs/cocoonr/lib/python3.6/site-packages/channels/db.py", line 14, in thread_handler
return super().thread_handler(loop, *args, **kwargs)
File "/home/tony/.venvs/cocoonr/lib/python3.6/site-packages/asgiref/sync.py", line 277, in thread_handler
return func(*args, **kwargs)
File "/home/tony/.venvs/cocoonr/lib/python3.6/site-packages/channels/consumer.py", line 105, in dispatch
handler(message)
File "/home/tony/.venvs/cocoonr/lib/python3.6/site-packages/channels/generic/websocket.py", line 39, in websocket_connect
self.connect()
File "/home/tony/Workspace/cocoonr/billing/consumers.py", line 32, in connect
traceback.print_stack()
three
WebSocket DISCONNECT /manager/accounting/isacompta/2020/03/management-fees.ws [192.168.96.1:42562]
Channels 2.4.0 および channels-redis 2.4.2 で Django 3.0 を使用しています。ここに私の CHANNEL_LAYERS 設定があります:
CHANNEL_LAYERS = {
'default': {
'BACKEND': 'channels_redis.core.RedisChannelLayer',
'CONFIG': {
'hosts': [
{
'address': get_env('CHANNELS_REDIS_HOST',
default='localhost:6379'),
'password': get_env('CHANNELS_REDIS_PASSWORD',
default=None),
'db': 1,
},
],
},
},
}