20200405のNode.jsに関する記事は10件です。

Node.js Express  で出てくる req, res, next

はじめに

Node.jsのExpressの雛形を生成するコマンド

express project-name

で出力されるコードの一部ですが

index.js
var 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について

reqapp.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();
}

reqapp.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

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

【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/

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

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

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

obnizとブザーで兄弟配管工ゲームのコイン音を出してみた

作ろうと思ったきっかけ

  • ProtoOutoStudio 3回目授業でobnizに触れる
  • ブザーが面白かった

 兄弟配管工ゲームでは、ブロックを叩くとコインが出てきますよね。その時の効果音を再現してみようと思いました。

”あの”効果音の音階を調べてみた

コインの音

が、楽譜が読めないぞ....。オクターブもよく分からん....。
よし、トライ&エラーか???(音痴です)

まずはベースとなる”ド”を鳴らす

”ド”の音が何Hzか調べました。今回はこちらのサイトを参考に周波数を設定していきます。

【鳴らしてみた様子】

音を鳴らすだけなら、obnizのJavascriptパーツライブラリから試すと爆速で確認できます。

 obniz IDを入力し、Test Runをクリックすると、指定した音が鳴ります。デフォルトは1000Hzになっているので、鳴らしたい音に合わせるといいかなと思います。
image.png

 今回は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つ上のオクターブになっているようです。

では、音階表を参考にしてみると....。

image.png

”シ”は987.76Hz、”ミ”は1318.51Hzでつくります。

 音を鳴らしてみるとこのようになりました。

 よさそうですね!あとは自動化してタイミング調整だな....

組み合わせる

 音を順番に出力するのは簡単ですが、タイミングがわからない!!!!!!!!
楽譜を見ると次のようになっています。

image.png

 こちらのサイトを参考にしてみると、200bpsのテンポであると読み解きました。200bpsとは、1分間に200拍の速度です。つまり、

  1分間(60秒) ÷ 200 = 0.3秒

となります。楽譜をみると、どうやら”シ”と”ミ”の間は何も記号がないので1拍だとして、”ミ”の後は2拍ありそうなので0.6秒として作りました。

結果....。


 
 思っていたより音が低そうなので、1オクターブあげてみましょう。

 こちらの方が近そうですね!でも何か足りない気がしてくる....
何度も同じ音を聞いていると分からなくなってしまうなぁ。でもこれで良しとしますか。

余談

 圧電スピーカーで効果音作るの楽しそう....沼に片足いれはじめてしまうぅ!

最終的なソースコード

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); // ミ
    }
  }
}
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

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.cv12.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-lambda

v12.xのディレクトリを元に、v8.10のディレクトリを作成します。

$ cp -r v12.x v8.10
$ cd v8.10

v12.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.js

Nodeのバージョンを確認しておきます。

$ ./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.yml
AWSTemplateFormatVersion: 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-nodejs8

Stackを作成。

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.runtimeprovidedを指定することでカスタムランタイムを使用できます。
Lambda Layerは、先程のCloudformation StackのOutputをインポートしてARNを指定します。

serverless.yml
service: 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.js
module.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

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

(小ネタ)Node.jsのWebアプリでclusterを使いながら定期的に子プロセスを再起動させる

Node.jsでサーバサイドWebアプリを開発中、なぜかメモリリークがあるライブラリに遭遇してしまったので、ワークアラウンドとして、定期的にプロセスを再起動させて、メモリリークの問題を緩和したいと思いました。

サーバサイド

http://0.0.0.0:10080 をリスンするプロセスが4つ立ち上がり、5秒未満で子プロセスを停止させ、その後に再起動します。(実用上は、もっと長い時間でプロセスを殺すべきです。)

src/index.js
const 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 でマルチプロセス化できるし、定期的にプロセスを再起動して、健全性を保つことができるはず
    • こういうゴミ掃除はマルチスレッドモデルだとできなさそう
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

DOM, Node, Elementについて

備忘録です。
間違いなどございましたらご指摘ください。

DOMとは

HTMLとXMLドキュメントへのAPI
(わかりやすくいうと、プログラムからHTMLやXMLを自由に操作するための仕組み、インターフェイス)

JavaScriptからHTMLに要素を追加したり、ボタンクリック時のイベントを登録したり、スタイルや属性を追加したり、要素のサイズや位置を追加したり、こういったものはすべてDOMのAPIを使うことで操作できる。

DOMツリー
DOMはHTMLドキュメントを『オブジェクトのツリー』として扱っている。これを『DOMツリー』という。

image.png

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

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

常に動く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に求めてない)

image.png
(2回同じこと聞くと返してくれる・・・)

2. 前回の回答を返してくる。(質問をちゃんと聞いて)

今回は「カラス?」と聞いているのに、「くま」について説明し始めた、、

3. 自動応答したいreplayMessageとWikipedia APIのpushMessageの順番がバラバラ(落ち着いてほしい…)

本当は「カラスの説明:」が上にきて、その下にwikipediaからの返答をのせたいのだけど、ずれる時がある。

image.png

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.js
async 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.js
const 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がおとぼけさんになってしまった。
(もっと人間的な振る舞いをしたら可愛げがあって多少のミスも許せるが、、まだ十分な人間味もない。)

時間的にもきついので、しばらくはこれ以上深掘らず、いったんここまで。

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

nowでアプリケーションエラーが出たとき対応したこと

nowでハマったメモ

状況

@n0bisukeこの記事を参考にやっていたつもりがこんなエラーと遭遇。

image.png

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 のあたり。

エラー出てたとき

エラー出てたとき.js
if(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}`);

image.png
成功!

結論

自分でアレンジしたところが悪かった。記事の通りにやっておけばよかった。
でもなんでこの書き方だとダメだったんだろう。。。
あと、nowを起動させるときにこのjsファイルも見ているんだなあ。

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

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していたんですね。
すごいです。これは理解できます。

他の「初見殺し構文」を読んでみる

難解.ts
var f = ([a, b] = [1, 2], {x: c} = {x: a + b}) => a + b + c;
難解.js
var 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に変換したところで意味はわかりませんでしたが、頑張れば読めるような気がしていきました。

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む