またしてもショートケーキを作った話

趣味で宇宙開発を行う団体「[リーマンサット・プロジェクト](https://www.rymansat.com)」がお送りする[新春アドベントカレンダー](https://qiita.com/rsp-dgkz/items/497225739b88c817a90c)です。

私は[RSP-01という超小型人工衛星](https://www.rymansat.com/satellites)のチャット機能を担当しております。
アドカレ初日の記事ですが、宇宙や人工衛星の記事は他の方にお任せするとして、本記事はここに言語されている通り、またショートケーキを作ったという話です。
[https://jflute.hatenadiary.jp/entry/20180223/mastercurrent](https://jflute.hatenadiary.jp/entry/20180223/mastercurrent)

今回のショートケーキは、Azureにて無料でプチWEBアプリを構築です。無料にこだわります。
繰り返しますが、宇宙や人工衛星の話は皆無です。

# 作ったショートケーキ
[https://matanonawo.azurewebsites.net](https://matanonawo.azurewebsites.net)
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/526509/f57fb651-695f-ffc8-9b0c-d55aa8d33e44.png)


# ご要件
二つ名、欲しいですよね。「見積もりの狂犬」とか。
元々リーマンサット・プロジェクト（以降rsp）のモエさんが二つ名が欲しいと言い出して、私も欲しいと思ったのと、rspのアドカレに良いネタと思い作成しました。

二つ名は誰かに名付けてもらおうとすると、相手が言いづらかったり、ネガ要素が入ったりして、中々自分が気に入る二つ名を付けてもらえない懸念があります。
これをTwitterのお人柄から自動生成することで解決します。

※ちなみに最初はFacebookの投稿から生成していたのですが、自分（開発者）以外の投稿取得はビジネス認証的なものが必要で諦めました。会社の書類を提出するとか、個人の趣味アプリでは無理です。急遽Twitterからの投稿取得に切り替えるという、仕事だったら真っ青的なことをしました。

# レシピ
### 基本的な流れ
1. Twitterログイン（OAuth）
2. Twitter投稿の取得
3. 二つ名生成と表示

### 材料
・Nuxt.js ＋ Express
　　サーバ1台のお手軽WEBアプリを構築する場合、CORSも意識せずデプロイも楽なのでNuxtは良い選択肢です。
・Azure App Service
　　ホスティング先です。F1プラン（無料プラン）にこだわります。
・kuromoji
　　形態素解析で使用。
　　Twitterの投稿をわかち書きし、品詞を元に二つ名で使用する単語をチョイスします。
・TwitterのAPI（認証、投稿の取得）
　　OAuthとtwitterのnpmライブラリを使用して楽しました。

# いちご
#### 二つ名に有能な品詞
名詞の「サ変接続」です。
さらに二つ名の表示パターンを増やすために「一般」と「地名」も含めました。
「一般」は二つ名に使うとしっくりこない単語もあったのですが、少しずれた単語も出たほうが面白かろうということで「一般」も含めています。
この品詞を先頭にして、末尾に「の狂犬」等を付与する形で二つ名を生成します。
末尾は数十個のパターンを用意してランダムに選択する方式にしています。

#### Azureに乗せると遅い
実際にApp Serviceに乗せて実行した際に判明しましたが、１回の二つ名の生成に５秒程度かかりました。
kuromojiのトークナイザーの作成で、サーバ時間のほぼすべてを消費していました。
ローカル実行時は瞬殺だったのですが、辞書ファイルの読み込みが遅いのか劇遅。
トークナイザーは使いまわしができるので、サーバ起動時に生成する方法もあるかと思いますが、今回は初回アクセス時にのみ生成する方式にしました。
結果、サーバ側の実行時間は数十ミリ秒になりました。
載せるまでもないかもですが、こんな感じです。

```javascript
let tokenizer = null

module.exports = {
  // 二つ名生成リクエストのハンドラ
  async make(req, res, next) {
    // 初回だけトークナイザーを生成
    // 同時アクセスにより重複して生成されるのは許容
    if (tokenizer == null) {
      tokenizer = await createTokenizer()  // ここが遅い
    }
  ...
```


#### F1プランの App Serviceはアクセスがないとアイドル状態になる
５分程度ですかね。アクセスがないとアイドル状態になります。
HTTPアクセスによりアイドル状態から復帰しますが、復帰には数十秒かかるためよろしくないです。
そこで、Functionsで5分おきにHTTPアクセスを行うことで、常にアクティブな状態を保つことを行っています。

注意としては、Functionsを従量課金プランにすることです。FunctionsはApp Serviceプランというものもあり、これのF1（無料）にしたくなりますが、これにすると（タイマーで実行を行っていても）Functionsは数分でアイドルになります。そうすると今回の目的が達成できなくなります。
5分に1回起動するFunctionsであれば、従量課金の無料枠内に収まるはずです。

Functionsは以下です。[こちらを](https://qiita.com/ma2shita/items/a9844a3a8e618600a109)ありがたく参考・ほぼコピペで利用させていただいております。

``` JavaScript
const http = require('https');
module.exports = function (context, req) {
    const url = 'https://matanonawo.azurewebsites.net/health.html';
    const options = {
        method: 'GET',
        headers: {
            'Content-Type': 'application/json',
        }
    };

    const client = http.request(url, options, (res) => {
        context.log(res.statusCode);
        let resBody = '';
        res.on('data', (chunk) => { resBody += chunk; });
        res.on('end', () => {
            context.log(resBody);
            context.res = res.statusCode;
            context.done();
        });
    });
    client.on('error', (e) => {
        context.log.error(e);
        context.res = {status: 500};
        context.done();
    });
    client.end();
};
```

#### Linux の App Service
当初App Serviceにデプロイしても起動せず。ログを見るとlocalhostで起動しようとしていました。
WindowsのApp Serviceだと発生したことがない事象です。
対処としては、App Serviceの「構成」にて「HOST = 0.0.0.0」を設定することで正常起動できました。
ただし、この設定はAzureオフィシャルのドキュメントからは発見できず、正しいやり方か不明です。
※Linux利用だとDockerを指定しなかった場合でも（Dockerのログとかでているので）その辺でしょうか。


#### Basic認証
アプリ公開前の動作確認においては、他者からのアクセスを制限するため、一応の認証をかけることになります。
Basic認証は、お手軽に認証をかけるにはありがたい仕組みです。
Nuxtでは「nuxt-basic-auth-module」というモジュールを利用するのが楽です。

#### LinuxのF1プラン数の制限
・F1プランで作成できるLinuxのApp Serviceサービスは1個だけです。
　WindowsのApp Serviceはボコボコ作れます。
　[ドキュメント](https://azure.microsoft.com/ja-jp/pricing/details/app-service/linux/)を見てもそんなこと書いていないので、作ろうとしてビックリするやつです。

　
# おわりに
今回もショートケーキを作りました。もう何をチャーハンと呼ぶかすら分からなくなってきています。

あと、二つ名の生成は、最初は表示される言葉に一人で笑っていましたが、開発していると見飽きます。

明日は @hokke_mirin の「[LTのススメ](https://qiita.com/hokke_mirin/items/d6020e10078e3640c95d)」です。

---
リーマンサット・プロジェクトは「普通の人が集まって宇宙開発しよう」を合言葉に活動をしている民間団体です。
他では経験できない「宇宙開発プロジェクト」に誰もが携わることができます。
興味を持たれた方は [https://www.rymansat.com/join](https://www.rymansat.com/join) からお気軽にどうぞ。