- 投稿日:2020-04-05T23:40:46+09:00
Node.js Express で出てくる req, res, next
はじめに
Node.jsのExpressの雛形を生成するコマンド
express project-nameで出力されるコードの一部ですが
index.jsvar express = require('express'); var router = express.Router(); router.get('/', function(req, res, next) { res.render('index', { title: 'Express', user: req.user }); });この
req
,res
,next
は何なのか書いていこうと思います。reqについて
req
はapp.get
の第一引数で指定されたパスに入ってきたHTTPリクエストを表すオブジェクトです。var express = require('express'); var app = express(); app.get('/', func); function func(req, res) { console.log(req.ip); console.log(req.method); console.log(req.path); console.log(req.protocol); console.log(req.query.name) res.end(); }
req
はapp.get
の第一引数で指定されたパスに入ってきたHTTPリクエストを表すオブジェクトです。
サーバを起動し(Portは8000)、curl -X GET localhost:8000/hoge\?name=user1::ffff:127.0.0.1 GET /hoge http user1とリクエストを送って出力を見てみましょう。ちなみにapp.
get
はGETメソッドでリクエストを受け付けます。最後のres.end()
はHTTPレスポンスのプロセスを終了する関数です。これがないと処理が正常に完了しません。
このように来たHTTPリクエストに関する様々な情報を取得することができます。reqのプロパティやメソッドは他にもまだまだありますので詳しくは、
https://expressjs.com/ja/4x/api.html#req
を見てください。resについて
res
は指定されたパスに入ってきたリクエストに対するHTTPレスポンスを構成するためのオブジェクトです。var express = require('express'); var app = express(); app.get('/', func); function func(req, res) { if(req.query.name){ res.status(200).send('Hello! your name :' + req.query.name); }else{ res.status(400).send('Who are you?'); } }サーバを立てて、
curl -X GET localhost:8000/hoge\?name=user1 > Hello! your name : user1 curl -X GET localhost:8000/hoge > Who Are you?とするとHTTPレスポンスが帰ってきます。
status()
でステータスコードを設定でき、send()
でレスポンスボディを設定できます。
send()
にはHTTPレスポンスプロセスの終了処理が含まれるので、end()
は不要です。
resのプロパティやメソッドは、https://expressjs.com/ja/4x/api.html#res
を参考にしてください。nextについて
通常は上記の、
app.get('/hoge', func);
のように、あるpath(/hoge
)に対する、手続き(func
)を呼んで、その中で、HTTPレスポンスを返して(res.end()
)終了します。次のコードを見てください。
app.get('/hoge', preProcess1, preProcess2, mainProcess); function preProcess1(req, res, next) { console.log('1個目の前処理したよ!'); } function preProcess2(req, res, next) { console.log('2個目の前処理したよ!'); } function mainProcess(req, res) { res.send('メインの処理をしたよ!'); }実はこのように
/hoge
に対するアクションを何個も登録することができます。
まずはこれにリクエストを送ってみましょう。curl -X GET localhost:8000/hoge ## server 側 1個目の処理をしたよ!このようにpreProcess1の1個のみが実行されて、他は実行されませんでした。
preProcess1->preProcess2->mainProcessと処理を継続させたい時に使用するのがnextです。app.get('/hoge', preProcess1, preProcess2, mainProcess); function preProcess1(req, res, next) { console.log('1個目の前処理したよ!'); next(); } function preProcess2(req, res, next) { console.log('2個目の前処理したよ!'); next(); } function mainProcess(req, res) { res.send('メインの処理をしたよ!'); }この場合、
curl -X GET localhost:8000/hoge メインの処理をしたよ! ## server 側 1個目の処理をしたよ! 2個目の処理をしたよ!と出力され、無事にHTTPレスポンスを返して処理を終えることができました。
next()関数によって、次の処理へ制御を渡すことができます。
https://expressjs.com/ja/guide/writing-middleware.html
ここによると、この場合の関数preProcess1やpreProcess2はmainProcessを行うための中間的な役割を行うものとして「ミドルウェア」と命名されています。
もしかしたらmainProcessも定義的にはミドルウェアかもしれないけど個人的にちょっと違和感があります。。。参考文献
https://expressjs.com/ja/4x/api.html#req
https://expressjs.com/ja/4x/api.html#res
https://expressjs.com/ja/guide/writing-middleware.html
- 投稿日:2020-04-05T19:18:36+09:00
【node.js】validationを新しいバージョンに対応コードに変更する
解決したい問題
パッケージvalidationのバージョンが古いので新しいバージョンの書き方に変更したい。
環境
OS: macOS
express: ^4.17.1
ejs: ^2.6.2
express-validator: ^6.4.0変更前のコード(古いコード)
router.post('/', (req, res, next) => { var request = req; var response = res; req.check('name', 'NAMEは必ず入力して下さい。').notEmpty(); req.check('password', 'PASSWORDは必ず入力して下さい。').notEmpty(); req.getValidationResult().then((result) => { if (!result.isEmpty()) { var content = '<ul class="error">'; var result_arr = result.array(); for(var n in result_arr) { content += '<li>' + reault_arr[n].msg + '</li>' } content += '</ul>'; var data = { title: 'Login', content: content, form: req.body } response.render('login', data); } else { var nm = req.body.name; var pw = req.body.password; User.query({where: {name: nm}, andWhre: {password: pw}}) .fetch() .then((model) => { if (model == null) { var data = { title: '再入力', content: '<p class="error">名前またはパスワードが違います</p>', form: req.body }; respose.render('login', data); } else { request.session.login = model.attributes; var data = { title: 'Login', content: '<p>ログインしました!<br>トップページに戻ってメッセージを送信下さい。</p>', form: req.body } respose.render('login', data); } }); } }) });このreq.checkがエラーになる。
変更後
上に↓のコードを書き足す。
const { check, validationResult } = require('express-validator');router.post('/', [ check('name', 'NAMEは必ず入力して下さい。').notEmpty(), check('password', 'PASSWORDは必ず入力して下さい。').notEmpty() ], (req, res, next) => { var request = req; var response = res; const errors = validationResult(req); if (!errors.isEmpty()) { var content = '<ul class="error">'; var result_arr = errors.array(); for(var n in result_arr) { content += '<li>' + result_arr[n].msg + '</li>' } content += '</ul>'; var data = { title: 'Login', content: content, form: req.body } response.render('login', data); } else { var nm = req.body.name; var pw = req.body.password; User.query({where: {name: nm}, andWhere: {password: pw}}) .fetch() .then((model) => { if (model == null) { var data = { title: '再入力', content: '<p class="error">名前またはパスワードが違います</p>', form: req.body }; response.render('login', data); } else { request.session.login = model.attributes; var data = { title: 'Login', content: '<p>ログインしました!<br>トップページに戻ってメッセージを送信下さい。</p>', form: req.body } response.render('login', data); } }).catch((error) => { var data = { title: '再入力', content: '<p class="error">名前またはパスワードが違います。</p>', form: req.body }; res.render('login', data); console.log(error); }); } });大きく変わったのはcheckの位置です。
参考
↓この本の学習中に、バージョンが古くてエラーになった。
https://www.amazon.co.jp/Node-js%E8%B6%85%E5%85%A5%E9%96%80-%E7%AC%AC2%E7%89%88-%E6%8E%8C%E7%94%B0-%E6%B4%A5%E8%80%B6%E4%B9%83/dp/4798055220↓最新バージョンの参考
https://express-validator.github.io/docs/
- 投稿日:2020-04-05T16:14:39+09:00
brew install nodeで色々詰まった自分用メモ
前提
https://qiita.com/okohs/items/ced3c3de30af1035242d
この記事を読んでbrew install nodeとしたがエラーに出会った。
そこで、
http://www.gworks.jp/2014/05/homebrew-node/
にしたがってbrew doctorのところまで行き、問題が生じたので一つ一つ解決したあと、
brew install nodeをしたらまたエラーが出た。
今度はbrew linkできないよ〜というエラーだった。
そこで、エラーメッセージに素直にしたがって解決しようとした。
僕が選んだ選択肢はbrew link --overwrite nodeこうしたら、とあるファイルに権限がないからエラーになった。
というわけで、
https://wtnvenga.hatenablog.com/entry/2017/11/15/125430
を参考にしてエラーを1つ1つ解決することになった。その時のコマンドとエラーと解決方法の組み合わせが以下になる。
出会ったコマンドとエラーの組み合わせが以下だったら読んでも良いかも
コマンド1
brew install nodeエラー1
Could not symlink share/systemtap/tapset/node.stp 以下略コマンド2
brew link --overwrite nodeエラー2
Error:Could not symlink 〇〇 △△ is not writable.解決方法
エラー1:
brew link --overwrite nodeエラー2:
cd (△△より手前) sudo chown -R $USER △△ brew link nodeもしまた同様のエラーが出たら上記手順を繰り返す。
感想
わからなくて人に聞けないなら、エラーの奴隷にまずはなることが一番だと思った。
ここら辺は半年近く経っても変わらないスタンスだと思う。
Quoraのどこかの質問に対する回答にも、ベテランエンジニアは初めて触るFWやライブラリに慣れるのが速いのは、エラー解決速度が異常に速いだからという話があった。上の方法がベストかどうかは知らない。
とはいえ、やりながらミスをして向き合って一つ一つ解決しながら実装するのが上達のために必要な条件だとつくづく思った。
(一番良いのは作りたいプロダクトがある時だ思うけど。JS知らなくても3日くらいやってりゃ初級レベルだったら実装できるようになるし、中級知識と言われるものがすぐに必要になってくるから。しかも必要だったらレベルが如何の斯うのと御構い無しになる。)参考文献
https://wtnvenga.hatenablog.com/entry/2017/11/15/125430
http://www.gworks.jp/2014/05/homebrew-node/
https://qiita.com/okohs/items/ced3c3de30af1035242d
- 投稿日:2020-04-05T15:18:57+09:00
obnizとブザーで兄弟配管工ゲームのコイン音を出してみた
作ろうと思ったきっかけ
- ProtoOutoStudio 3回目授業でobnizに触れる
- ブザーが面白かった
兄弟配管工ゲームでは、ブロックを叩くとコインが出てきますよね。その時の効果音を再現してみようと思いました。
”あの”効果音の音階を調べてみた
が、楽譜が読めないぞ....。オクターブもよく分からん....。
よし、トライ&エラーか???(音痴です)まずはベースとなる”ド”を鳴らす
”ド”の音が何Hzか調べました。今回はこちらのサイトを参考に周波数を設定していきます。
【鳴らしてみた様子】
obnizにブザーをつけてみた。まずはドの音を実装!#obniz pic.twitter.com/4pDRjS9z9N
— まえぷー@出窓菜園 BWG (@kmaepu) April 5, 2020音を鳴らすだけなら、obnizのJavascriptパーツライブラリから試すと爆速で確認できます。
obniz IDを入力し、Test Runをクリックすると、指定した音が鳴ります。デフォルトは1000Hzになっているので、鳴らしたい音に合わせるといいかなと思います。
今回はobnizのボタンを押すと、指定の音が鳴るようなプログラムをNode.jsで動かしています。ソースコードはこちら。
const Obniz = require('obniz'); var obniz = new Obniz("OBNIZ-ID"); // OBNIZ-IDに自分のIDを入れます obniz.onconnect = async function () { // スピーカーを呼び出す var speaker = obniz.wired("Speaker", {signal:0, gnd:1}); // ディスプレイ処理 obniz.display.clear(); // 一旦クリアする obniz.display.print("Hello obniz!"); // Hello obniz!という文字を出す // スイッチの反応を常時監視 obniz.switch.onchange = function(state) { if (state === "push") { // 押されたとき console.log("pushed"); // ディスプレイ処理 obniz.display.clear(); // 一旦クリアする obniz.display.print("pushed"); // pushed という文字を出す // 音を鳴らす speaker.play(523.25); // ドの音 } else if (state === "none") { // none で押してないとき obniz.display.clear(); // 一旦クリアする // スピーカーで音を鳴らさない stop speaker.stop(); } } }上述のプログラムを使う際は、obniz.jsが必要となります。次のコードでインストールしておきましょう。
npm i obnizあの効果音に使われている音を鳴らす
あの効果音は”シ”と”ミ”の2音で構成されているようです。といっても”ミ”は1つ上のオクターブになっているようです。
では、音階表を参考にしてみると....。
”シ”は987.76Hz、”ミ”は1318.51Hzでつくります。
音を鳴らしてみるとこのようになりました。
ちょっと発展。#obniz pic.twitter.com/SDfbhotxgO
— まえぷー@出窓菜園 BWG (@kmaepu) April 5, 2020よさそうですね!あとは自動化してタイミング調整だな....
組み合わせる
音を順番に出力するのは簡単ですが、タイミングがわからない!!!!!!!!
楽譜を見ると次のようになっています。こちらのサイトを参考にしてみると、200bpsのテンポであると読み解きました。200bpsとは、1分間に200拍の速度です。つまり、
1分間(60秒) ÷ 200 = 0.3秒
となります。楽譜をみると、どうやら”シ”と”ミ”の間は何も記号がないので1拍だとして、”ミ”の後は2拍ありそうなので0.6秒として作りました。
結果....。
あの効果音に少し近づいたか?
— まえぷー@出窓菜園 BWG (@kmaepu) April 5, 2020
オクターブを1つ上げれば良さそう(^^)#obniz #protoout pic.twitter.com/TiLEEtjqCd
思っていたより音が低そうなので、1オクターブあげてみましょう。1オクターブあげてみた。
— まえぷー@出窓菜園 BWG (@kmaepu) April 5, 2020
こっちの方が良さそう。でも、なにか物足りなさが…。#obniz #protoout pic.twitter.com/3abNA7yBQHこちらの方が近そうですね!でも何か足りない気がしてくる....
何度も同じ音を聞いていると分からなくなってしまうなぁ。でもこれで良しとしますか。余談
圧電スピーカーで効果音作るの楽しそう....沼に片足いれはじめてしまうぅ!
最終的なソースコード
const Obniz = require('obniz'); var obniz = new Obniz("Obniz_ID"); // Obniz_IDに自分のIDを入れます obniz.onconnect = async function () { // スピーカーを呼び出す var speaker = obniz.wired("Speaker", {signal:0, gnd:1}); // ディスプレイ処理 obniz.display.clear(); // 一旦クリアする obniz.display.print("Hello obniz!"); // Hello obniz!という文字を出す // スイッチの反応を常時監視 obniz.switch.onchange = async function(state) { if (state === "push") { // 押されたとき console.log("pushed"); // ディスプレイ処理 obniz.display.clear(); // 一旦クリアする obniz.display.print("pushed"); // pushed という文字を出す // 音を鳴らす speaker.play(1975.53); // シ await obniz.wait(300); // Wait speaker.play(2637.02); // ミ await obniz.wait(600); // Wait speaker.stop(); } else if (state === "none") { // none で押してないとき obniz.display.clear(); // 一旦クリアする // スピーカーで音を鳴らさない stop speaker.stop(); } else if (state === "left") { speaker.play(1318.51); // ミ } } }
- 投稿日:2020-04-05T15:07:37+09:00
AWS LambdaのCustom Runtimeを使い、Node.js v8などEoLとなったランタイムを動かす
はじめに
Node.js、バージョンアップの足がかなり早いですよね。
AWS Lambdaにおけるランタイムサポート期間も、これにあわせてハイテンポになっています。ちゃんとバージョンアップをしろというご意見は重々承知の上ではありますが、
Node.js v8.10でLambda Functionを使い続けざるを得ない場合に、カスタムランタイムを使ってEoLとなったランタイムを動かし延命処置を図ります。動作確認環境
- Arch Linux (2020.04.04)
- Docker 19.03.8-ce
- aws-cli 1.18.36
カスタムランタイムの使い方
カスタムランタイムの仕様については、公式ドキュメントが詳しいので割愛します。
カスタムランタイムを使用するには、デプロイパッケージあるいはLayerに、
node
実行ファイルと、ハンドラー関数を起動するためのbootstrap
実行ファイルを含める必要があります。
今回は既存のLambda Functionを使用することを想定していますので、関数のデプロイパッケージには手を加えずLambda Layerでランタイムを読み込ませます。Lambda Layerの作成には、以下のリポジトリを活用させていただきます。
https://github.com/lambci/node-custom-lambdaこちらのリポジトリにはNode v10、v12のファイルが含まれています。(2020/04/04現在)
今回はこのリポジトリをフォークし、v8.10用のファイルを追加することでLayerを作成します。
bootstrap
については、CとJavascriptで書かれたものがそれぞれv12.x/bootstrap.c
、v12.x/bootstrap.js
にあります。(v10.xも同様)
bootstrap.c
(をコンパイルしたbootstrap
)がまずAWS Lambdaによって起動され、これがbootstrap.js
スクリプトをカスタムランタイムのNodeで実行します。
bootstrap.js
は、AWS Lambda ランタイムインターフェイスから関数の呼び出しイベントの受け取り、デプロイパッケージのスクリプト実行、実行結果のPOSTを行います。上記リポジトリの
bootstrap.js
はNode v8.10でも問題なく動くので、
必要な変更点はLambda Layerに含めるnode
実行ファイルをv8.10のものに変更するだけとなります。カスタムランタイムの作成
前節で紹介したリポジトリをクローンするところからはじめます。
Dockerが必要となります。$ git clone https://github.com/lambci/node-custom-lambda.git $ cd node-custom-lambdav12.xのディレクトリを元に、v8.10のディレクトリを作成します。
$ cp -r v12.x v8.10 $ cd v8.10v12.xのLayerファイルを削除しておきます。
$ rm layer.zipこのプロジェクトでは、Docker上で
bootstrap.c
のビルドとNodeのダウンロードを行います。
config.sh
を編集し、Nodeのバージョンを指定します。config.sh< export NODE_VERSION=12.16.1 --- > export NODE_VERSION=8.10.0ビルドします。
$ ./build.sh
v8.10/layer.zip
ファイルが出来上がります。
これを解凍すると以下のようなファイルが入っています。layer ├── bin │ └── node ├── bootstrap └── bootstrap.jsNodeのバージョンを確認しておきます。
$ ./layer/bin/node -v v8.10.0テストが用意されていますので、実行してみます。
$ ./test.sh
以上でカスタムランタイムのLayerが完成しました。
カスタムランタイムのデプロイ
リポジトリには
publish.sh
が用意されていますが、このスクリプトは全てのリージョンにデプロイされてしまいます。
今回はap-northeast-1にのみデプロイできればよいので、AWS CLIを使って手動でLayerを作成します。まず、作成したレイヤーのファイル(
layer.zip
)を任意のS3にアップロードします。aws s3api put-object --bucket ${BUCKET_NAME} --key nodejs/8.10.0/layer.zip --body layer.zip --output json次に、Lambda Layerを作成します。
ここではCloudFormationで作成します。template.ymlAWSTemplateFormatVersion: 2010-09-09 Parameters: S3BucketName: Description: A S3 bucket name contains layer.zip Type: String Resources: Nodejs8Runtime: Type: AWS::Lambda::LayerVersion Properties: Content: S3Bucket: !Ref S3BucketName S3Key: nodejs/8.10.0/layer.zip Description: Layer for Node.js 8.10.0 Custom Runtime LayerName: custom-runtime-nodejs-8 Outputs: Nodejs8RuntimeLayerARN: Description: A lambda layer ARN of Node.js 8.10.0 Custom Runtime Value: !Ref Nodejs8Runtime Export: Name: !Sub ${AWS::StackName}-runtime-nodejs8Stackを作成。
aws cloudformation create-stack --stack-name dev-lambdalayers-nodejs \ --template-body file://template.yml \ --parameter ParameterKey=S3BucketName,ParameterValue=${S3_BUCKET_NAME}Layerができていることを確認します。
$ aws lambda list-layer-versions --layer-name custom-runtime-nodejs-8 { "LayerVersions": [ { "LayerVersionArn": "arn:aws:lambda:ap-northeast-1:123456789012:layer:custom-runtime-nodejs-8:1", "Version": 1, "Description": "Layer for Node.js 8.10.0 Custom Runtime", "CreatedDate": "2020-04-04T16:59:31.629+0000" } ] }Lambda Functionの作成とテスト
上記で作成したカスタムランタイムをテストします。
Lambda FunctionはServerless Frameworkを使用して作ることにします。
provider.runtime
にprovided
を指定することでカスタムランタイムを使用できます。
Lambda Layerは、先程のCloudformation StackのOutputをインポートしてARNを指定します。serverless.ymlservice: test-lambda-function provider: name: aws runtime: provided stage: dev region: ap-northeast-1 functions: hello: handler: handler.hello layers: - 'Fn::ImportValue': dev-lambdalayers-nodejs-runtime-nodejs8関数のコードはシンプルに、実行しているNode.jsのバージョンを返すだけです。
handler.jsmodule.exports.hello = async event => { return process.version; };これを実行して、
v8.10.0
という文字列が帰ってきたら成功です。$ sls invoke -f hello "v8.10.0"注意点
- AWS公式のNode.jsランタイムには
aws-sdk
が含まれていますが、この方法で作成したカスタムランタイムにはいずれのnpmパッケージも含まれていません。おわりに
以上でNode v8を使用するLambda Functionの延命措置ができました。
同様の方法で、Node v6、v4も動かすことが可能です。しっかりバージョンアップしていくのがベストであることは言うまでもありませんが、
node-gypなどネイティブモジュールはバージョンアップで動かなくなることも多々ありますので、とりあえずの措置には使えるかと思います。また今回使用したコードはすべて以下リポジトリにアップしています。
https://github.com/uhey22e/node-custom-lambda
- 投稿日:2020-04-05T13:05:48+09:00
(小ネタ)Node.jsのWebアプリでclusterを使いながら定期的に子プロセスを再起動させる
Node.jsでサーバサイドWebアプリを開発中、なぜかメモリリークがあるライブラリに遭遇してしまったので、ワークアラウンドとして、定期的にプロセスを再起動させて、メモリリークの問題を緩和したいと思いました。
- コード: https://github.com/knjname/2020-04-05_restartClusterChild
- 参考: https://nodejs.org/api/cluster.html
サーバサイド
http://0.0.0.0:10080 をリスンするプロセスが4つ立ち上がり、5秒未満で子プロセスを停止させ、その後に再起動します。(実用上は、もっと長い時間でプロセスを殺すべきです。)
src/index.jsconst cluster = require("cluster"); const http = require("http"); const sleep = time => new Promise(done => setTimeout(done, time)); const clusterCount = 4; const portNumber = 10080; if (cluster.isMaster) { const spawnProcess = () => { // プロセスを終了させるまでの時間: 0 〜 5000 msec const ttl = ~~(5000 * Math.random()); const child = cluster.fork(); let timeout; child.on("listening", () => { // 指定時間で終了(Graceful kill)させる console.log(`誕生! 死まで ${ttl} msec.`); timeout = setTimeout(() => { console.log(`死: ${child.id}`); child.kill(); }, ttl); }); child.on("disconnect", () => { // 別の理由で死んだ場合はkillをキャンセル if (timeout) { clearTimeout(timeout); } }); child.on("exit", () => { // 子プロセスが終了したら代わりのものを1つ起動する spawnProcess(); }); }; // 子プロセスを複数起動する for (let i = 0; i < clusterCount; i++) { spawnProcess(); } } if (cluster.isWorker) { // Express や Koa など好きに使いましょう http .createServer(async (req, res) => { // リクエスト終了までやや時間がかかる設定 await sleep(1000); res.writeHead(200); res.end("Request done\n"); }) .listen(portNumber); }起動すると、下記のようなログを吐きながら、プロセスの終了と生成を延々と繰り返します。
誕生! 死まで 2712 msec. 誕生! 死まで 3984 msec. 誕生! 死まで 4297 msec. 誕生! 死まで 1547 msec. 死: 4 誕生! 死まで 4276 msec. 死: 2 : :テスト
ab
コマンドできちんとリクエストが中断されずにいるか、テストしてみます、$ ab -c 20 -n 500 http://localhost:10080/ This is ApacheBench, Version 2.3 <$Revision: 1826891 $> Copyright 1996 Adam Twiss, Zeus Technology Ltd, http://www.zeustech.net/ Licensed to The Apache Software Foundation, http://www.apache.org/ Benchmarking localhost (be patient) Completed 100 requests Completed 200 requests Completed 300 requests Completed 400 requests Completed 500 requests Finished 500 requests Server Software: Server Hostname: localhost Server Port: 10080 Document Path: / Document Length: 13 bytes Concurrency Level: 20 Time taken for tests: 26.226 seconds Complete requests: 500 Failed requests: 0 Total transferred: 44000 bytes HTML transferred: 6500 bytes Requests per second: 19.07 [#/sec] (mean) Time per request: 1049.029 [ms] (mean) Time per request: 52.451 [ms] (mean, across all concurrent requests) Transfer rate: 1.64 [Kbytes/sec] received Connection Times (ms) min mean[+/-sd] median max Connect: 0 1 1.0 0 7 Processing: 1000 1008 5.4 1007 1025 Waiting: 1000 1007 4.7 1006 1023 Total: 1000 1008 5.5 1007 1025 Percentage of the requests served within a certain time (ms) 50% 1007 66% 1010 75% 1012 80% 1013 90% 1016 95% 1019 98% 1020 99% 1022 100% 1025 (longest request)特に何も問題なくリクエスト処理は完了しているようです。
Complete requests: 500 Failed requests: 0まとめ
- cluster でマルチプロセス化できるし、定期的にプロセスを再起動して、健全性を保つことができるはず
- こういうゴミ掃除はマルチスレッドモデルだとできなさそう
- 投稿日:2020-04-05T12:46:14+09:00
DOM, Node, Elementについて
備忘録です。
間違いなどございましたらご指摘ください。DOMとは
HTMLとXMLドキュメントへのAPI
(わかりやすくいうと、プログラムからHTMLやXMLを自由に操作するための仕組み、インターフェイス)JavaScriptからHTMLに要素を追加したり、ボタンクリック時のイベントを登録したり、スタイルや属性を追加したり、要素のサイズや位置を追加したり、こういったものはすべてDOMのAPIを使うことで操作できる。
DOMツリー
DOMはHTMLドキュメントを『オブジェクトのツリー』として扱っている。これを『DOMツリー』という。Nodeとは
ノードとは各要素(HTMLではエレメントやタグという)自体のことを表す。(つまり、上の図のひとつひとつのボックスがNode)
特定のノードを基準としたときに、その上にあるノードを「親:parent」ノードと表現し、その下にあるノードを「子:childまたはchildren」ノードと表現する。
例えば「そのタグの子ノード全体を取得して、その親ノードから削除する」のような言い方ができる。firstChildやparentNodeなどのプロパティ、appendChild、removeChildなどのメソッドはNodeが提供している機能。
Nodeの種類
- Document(Document型Node・ドキュメントノード)
- Element(Element型Node・エレメントノード)
- Attr(Attribute型Node・属性ノード)
などElementとは
さきほどNodeにはいくつか種類があると説明した。
ElementはそのNodeの中のひとつ、Element型のNodeのこと。HTMLの要素はElementを継承しておりElement<-HTMLElement<-HTMLDivElementのようになっている。
classListやinnerHTMLなどのプロパティ、getElementByIdやquerySelectorAll、setAttributeなどのメソッドはElementが提供している機能。
まとめ
DOM
- HTMLドキュメントとJavaScriptをつなぐインターフェース
- DOMツリーはNode構成されているNode
- DOMツリーを意識して操作を行うときのオブジェクト
- 例: appendChild、removeChild、parentNodeなどElement
- HTMLドキュメントの要素を意識して操作を行うときのオブジェクト
- 例: style、attribute、width、heightなど参照
https://kuroeveryday.blogspot.com/2018/11/difference-between-dom-and-node-and-element.html
https://eng-entrance.com/what-is-dom
- 投稿日:2020-04-05T03:29:36+09:00
常に動くLINEBOTにお引っ越し(now編)(未解決版)
前回つまって諦めたnowに再挑戦した記録。
ngrokで作った時の記事:
WikipediaのAPIを使ってLINEbotに調べてもらうはじめに
前回同様、課題感とテーマとしては「ngrokだと使いたいときに使えない!だから常時使えるようにしたい!」です。
目次としては、
- now でだいぶハマる
- さらに now にハマる
- 対応したこと
です。
nowでだいぶハマる
nowを正しく動かすところでまずハマりました。
いくつかハマった気がして、だいたいはいじったりnow自体を消してやり直したりしたらできた印象だが、うっかりでハマったのがこの記事参照。
https://qiita.com/shima-07/items/64c051c9982ac0899b21さらにnowにハマる
無事、ngrokを卒業して、nowを使ってLINE botができました。
が、、
なんか挙動がおかしい。ソースコードはngrokの時と何も変えてないのに。
botなのになんだか頭が悪い人間っぽさが出てきた・・・。具体的には、
1. 初めて検索するキーワードにはwikipediaからの返り値を返してくれない。 (「初めてなので・・・」とかウブさはbotに求めてない)
2. 前回の回答を返してくる。(質問をちゃんと聞いて)
今回は「カラス?」と聞いているのに、「くま」について説明し始めた、、
3. 自動応答したいreplayMessageとWikipedia APIのpushMessageの順番がバラバラ(落ち着いてほしい…)
本当は「カラスの説明:」が上にきて、その下にwikipediaからの返答をのせたいのだけど、ずれる時がある。
nowにしたことで、何やら処理の順番とかが変わるようだ。。
対応したこと
https://qiita.com/n0bisuke/items/fb19ed3cd0138135ae69
この記事を見て、asyncとawaitを使い、replyとpushの場所も変えてみた
変更前↓
before.js. 省略 . . . function handleEvent(event) { if (event.type !== 'message' || event.message.type !== 'text') { return Promise.resolve(null); } let mes = '' // console.log(event.message.text); if(event.message.text.indexOf('?') > -1){ // ?を含んでいる場合にはwikiで検索したものを出して、含んでない場合はurlを返す var str = event.message.text; var result = str.split( '?' ).join( '' ); //?を取り除く処理 mes = result + 'の説明:'; //wikiのbodyの前の一言 getBody(event.source.userId,result); //wiki APIで取得できたらプッシュメッセージ }else{ var result = event.message.text; mes = result + 'のURL:'; //wikiのurlの前の一言 getUrl(event.source.userId,result); //wiki APIで取得できたらプッシュメッセージ } return client.replyMessage(event.replyToken, { type: 'text', // text: event.message.text //実際に返信の言葉を入れる箇所 text : mes }); } const getBody = async (userId,word) => { const res = await axios.get('http://wikipedia.simpleapi.net/api?keyword='+ encodeURIComponent(word) + '&output=json'); const item = res.data; // console.log(item); await client.pushMessage(userId, { type: 'text', text: item[0].body, }); } const getUrl = async (userId,word) => { const res = await axios.get('http://wikipedia.simpleapi.net/api?keyword='+ encodeURIComponent(word) + '&output=json'); const item = res.data; // console.log(item); await client.pushMessage(userId, { type: 'text', text: item[0].url, }); } (process.env.NOW_REGION) ? module.exports = app : app.listen(PORT); console.log(`Server running at ${PORT}`);変更後↓(replyMessageをasyncとawaitを使って書き換え、場所も変えてみた。)
※?がある場合の分岐の抜粋。after1.jsasync function handleEvent(event) { if (event.type !== 'message' || event.message.type !== 'text') { return Promise.resolve(null); } let mes = '' // console.log(event.message.text); if(event.message.text.indexOf('?') > -1){ // ?を含んでいる場合にはwikiで検索したものを出して、含んでない場合はurlを返す var str = event.message.text; var result = str.split( '?' ).join( '' ); //?を取り除く処理 mes = result + 'の説明:'; //wikiのbodyの前の一言 await client.replyMessage(event.replyToken, { type: 'text', // text: event.message.text //実際に返信の言葉を入れる箇所 text : mes }); getBody(event.source.userId,result,mes); //wiki APIで取得できたらプッシュメッセージ }こういう対応ではダメだった?
これにしても直らず。
上記3つのおとぼけbotさんのままでした。。次に、
いっそwikiの内容をとってくる関数の中にreplyMessage入れたらいいんじゃない?
と思い、やってみた。(完全に素人の悪あがき感。。)
getBody.jsconst getBody = async (userId,word,message) => { await client.replyMessage(event.replyToken, { type: 'text', // text: event.message.text //実際に返信の言葉を入れる箇所 text : message }); const res = await axios.get('http://wikipedia.simpleapi.net/api?keyword='+ encodeURIComponent(word) + '&output=json'); const item = res.data; console.log(item); await client.pushMessage(userId, { type: 'text', text: item[0].body, }); }これでも、相変わらずおとぼけbotは治らず。。
今回も失敗…
前回の諦めポイントのnow自体からは進んだのですが、結果botがおとぼけさんになってしまった。
(もっと人間的な振る舞いをしたら可愛げがあって多少のミスも許せるが、、まだ十分な人間味もない。)時間的にもきついので、しばらくはこれ以上深掘らず、いったんここまで。
- 投稿日:2020-04-05T01:34:08+09:00
nowでアプリケーションエラーが出たとき対応したこと
nowでハマったメモ
状況
@n0bisuke のこの記事を参考にやっていたつもりがこんなエラーと遭遇。
An error occurred with this application.
This is an error with the application itself, not the platform.「nowは問題ないよ。お前のアプリケーション(ソースコード)が問題なんだよ。」とのこと。
対応
このnow.json内で指定しているjsファイルの一部を直したら直った。
(記事の例でいくと、server.jsってファイルの文末)直した場所
自分にとってわかりやすいと思って書き換えてた最後のserver running のあたり。
エラー出てたとき
エラー出てたとき.jsif(process.env.NOW_REGION){ module.export = app; }else{ app.listen(PORT); console.log(`Server running at ${PORT}`); }これ、記事の通り下記に直したら直った。
(初めから記事の通りやっておけばよかった話)エラー出なくなった.js(process.env.NOW_REGION) ? module.exports = app : app.listen(PORT); console.log(`Server running at ${PORT}`);結論
自分でアレンジしたところが悪かった。記事の通りにやっておけばよかった。
でもなんでこの書き方だとダメだったんだろう。。。
あと、nowを起動させるときにこのjsファイルも見ているんだなあ。
- 投稿日:2020-04-05T01:17:01+09:00
typescriptを使ってjavascriptのシンタックスシュガーを理解する
背景
typescriptを勉強していて、アロー演算子構文の読み方がわからない。ドキュメントを読んでもよくわからなかった、ということがありました。
そんな時、tscコマンドを使うことで難解なシンタックスシュガーへの理解のヒントになることに気が付きました。
それについて紹介します。https://developer.mozilla.org/ja/docs/Web/JavaScript/Reference/Functions/Arrow_functions
たとえば下記アロー演算子の構文がわかりませんでした。
['bbbbb', '3'].map(({ length }) => length)
仮引数に{}
使とは、、、?みたいな感じです。
出力内容から見ると、length
プロパティを返していることを察することができましたが、いまいち腑に落ちませんでした。tscコマンドの出番です
typescriptをjavascritに変更するコマンドが
tsc
です。arrow.ts['bbbbb', '3'].map(({ length }) => length)上記のようにをファイルに書き出して、tscコマンドを実行します。
tsc arrow.ts
すると、arrow.jsというファイルが生成されます。中身を見るとシンタックスシュガーを使わずに実装されたjavascriptができています。arrow.js['bbbbb', '3'].map(function (_a) { var length = _a.length; return length; });
arrow.js
を読むと、['bbbbb', '3'].map(({ length }) => length)
がどのように実行されているのかがわかりました。
ローカル変数にlengthを格納しておいて、そのローカル変数をreturnしていたんですね。
すごいです。これは理解できます。他の「初見殺し構文」を読んでみる
難解.tsvar f = ([a, b] = [1, 2], {x: c} = {x: a + b}) => a + b + c;難解.jsvar f = function (_a, _b) { var _c = _a === void 0 ? [1, 2] : _a, a = _c[0], b = _c[1]; var c = (_b === void 0 ? { x: a + b } : _b).x; return a + b + c; };javascriptに変換したところで意味はわかりませんでしたが、頑張れば読めるような気がしていきました。