現在仕事でWEBサービスを作るときはDjango一択な環境にいます。今度Websocketが必要になったのですがDjangoでWebsocketどうするんだってことで調べたところChannelsというライブラリを使うらしいです。Djangoのグループが作っているので安心感があります。Channelsは日本語でもいくつか記事がありましたが1.x時代のものでそのままでは動かなかったりしたのでチュートリアルを中心に試してみました。
ということで何回かにわけて(たぶん3回?)Django Channelsのチュートリアルをベースにちょっとだけ手を加えた内容をまとめていきます。また最終的には本番環境で動かせるようにnginx経由でwebsocketができるところまで持っていきます。
環境
以下の構成でやっていきます。
- Python 3.6
- Django 2.0.3
- Django Channels 2.0.2
また、どのような環境でも動作するようにDockerでの環境構築をしています。とりあえず動かしたいという場合はdenzow/channel-sampleのmasterブランチdocker-compose up
で動かせると思います。
セットアップ
まず、必要なライブラリを導入しDjangoのプロジェクトを作る必要があります。
$ pip install django channels channels_redis $ django-admin startproject mysite .
さらに、チュートリアルの例ではchat
というアプリをstartapp
で作成し、それを編集する流れになっています。ここまでを終わらせた状態のものをDockerの環境こみでstart
ブランチとして公開しましたので以下よりDLして展開し、docker-compose up
すれば整います。
denzownoMacBook-Pro:~ denzow$ docker-compose up
実行すると、以下のように3つのコンテナが起動します。
denzownoMacBook-Pro:~ denzow$ docker ps CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES 92ad1eb59de1 channelsample_service_nginx "/start-nginx.sh" About a minute ago Up About a minute 80/tcp, 443/tcp, 0.0.0.0:8000->8000/tcp channelsample_service_nginx_1 11dd8ba126a0 channelsample_service "/app/docker/servi..." About a minute ago Up About a minute 0.0.0.0:3000->3000/tcp channelsample_service_1 77a55e623f6a channelsample_redis "docker-entrypoint..." About a minute ago Up About a minute 0.0.0.0:6379->6379/tcp channelsample_redis_1
channelsample_service
がDjangoのコンテナで、channelsample_service_nginx
がリバースプロキシ用のnginx、channelsample_redis
はあとで必要になるredisサーバです。
では、channelsample_service
に入って作業をしていきます。まずはとりあえず管理用のスーパーユーザを作っておきます。
denzownoMacBook-Pro:~ denzow$ docker exec -it channelsample_service_1 bash root@11dd8ba126a0:/app# ls root@11dd8ba126a0:/app# python manage.py createsuperuser
これで、Djang-adminが使えるはずです。ブラウザでhttp://localhost:3000/admin/
にアクセスし、作成したユーザ・パスワードでログインできるか確認しておきます。
問題なくログインできたら次に進みます。
chatアプリケーションについて
現時点ではhttp://localhost:3000/chat/
にアクセスするとチャットルーム名の入力を促すページが表示されます。
What chat room would you like to enter?
ただし、この時点ではルーム名を入力して進むと404になります。これはchat/urls.py
に以下のように空のルーティングの設定しかないためです。
# chat/urls.py from django.urls import path from . import views urlpatterns = [ path('', views.index, name='index'), ]
このファイルはsettings.py
のROOT_URLCONF
に指定されているmysite.urls
で以下の様にinclude
されています。
from django.contrib import admin from django.urls import path, include urlpatterns = [ path('admin/', admin.site.urls), path('chat/', include('chat.urls')) ]
そのため、現在はchat/
+ ''
のURLしかアクセスできない状態になっていますので、この動作は現時点では想定通りです。
Channelsの有効化
ではChannelsを有効化していきます。変更内容のcommitはこちらです。まずmysite/routing.py
として以下のファイルを作成します。
from channels.routing import ProtocolTypeRouter application = ProtocolTypeRouter({ # (http->django views is added by default) })
ProtocolTypeRouter
はDjangoのURLConfと同じようにリクエストを受け取った際にどのようなコードを実行するかをChannelsに伝えるものです。
では続いてsettings.py
を編集します。
INSTALLED_APPS = [ 'channels', # <--- 追加 'chat', 'django.contrib.admin', 'django.contrib.auth', 'django.contrib.contenttypes', 'django.contrib.sessions', 'django.contrib.messages', 'django.contrib.staticfiles', ] # : # 最下部 # : # ASGIの起点を指定 # sys.path.append(APP_ROOT_PATH)してるからmysiteは省略できる ASGI_APPLICATION = 'routing.application'
channels
をINSTALLED_APPS
に追加し、ASGI_APPLICATION
に先程作成したrouting.py
のapplication
を指定します。これでChannelsが有効化されます。
runserverについて
channelsample_service
コンテナは現時点でpython manage.py runserver 0.0.0.0:3000
を実行しています。本番環境等ではuwsgi
等のアプリケーションサーバを使用しますが、Channelsが扱うASGIというインターフェースはuwsgi
で扱うことができないのです。一方で、channelsを導入した環境ではrunserverがASGI対応に自動的に置き換えられます。実際、起動時の出力を見てみると以下のようにASGI対応であることが表示されています。
service_1 | System check identified no issues (0 silenced). service_1 | March 24, 2018 - 13:55:26 service_1 | Django version 2.0.3, using settings 'mysite.settings' service_1 | Starting ASGI/Channels development server at http://0.0.0.0:3000/ <-- ASGI service_1 | Quit the server with CONTROL-C.
通常時のDjangoのrunserverの出力は以下のようにASGIの文字はありません。
service_1 | System check identified no issues (0 silenced). service_1 | March 25, 2018 - 14:31:33 service_1 | Django version 2.0.3, using settings 'mysite.settings' service_1 | Starting development server at http://0.0.0.0:3000/ service_1 | Quit the server with CONTROL-C.
channels
をINSTALLED_APPS
に追加し、ASGI_APPLICATION
を設定した時点でrunserver
がASGI開発ように置き換わっていることがわかります。なお、ASGI_APPLICATION
を定義せずにchannels
を追加するとrunserverがエラーで落ちるので注意します。
一旦まとめ
環境準備からChannelsの有効化までを簡単にまとめました。次回は実際にWebsocketでチャットルームを実装していくところまで進めていく予定です。