vue.jsのアプリのデプロイで躓きやすいポイント、celery, redisの扱い方の話

今日やるやるべきこと

今日はvue.jsで作ったアプリをディプロイするときに`vue＋express`で発生するエラーの解決、celery, redisの扱い方について

#【Vue.js+Express】historyモードでURLを直接指定したとき404の対策
Vue-Routerでただhistoryモードするだけだと、URL直接指定、もしくはブラウザのリロードで遷移できなくなる。

僕の場合だとこんなエラーが出てきた。

![スクリーンショット 2021-02-23 9.50.53.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/965206/4695a71f-2aa9-2b6a-5dc1-bcc66b0e5cf6.png)


これはSPAの仕組みに起因する。

この解決方法としてはサーバ側に設定を追加するだけでOK。まあ、ドキュメントを見れば解説はついているのだが、、、

[参考URL：HTML5 History モード](https://router.vuejs.org/ja/guide/essentials/history-mode.html)

herokuディプロイをするためには`server.js`ファイルを追加し、expressを使ってアプリを起動する方法がある。
これをベースにhistoryモード対応していこうと思う。

###connect-history-api-fallback

公式では

「connect-history-api-fallbackを使うといいと思うよ！」

と言っているので素直に従おうと思うww

ということでこれをインストール

```:ターミナル
npm install --save connect-history-api-fallback
```

そしてserver.jsに、connect-history-api-fallbackを使う宣言。

変更後のコードはこれです。

```js:server.js

var history = require('connect-history-api-fallback');
const express = require('express')
const serveStatic = require('serve-static')
const path = require('path')
const app = express()
app.use(history());
app.use('/', serveStatic(path.join(__dirname, '/dist')))
const port = process.env.PORT || 8080
app.listen(port)

```
主に追加したのは

```js
var history = require('connect-history-api-fallback');
app.use(history());
```

の2行で、後は順番を整えただけ！

コピペで使えると思う。

------------

#backgroundのtask

ex)facebookでログインしたときに自動でポストしたものを取得して表示するなど

###TaskQueue

>タスクキューとは？
>>一時的にタスクメッセージを蓄えておく場所です。 タスクキューに保存されたタスクメッセージは、タスク処理サービスによって、条件に合うものから順に取得されて処理されます

![taskqueue.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/965206/531384d0-b64e-d443-f1db-10853d48123a.png)

###Redis

>redisとは？
>>Redis = REmote DIctionary Server の略
Redis は、Key-Value型 の NoSQL データベース
Pythonは辞書型があるがredisは辞書型のデータベースみたいなもの。


MacにRedisをインストールしよう

`brew install redis`

早速redisを使ってみよう！！

###Redisに接続する
redis-cliコマンドで接続する

```:terminal
$ redis-cli
127.0.0.1:6379>
```

###データの登録と取得と削除

```:terminal
$ redis-cli
127.0.0.1:6379> set Key Value
OK
127.0.0.1:6379> get Key
"Value"
127.0.0.1:6379> del Key
(integer) 1
127.0.0.1:6379> get Key
(nil)
127.0.0.1:6379> Keys *
(empty list or set)
```

--------------

#celery

【Python】ローカル環境でCeleryを動かしてみる
ジョブキューフレームワークのCeleryの使い方です。とりあえず、ローカル環境で動かしてみるところまでやってみます。

大まかな内容は[ドキュメント](https://docs.celeryproject.org/en/latest/getting-started/first-steps-with-celery.html)に沿っているのでよければ確認してみてください。

##celeryをインストールする

`pip install -U Celery`

##redisをインストールする

Celeryはメッセージの送受信のために、メッセージブローカーという別のサービスが必要になります。

ブローカーにはRabbitMQやRedisがあります。今回使うのはRedis。

`pip install redis`

次にrequirementファイルを変更する

```:requirements.txt
celery==3.5.3
redis==5.0.5
```

##djangoでceleryを使うための準備

djangoでceleryを使うためには

1. celeryファイルを追加し、タスクを書く
2. `__init__.py`にコマンドを打つ
3. redisをバックグラウンドで設定する
4. Celery Worker Serverを起動する
5. Taskを呼び出す


これは[公式ドキュメント](https://docs.celeryproject.org/en/stable/django/first-steps-with-django.html#django-first-steps)を参考にしている

####1. celeryファイルを追加し、タスクを書く

```python:main(設定ディレクトリ)/celery.py
import os

from celery import Celery

# set the default Django settings module for the 'celery' program.
os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'main.settings')

app = Celery('main')

# Using a string here means the worker doesn't have to serialize
# the configuration object to child processes.
# - namespace='CELERY' means all celery-related configuration keys
#   should have a `CELERY_` prefix.
app.config_from_object('django.conf:settings', namespace='CELERY')

# Load task modules from all registered Django app configs.
app.autodiscover_tasks()


@app.task(bind=True)
def debug_task(self):
    print(f'Request: {self.request!r}')

```

####2. `__init__.py`にコマンドを打つ

```python:main/__init__py
# This will make sure the app is always imported when
# Django starts so that shared_task will use this app.
from .celery import app as celery_app

__all__ = ('celery_app',)
```

####3. ブロッカーを設定する

[公式ドキュメント](https://docs.celeryproject.org/en/stable/userguide/configuration.html#broker-settings)はこちら

```python:settings.py
CELERY_BROKER_URL='redis://localhost'
```
これを追加する

準備は整ったので次のステップに進もう

####4. Celery Worker Serverを起動する
タスクを実行するワーカーを起動します。

```:terminal
celery -A main worker -l INFO
```

本番環境で、デーモンシステムとしてワーカーを使うためには、各プラットフォームが提供しているツールを使うことになります。 

####5. Taskを呼び出す

```python:main(設定ディレクトリ)/celery.py

@app.task(bind=True)
def hello(self):
    print('hello')
```


```:terminal
>>> from main.celery import hello
>>> hello.delay()
```

hello関数がタスクのワーカーに入ってceleryがそれを実行する


複数のタスクを処理したい場合はtasks.pyを作る

####tasks.py

accountのfirst_nameを表示する処理をバックグラウンドで行う

```python:tasks.py
from celery import shared_task
from core.models import Account 

@shared_task
def print_account_name(acccount_id):
    account = Account.objects.filter(id=account_id).first()

    print(account.user.first_name)
```

実際にdjangoの`serializers.py`にコードを書き込んでみよう

まずは処理内容を`tasks.py`に書いていく

```python:tasks.py
@shared_task
def get_fb_post(account_id):
    pass
```

次に`serializers`に`tasks.py`を実行するためのコードを書く

```python:serializers.py
                #userのfb_id
                user = User(username=str(uuid.uuid4()))
                user.first_name = profile['first_name']
                user.last_name = profile['last_name']
                user.email = profile.get('email', '')
                user.save()
                account = Account()
                account.user = user
                account.fb_id = profile['id']
                account.fb_token = fb_token
                account.save()
     #get_fb_post関数を実行する
                get_fb_post.delay(account.id)
                return account
```

--------------

#今日のタスク：
tasks.pyのなかにfacebookの投稿を取得する処理をかく