趣味で宇宙開発を行う団体「リーマンサット・プロジェクト」がお送りする新春アドベントカレンダーです。
私はRSP-01という超小型人工衛星のチャット機能を担当しております。
アドカレ初日の記事ですが、宇宙や人工衛星の記事は他の方にお任せするとして、本記事はここに言語されている通り、またショートケーキを作ったという話です。
https://jflute.hatenadiary.jp/entry/20180223/mastercurrent
今回のショートケーキは、Azureにて無料でプチWEBアプリを構築です。無料にこだわります。
繰り返しますが、宇宙や人工衛星の話は皆無です。
作ったショートケーキ
https://matanonawo.azurewebsites.net
ご要件
二つ名、欲しいですよね。「見積もりの狂犬」とか。
元々リーマンサット・プロジェクト(以降rsp)のモエさんが二つ名が欲しいと言い出して、私も欲しいと思ったのと、rspのアドカレに良いネタと思い作成しました。
二つ名は誰かに名付けてもらおうとすると、相手が言いづらかったり、ネガ要素が入ったりして、中々自分が気に入る二つ名を付けてもらえない懸念があります。
これをTwitterのお人柄から自動生成することで解決します。
※ちなみに最初はFacebookの投稿から生成していたのですが、自分(開発者)以外の投稿取得はビジネス認証的なものが必要で諦めました。会社の書類を提出するとか、個人の趣味アプリでは無理です。急遽Twitterからの投稿取得に切り替えるという、仕事だったら真っ青的なことをしました。
レシピ
基本的な流れ
- Twitterログイン(OAuth)
- Twitter投稿の取得
- 二つ名生成と表示
材料
・Nuxt.js + Express
サーバ1台のお手軽WEBアプリを構築する場合、CORSも意識せずデプロイも楽なのでNuxtは良い選択肢です。
・Azure App Service
ホスティング先です。F1プラン(無料プラン)にこだわります。
・kuromoji
形態素解析で使用。
Twitterの投稿をわかち書きし、品詞を元に二つ名で使用する単語をチョイスします。
・TwitterのAPI(認証、投稿の取得)
OAuthとtwitterのnpmライブラリを使用して楽しました。
いちご
二つ名に有能な品詞
名詞の「サ変接続」です。
さらに二つ名の表示パターンを増やすために「一般」と「地名」も含めました。
「一般」は二つ名に使うとしっくりこない単語もあったのですが、少しずれた単語も出たほうが面白かろうということで「一般」も含めています。
この品詞を先頭にして、末尾に「の狂犬」等を付与する形で二つ名を生成します。
末尾は数十個のパターンを用意してランダムに選択する方式にしています。
Azureに乗せると遅い
実際にApp Serviceに乗せて実行した際に判明しましたが、1回の二つ名の生成に5秒程度かかりました。
kuromojiのトークナイザーの作成で、サーバ時間のほぼすべてを消費していました。
ローカル実行時は瞬殺だったのですが、辞書ファイルの読み込みが遅いのか劇遅。
トークナイザーは使いまわしができるので、サーバ起動時に生成する方法もあるかと思いますが、今回は初回アクセス時にのみ生成する方式にしました。
結果、サーバ側の実行時間は数十ミリ秒になりました。
載せるまでもないかもですが、こんな感じです。
let tokenizer = null
module.exports = {
// 二つ名生成リクエストのハンドラ
async make(req, res, next) {
// 初回だけトークナイザーを生成
// 同時アクセスにより重複して生成されるのは許容
if (tokenizer == null) {
tokenizer = await createTokenizer() // ここが遅い
}
...
F1プランの App Serviceはアクセスがないとアイドル状態になる
5分程度ですかね。アクセスがないとアイドル状態になります。
HTTPアクセスによりアイドル状態から復帰しますが、復帰には数十秒かかるためよろしくないです。
そこで、Functionsで5分おきにHTTPアクセスを行うことで、常にアクティブな状態を保つことを行っています。
注意としては、Functionsを従量課金プランにすることです。FunctionsはApp Serviceプランというものもあり、これのF1(無料)にしたくなりますが、これにすると(タイマーで実行を行っていても)Functionsは数分でアイドルになります。そうすると今回の目的が達成できなくなります。
5分に1回起動するFunctionsであれば、従量課金の無料枠内に収まるはずです。
Functionsは以下です。こちらをありがたく参考・ほぼコピペで利用させていただいております。
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はボコボコ作れます。
ドキュメントを見てもそんなこと書いていないので、作ろうとしてビックリするやつです。
おわりに
今回もショートケーキを作りました。もう何をチャーハンと呼ぶかすら分からなくなってきています。
あと、二つ名の生成は、最初は表示される言葉に一人で笑っていましたが、開発していると見飽きます。
明日は @hokke_mirin の「LTのススメ」です。
リーマンサット・プロジェクトは「普通の人が集まって宇宙開発しよう」を合言葉に活動をしている民間団体です。
他では経験できない「宇宙開発プロジェクト」に誰もが携わることができます。
興味を持たれた方は https://www.rymansat.com/join からお気軽にどうぞ。